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

import { createHttpError } from '../../../utils/http.js'
import { logWarn } from '../../../utils/logger.js'
import { assertKuaishouIndustryConfig } from './config.js'

export type SignMethod = 'MD5' | 'HMAC_SHA256'

export function buildKuaishouIndustrySignSource(
  params: JsonObject = {},
  { signSecret } = assertKuaishouIndustryConfig(),
) {
  const entries = Object.entries(params)
    .filter(([key]) => key !== 'sign' && key !== 'signSecret')
    .sort(([left], [right]) => {
      if (left === right) {
        return 0
      }
      return left < right ? -1 : 1
    })

  const queryString = entries.map(([key, value]) => `${key}=${stringifySignValue(value)}`).join('&')

  return `${queryString}&signSecret=${signSecret}`
}

export function signKuaishouIndustryPayload(
  params: JsonObject = {},
  signMethod: SignMethod = 'MD5',
  config = assertKuaishouIndustryConfig(),
) {
  const source = buildKuaishouIndustrySignSource(params, config)

  if (signMethod === 'HMAC_SHA256') {
    return hmacSha256Sign(source, config.signSecret)
  }

  return crypto.createHash('md5').update(source, 'utf8').digest('hex').toLowerCase()
}

export function verifyKuaishouIndustrySignature(
  params: JsonObject = {},
  config = assertKuaishouIndustryConfig(),
) {
  const signMethod = normalizeSignMethod(params.signMethod)
  const source = buildKuaishouIndustrySignSource(params, config)
  const expected = signKuaishouIndustryPayload(params, signMethod, config)
  const actual = String(params.sign || '').trim()

  if (expected !== actual) {
    logWarn('[kuaishou-industry/crypto]', '签名验证失败', {
      signMethod,
      expected,
      actual,
      source: source.replace(config.signSecret, '***'),
    })
    return false
  }

  return true
}

export function assertKuaishouIndustrySignature(
  params: JsonObject = {},
  config = assertKuaishouIndustryConfig(),
) {
  if (!verifyKuaishouIndustrySignature(params, config)) {
    throw createHttpError('签名验证失败', {
      statusCode: 400,
      errorCode: 'kuaishou_industry_invalid_signature',
    })
  }
}

function hmacSha256Sign(source: string, secret: string) {
  return crypto.createHmac('sha256', secret).update(source, 'utf8').digest('base64')
}

function normalizeSignMethod(value: unknown): SignMethod {
  const normalized = String(value || '')
    .trim()
    .toUpperCase()
  if (normalized === 'HMAC_SHA256' || normalized === 'HMAC-SHA256') {
    return 'HMAC_SHA256'
  }
  return 'MD5'
}

function stringifySignValue(value: unknown) {
  if (typeof value === 'number' && Number.isFinite(value)) {
    return String(value)
  }

  if (typeof value === 'boolean') {
    return value ? 'true' : 'false'
  }

  if (value == null) {
    return ''
  }

  if (typeof value === 'object') {
    return JSON.stringify(normalizeSignJsonValue(value))
  }

  return String(value)
}

function normalizeSignJsonValue(value: unknown): unknown {
  if (Array.isArray(value)) {
    return value.map((item) => normalizeSignJsonValue(item))
  }

  if (value && typeof value === 'object') {
    const record = value as Record<string, unknown>
    const normalized: Record<string, unknown> = {}
    for (const key of Object.keys(record).sort()) {
      const current = normalizeSignJsonValue(record[key])
      if (current !== undefined) {
        normalized[key] = current
      }
    }
    return normalized
  }

  return value
}
