import type { JsonObject } from '../../../types/json.js'

import { createHttpError } from '../../../utils/http.js'
import { getAffiliateDashConfig } from './config.js'
import { verifyCallbackSign } from './sign.js'

type CallbackHeaders = Record<string, string | string[] | undefined>

export type AffiliateDashCallbackPayload = {
  eventId: string
  event: string
  occurredAt?: string | undefined
  data: JsonObject
}

/**
 * affiliate_dash 回调验签入口。
 * 校验顺序：X-Timestamp 在容差内 → 重算签名（timingSafeEqual）→ 返回事件。
 * X-Event-ID 幂等去重属于业务层（阶段 5），本函数只负责验签与解析。
 */
export function verifyAffiliateDashCallback(
  input: {
    headers: CallbackHeaders
    rawBody?: string | undefined
  },
  configOverride: Partial<{
    callbackSecret: string
    timestampToleranceSeconds: number
  }> = {},
) {
  const config = getAffiliateDashConfig(configOverride)
  const rawBody = String(input.rawBody || '')
  const eventId = normalizeHeader(input.headers['x-event-id'])
  const timestamp = normalizeHeader(input.headers['x-timestamp'])
  const sign = normalizeHeader(input.headers['x-sign'])

  if (!rawBody) {
    throw createHttpError('affiliate-dash 回调原始请求体缺失', {
      statusCode: 400,
      errorCode: 'affiliate_dash_callback_raw_body_missing',
    })
  }

  if (!eventId || !timestamp || !sign) {
    throw createHttpError('affiliate-dash 回调签名参数缺失', {
      statusCode: 401,
      errorCode: 'affiliate_dash_callback_sign_missing',
    })
  }

  if (!/^\d{10,}$/.test(timestamp)) {
    throw createHttpError('affiliate-dash 回调时间戳格式无效', {
      statusCode: 401,
      errorCode: 'affiliate_dash_callback_timestamp_invalid',
    })
  }

  const timestampSeconds = Number(timestamp)
  const nowSeconds = Math.floor(Date.now() / 1000)
  if (Math.abs(nowSeconds - timestampSeconds) > config.timestampToleranceSeconds) {
    throw createHttpError('affiliate-dash 回调时间戳超出容差', {
      statusCode: 401,
      errorCode: 'affiliate_dash_callback_timestamp_skew',
      context: {
        timestamp: timestampSeconds,
        toleranceSeconds: config.timestampToleranceSeconds,
      },
    })
  }

  if (!config.callbackSecret) {
    throw createHttpError('affiliate-dash 回调 Secret 未配置', {
      statusCode: 500,
      errorCode: 'affiliate_dash_missing_callback_secret',
    })
  }

  if (
    !verifyCallbackSign({
      callbackSecret: config.callbackSecret,
      rawBody,
      timestamp,
      sign,
    })
  ) {
    throw createHttpError('affiliate-dash 回调验签失败', {
      statusCode: 401,
      errorCode: 'affiliate_dash_callback_sign_invalid',
    })
  }

  let source: JsonObject = {}
  try {
    const parsed = JSON.parse(rawBody)
    if (isPlainObject(parsed)) {
      source = parsed
    }
  } catch {
    // 非 JSON body：视为无业务数据，验签已通过
  }
  const data = isPlainObject(source.data) ? (source.data as JsonObject) : {}

  return {
    eventId,
    event: String(source.event || '').trim(),
    occurredAt: String(source.occurred_at || '').trim() || undefined,
    data,
  } satisfies AffiliateDashCallbackPayload
}

function normalizeHeader(value: string | string[] | undefined) {
  return Array.isArray(value) ? String(value[0] || '').trim() : String(value || '').trim()
}

function isPlainObject(value: unknown): value is JsonObject {
  return Object.prototype.toString.call(value) === '[object Object]'
}
