import crypto from 'crypto'

/**
 * H5 短时 cookie 签发/校验。
 * forge_h5 cookie 格式：base64(pageId|appId|externalUserId|exp|name) + '.' + HMAC签名
 *
 * 安全要点：
 * - httpOnly + SameSite=Lax + Secure + Max-Age=7200（2h）
 * - 不携带 owner_user_id（服务端每次反查，避免 owner 变更时 cookie 失效）
 * - HMAC 签名，外部无法伪造；篡改 payload 任一字节 → 校验失败
 */

const COOKIE_NAME = 'forge_h5'
const COOKIE_TTL_SECONDS = 2 * 60 * 60 // 2h

/** cookie 的明文载荷（签名前） */
export interface H5CookiePayload {
  pageId: string
  appId: string
  /** 外部用户 ID（external_users.id，UUID） */
  externalUserId: string
  /** 过期时间戳（秒） */
  exp: number
  /** 外部用户展示名（JWT name，仅展示用） */
  name?: string
}

/** 取 cookie 名称 */
export function h5CookieName(): string {
  return COOKIE_NAME
}

/** 取 cookie 有效期（秒） */
export function h5CookieTtl(): number {
  return COOKIE_TTL_SECONDS
}

/**
 * 签发 forge_h5 cookie 值。
 * 需要传入服务端密钥（FORGE_H5_COOKIE_SECRET 环境变量，回退到 FORGE_SECRET）。
 */
export function signH5Cookie(payload: H5CookiePayload, secret: string): string {
  const exp = payload.exp || Math.floor(Date.now() / 1000) + COOKIE_TTL_SECONDS
  const name = payload.name ?? ''
  const plaintext = `${payload.pageId}|${payload.appId}|${payload.externalUserId}|${exp}|${name}`
  const encoded = Buffer.from(plaintext).toString('base64url')
  const sig = hmacSha256(encoded, secret)
  return `${encoded}.${sig}`
}

/**
 * 校验 forge_h5 cookie 值。
 * @returns 解析出的 payload，或 null（校验失败/过期）
 */
export function verifyH5Cookie(value: string | undefined | null, secret: string): H5CookiePayload | null {
  if (!value) return null

  const dotIdx = value.lastIndexOf('.')
  if (dotIdx === -1) return null

  const encoded = value.slice(0, dotIdx)
  const providedSig = value.slice(dotIdx + 1)

  const expectedSig = hmacSha256(encoded, secret)
  if (!timingSafeEqualString(expectedSig, providedSig)) return null

  let plaintext: string
  try {
    plaintext = Buffer.from(encoded, 'base64url').toString('utf-8')
  } catch {
    return null
  }

  const parts = plaintext.split('|')
  if (parts.length < 4) return null

  const [pageId, appId, externalUserId, expStr] = parts
  const exp = parseInt(expStr, 10)
  if (isNaN(exp)) return null

  if (exp <= Math.floor(Date.now() / 1000)) return null

  if (!pageId || !appId || !externalUserId) return null

  const name = parts.length > 4 ? parts.slice(4).join('|') : ''
  return { pageId, appId, externalUserId, exp, name }
}

/** 取 cookie 配置项（httpOnly + SameSite=Lax + Secure + Max-Age） */
export function h5CookieSerializeOptions() {
  return {
    httpOnly: true,
    sameSite: 'lax' as const,
    secure: process.env.NODE_ENV === 'production',
    maxAge: COOKIE_TTL_SECONDS,
    path: '/',
  }
}

/** forge_h5_theme cookie 名称（与身份 cookie 并行，存品牌主题覆盖） */
const THEME_COOKIE_NAME = 'forge_h5_theme'

/** 取主题 cookie 名称 */
export function h5ThemeCookieName(): string {
  return THEME_COOKIE_NAME
}

/**
 * 主题 cookie 配置项。
 */
export function h5ThemeCookieSerializeOptions() {
  return {
    httpOnly: false,
    sameSite: 'lax' as const,
    secure: process.env.NODE_ENV === 'production',
    maxAge: 30 * 24 * 60 * 60, // 30 days
    path: '/',
  }
}

/**
 * 从环境变量取 cookie 签名密钥。
 * 优先 FORGE_H5_COOKIE_SECRET，回退 FORGE_SECRET，最后回退硬编码（仅开发环境）。
 */
export function getH5CookieSecret(): string {
  const secret = process.env.FORGE_H5_COOKIE_SECRET || process.env.FORGE_SECRET
  if (secret) return secret
  if (process.env.NODE_ENV !== 'production') return 'dev-only-h5-cookie-secret-not-for-production'
  throw new Error('FORGE_H5_COOKIE_SECRET is required in production')
}

function hmacSha256(data: string, secret: string): string {
  return crypto.createHmac('sha256', secret).update(data).digest('base64url')
}

function timingSafeEqualString(a: string, b: string): boolean {
  if (a.length !== b.length) return false
  return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b))
}
