import crypto from 'node:crypto'

import { runtimeConfig } from '../../config/runtime.js'
import { createHttpError } from '../../utils/http.js'

export const OPEN_91_PROVIDER = '91kaquan'
export const OPEN_91_PLATFORM = 'kuaishou'
export const OPEN_91_SUCCESS_MESSAGE = '接口调用成功'
export const OPEN_91_DEFAULT_FAIL_CODE = 1204
export const OPEN_91_COST_EXCEED_FAIL_CODE = 1220

type JsonObject = Record<string, any>

export function getOpen91Config() {
  const config = (runtimeConfig.platforms?.ninetyone || {}) as JsonObject

  return {
    userId: String(config.userId || '').trim(),
    secret: String(config.secret || '').trim(),
    version: String(config.version || '1.0').trim() || '1.0',
    shopId: String(config.shopId || OPEN_91_PROVIDER).trim() || OPEN_91_PROVIDER,
    shopName: String(config.shopName || '91卡券').trim() || '91卡券',
    timestampToleranceSeconds: Number(config.timestampToleranceSeconds || 600) || 600,
    cardsEncoding: String(config.cardsEncoding || 'aes-256-ecb-base64').trim() || 'aes-256-ecb-base64',
  }
}

export function assertOpen91Config() {
  const config = getOpen91Config()

  if (!config.secret) {
    throw createHttpError('91卡券 secret 未配置', {
      statusCode: 500,
      errorCode: 'open91_missing_secret',
    })
  }

  if (config.secret.length !== 32) {
    throw createHttpError('91卡券 secret 长度必须为 32 个字符', {
      statusCode: 500,
      errorCode: 'open91_invalid_secret_length',
    })
  }

  return config
}

export function normalizeOpen91String(value) {
  return String(value || '').trim()
}

export function normalizeOpen91CreatePayload(payload: JsonObject = {}) {
  return {
    orderNo: normalizeOpen91String(payload.orderNo),
    productNo: normalizeOpen91String(payload.productNo),
    buyNum: normalizeOpen91BuyNum(payload.buyNum),
    maxAmount: normalizeOpen91OptionalAmount(payload.maxAmount),
    callbackUrl: normalizeOpen91String(payload.callbackUrl),
    timestamp: normalizeOpen91Timestamp(payload.timestamp),
    version: normalizeOpen91String(payload.version),
    sign: normalizeOpen91String(payload.sign).toUpperCase(),
  }
}

export function normalizeOpen91QueryPayload(payload: JsonObject = {}) {
  return {
    orderNo: normalizeOpen91String(payload.orderNo),
    timestamp: normalizeOpen91Timestamp(payload.timestamp),
    version: normalizeOpen91String(payload.version),
    sign: normalizeOpen91String(payload.sign).toUpperCase(),
  }
}

export function assertOpen91CreatePayload(payload: JsonObject, config = assertOpen91Config()) {
  if (!payload.orderNo) {
    throw createHttpError('缺少 orderNo', {
      statusCode: 400,
      errorCode: 'open91_missing_order_no',
    })
  }

  if (!payload.productNo) {
    throw createHttpError('缺少 productNo', {
      statusCode: 400,
      errorCode: 'open91_missing_product_no',
    })
  }

  if (!Number.isInteger(payload.buyNum) || payload.buyNum <= 0) {
    throw createHttpError('buyNum 必须是大于 0 的整数', {
      statusCode: 400,
      errorCode: 'open91_invalid_buy_num',
    })
  }

  assertOpen91CommonPayload(payload, config)
}

export function assertOpen91QueryPayload(payload: JsonObject, config = assertOpen91Config()) {
  if (!payload.orderNo) {
    throw createHttpError('缺少 orderNo', {
      statusCode: 400,
      errorCode: 'open91_missing_order_no',
    })
  }

  assertOpen91CommonPayload(payload, config)
}

function assertOpen91CommonPayload(payload: JsonObject, config: JsonObject) {
  if (!payload.timestamp) {
    throw createHttpError('缺少 timestamp', {
      statusCode: 400,
      errorCode: 'open91_missing_timestamp',
    })
  }

  if (!payload.version) {
    throw createHttpError('缺少 version', {
      statusCode: 400,
      errorCode: 'open91_missing_version',
    })
  }

  if (payload.version !== config.version) {
    throw createHttpError(`version 不匹配，当前仅支持 ${config.version}`, {
      statusCode: 400,
      errorCode: 'open91_invalid_version',
    })
  }

  if (!payload.sign) {
    throw createHttpError('缺少 sign', {
      statusCode: 400,
      errorCode: 'open91_missing_sign',
    })
  }
}

export function assertOpen91Timestamp(timestamp: unknown, toleranceSeconds = assertOpen91Config().timestampToleranceSeconds) {
  const nowSeconds = Math.floor(Date.now() / 1000)
  if (Math.abs(nowSeconds - Number(timestamp)) > Math.max(1, Number(toleranceSeconds) || 600)) {
    throw createHttpError('timestamp 已过期', {
      statusCode: 400,
      errorCode: 'open91_expired_timestamp',
    })
  }
}

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

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

  return `${secret}${queryString}${secret}`
}

export function signOpen91Payload(params: JsonObject = {}, config = assertOpen91Config()) {
  return crypto
    .createHash('md5')
    .update(buildOpen91SignSource(params, config), 'utf8')
    .digest('hex')
    .toUpperCase()
}

export function verifyOpen91Signature(params: JsonObject = {}, config = assertOpen91Config()) {
  const expected = signOpen91Payload(params, config)
  const actual = normalizeOpen91String(params.sign).toUpperCase()
  return expected === actual
}

export function assertOpen91Signature(params: JsonObject = {}, config = assertOpen91Config()) {
  if (!verifyOpen91Signature(params, config)) {
    throw createHttpError('验签失败', {
      statusCode: 400,
      errorCode: 'open91_invalid_signature',
    })
  }
}

export function encryptOpen91Cards(cards = [], secret = assertOpen91Config().secret) {
  const normalizedSecret = normalizeOpen91String(secret)
  if (normalizedSecret.length !== 32) {
    throw createHttpError('91卡券 cards 加密密钥长度必须为 32 个字符', {
      statusCode: 500,
      errorCode: 'open91_invalid_cards_secret_length',
    })
  }

  const plainText = JSON.stringify(Array.isArray(cards) ? cards : [])
  const cipher = crypto.createCipheriv('aes-256-ecb', Buffer.from(normalizedSecret, 'utf8'), null)
  cipher.setAutoPadding(true)

  return `${cipher.update(plainText, 'utf8', 'base64')}${cipher.final('base64')}`
}

export function buildOpen91Cards(cards: unknown[] = [], config = assertOpen91Config()) {
  const encoding = normalizeOpen91String(config.cardsEncoding).toLowerCase()
  if (encoding && encoding !== 'aes-256-ecb-base64') {
    throw createHttpError(`暂不支持的 cardsEncoding: ${config.cardsEncoding}`, {
      statusCode: 500,
      errorCode: 'open91_unsupported_cards_encoding',
    })
  }

  return encryptOpen91Cards(cards, config.secret)
}

export function buildOpen91SuccessResponse(data: unknown, message = OPEN_91_SUCCESS_MESSAGE) {
  return {
    code: 200,
    message,
    data,
  }
}

export function buildOpen91ErrorResponse(message: unknown, code = 500) {
  return {
    code,
    message: normalizeOpen91String(message) || '系统错误',
    data: null,
  }
}

export function buildOpen91OutTradeNo(order: JsonObject) {
  const orderId = Number(order?.id || 0)
  return orderId > 0 ? `OS${String(orderId).padStart(10, '0')}` : ''
}

export function buildOpen91OrderCost(value = 0) {
  const amount = Number(value)
  return Number.isFinite(amount) ? amount.toFixed(4) : '0.0000'
}

export function buildOpen91CardItem({ claimUrl = '', expireTime = '' }: { claimUrl?: unknown, expireTime?: unknown } = {}) {
  const normalizedClaimUrl = normalizeOpen91String(claimUrl)
  return {
    cardNo: normalizedClaimUrl,
    cardPwd: '',
    expireTime: formatOpen91ExpireTime(expireTime),
    jumpLink: normalizedClaimUrl,
  }
}

export function formatOpen91ExpireTime(value: unknown = '') {
  const normalized = normalizeOpen91String(value)
  if (!normalized) {
    return ''
  }

  if (/^\d{10}$/.test(normalized) || /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(normalized)) {
    return normalized
  }

  const date = value instanceof Date ? value : new Date(normalized)
  if (Number.isNaN(date.getTime())) {
    return normalized
  }

  const parts = new Intl.DateTimeFormat('zh-CN', {
    timeZone: 'Asia/Shanghai',
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hourCycle: 'h23',
  }).formatToParts(date)

  const byType = Object.fromEntries(parts.map((part) => [part.type, part.value]))
  return `${byType.year}-${byType.month}-${byType.day} ${byType.hour}:${byType.minute}:${byType.second}`
}

export function resolveOpen91QueryState(
  { tasks = [], readyTaskIds = [] }: { tasks?: JsonObject[], readyTaskIds?: unknown[] } = {},
) {
  const normalizedTasks = Array.isArray(tasks) ? tasks : []
  const readyTaskIdSet = new Set((Array.isArray(readyTaskIds) ? readyTaskIds : []).map((item) => Number(item)))

  if (normalizedTasks.length === 0) {
    return {
      orderStatus: 30,
      failCode: OPEN_91_DEFAULT_FAIL_CODE,
      failReason: '订单未生成履约任务',
    }
  }

  const failedTask = normalizedTasks.find((task) => {
    const status = normalizeOpen91String(task?.task_status)
    return ['failed', 'manual_review', 'closed'].includes(status) && !readyTaskIdSet.has(Number(task?.id || 0))
  })

  if (failedTask) {
    return {
      orderStatus: 30,
      failCode: OPEN_91_DEFAULT_FAIL_CODE,
      failReason: normalizeOpen91String(failedTask.last_error) || '订单无法履约',
    }
  }

  if (readyTaskIdSet.size < normalizedTasks.length) {
    return {
      orderStatus: 10,
      failCode: 0,
      failReason: '',
    }
  }

  return {
    orderStatus: 20,
    failCode: 0,
    failReason: '',
  }
}

export function stringifyOpen91SignValue(value) {
  if (typeof value === 'number' && Number.isFinite(value)) {
    return String(value)
  }

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

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

  return String(value)
}

function normalizeOpen91BuyNum(value) {
  const parsed = Number(value)
  return Number.isInteger(parsed) ? parsed : 0
}

function normalizeOpen91OptionalAmount(value) {
  const normalized = normalizeOpen91String(value)
  if (!normalized) {
    return ''
  }

  if (!/^\d+(?:\.\d+)?$/.test(normalized)) {
    throw createHttpError('maxAmount 格式无效', {
      statusCode: 400,
      errorCode: 'open91_invalid_max_amount',
    })
  }

  return normalized
}

function normalizeOpen91Timestamp(value) {
  const normalized = normalizeOpen91String(value)
  if (!/^\d{10}$/.test(normalized)) {
    return 0
  }

  return Number(normalized)
}
