import 'server-only'
import Cap, {ChallengeData} from '@cap.js/server'
import {cookies} from 'next/headers'
import {createHmac, randomBytes, timingSafeEqual} from 'crypto'
import {CLIENT_SECRET} from '@/lib/api'

type Sign = {
  data: string
  timestamp: number
  nonce: string
  sign: string
}

function sign(data: string, timestamp: number) {
  if (!CLIENT_SECRET) throw new Error('无法完成签名')
  const hash = createHmac('sha256', CLIENT_SECRET)
  const nonce = String(randomBytes(16))
  hash.update(data).update(nonce).update(String(timestamp))
  return {
    data, timestamp, nonce,
    sign: hash.digest('hex'),
  }
}

function verify({data, timestamp, nonce, sign}: Sign) {
  if (!CLIENT_SECRET) throw new Error('无法完成验证')
  const hash = createHmac('sha256', CLIENT_SECRET)
  hash.update(data).update(nonce).update(String(timestamp))
  const excepted = Buffer.from(sign)
  const received = Buffer.from(hash.digest('hex'))
  return timingSafeEqual(excepted, received)
}

export async function getCap() {
  return new Cap({
    disableAutoCleanup: true,
    noFSState: true,
    storage: {
      challenges: {
        store: async (token: string, data: ChallengeData) => {
          const cookie = await cookies()
          const rs = sign(JSON.stringify({token, data}), data.expires)
          cookie.set(`challenge:${token}`, JSON.stringify(rs), {
            secure: process.env.NODE_ENV === 'production',
            expires: data.expires,
            sameSite: true,
            httpOnly: true,
          })
        },
        read: async (token: string) => {
          const cookie = await cookies()
          const json = cookie.get(`challenge:${token}`)?.value
          if (!json) return null
          const sign = JSON.parse(json) as Sign
          if (!verify(sign)) return null
          return JSON.parse(sign.data)['data']
        },
        delete: async (token: string) => {
          const cookie = await cookies()
          cookie.delete(`challenge:${token}`)
        },
        deleteExpired: async () => {},
      },
      tokens: {
        store: async (token: string, expires: number) => {
          const cookie = await cookies()
          const rs = sign(JSON.stringify({token, expires}), expires)
          cookie.set(token, JSON.stringify(rs), {
            secure: process.env.NODE_ENV === 'production',
            expires: expires,
            sameSite: true,
            httpOnly: true,
          })
        },
        get: async (token: string) => {
          const cookie = await cookies()
          const json = cookie.get(token)?.value
          if (!json) return null
          const sign = JSON.parse(json) as Sign
          if (!verify(sign)) return null
          return JSON.parse(sign.data)['expires']
        },
        delete: async (token: string) => {
          const cookie = await cookies()
          cookie.delete(`token:${token}`)
        },
        deleteExpired: async () => {},
      },
    },
  })
}
