import './env.mjs'
import { createDecipheriv, createSign, createVerify, randomBytes } from 'node:crypto'
import { existsSync, readFileSync } from 'node:fs'

const defaultApiBaseUrl = 'https://api.mch.weixin.qq.com'

function envFlag(name, defaultValue = false) {
  const value = process.env[name]
  if (value === undefined || value === '') return defaultValue
  return ['1', 'true', 'yes', 'on'].includes(String(value).toLowerCase())
}

function readPem({ base64, path, label }) {
  if (base64) return Buffer.from(String(base64).trim(), 'base64').toString('utf8')
  if (!path) return ''
  if (!existsSync(path)) {
    const error = new Error(`${label}文件不存在`)
    error.statusCode = 503
    error.code = 'wechat_pay_key_missing'
    throw error
  }
  return readFileSync(path, 'utf8')
}

export function wechatPayEnabled() {
  return envFlag('WECHAT_PAY_ENABLED', process.env.NODE_ENV === 'production')
}

export function wechatPayConfig() {
  const appId = process.env.WECHAT_PAY_APP_ID || process.env.MINIAPP_WECHAT_APP_ID || process.env.WECHAT_APP_ID || ''
  const apiPublicOrigin = (process.env.API_PUBLIC_ORIGIN || 'http://localhost:4000').replace(/\/+$/, '')
  return {
    enabled: wechatPayEnabled(),
    apiBaseUrl: (process.env.WECHAT_PAY_API_BASE_URL || defaultApiBaseUrl).replace(/\/+$/, ''),
    mchId: process.env.WECHAT_PAY_MCH_ID || '',
    appId,
    apiV3Key: process.env.WECHAT_PAY_API_V3_KEY || '',
    mchSerialNo: process.env.WECHAT_PAY_MCH_SERIAL_NO || '',
    publicKeyId: process.env.WECHAT_PAY_PUBLIC_KEY_ID || '',
    privateKeyPath: process.env.WECHAT_PAY_PRIVATE_KEY_PATH || '',
    privateKeyBase64: process.env.WECHAT_PAY_PRIVATE_KEY_BASE64 || '',
    publicKeyPath: process.env.WECHAT_PAY_PUBLIC_KEY_PATH || '',
    publicKeyBase64: process.env.WECHAT_PAY_PUBLIC_KEY_BASE64 || '',
    notifyUrl: process.env.WECHAT_PAY_NOTIFY_URL || `${apiPublicOrigin}/api/payments/wechat/notify`,
    timeoutMs: Math.max(1000, Number(process.env.WECHAT_PAY_TIMEOUT_MS || 10000)),
  }
}

function providerError(message, code = 'wechat_pay_failed', statusCode = 502, details = {}) {
  const error = new Error(message)
  error.statusCode = statusCode
  error.code = code
  error.details = details
  return error
}

function assertWechatPayConfigured(config = wechatPayConfig()) {
  if (!config.enabled) {
    throw providerError('微信支付未启用', 'wechat_pay_disabled', 503)
  }
  const missing = []
  if (!config.mchId) missing.push('WECHAT_PAY_MCH_ID')
  if (!config.appId) missing.push('WECHAT_PAY_APP_ID')
  if (!config.apiV3Key) missing.push('WECHAT_PAY_API_V3_KEY')
  if (!config.mchSerialNo) missing.push('WECHAT_PAY_MCH_SERIAL_NO')
  if (!config.publicKeyId) missing.push('WECHAT_PAY_PUBLIC_KEY_ID')
  if (!config.notifyUrl) missing.push('WECHAT_PAY_NOTIFY_URL')
  if (!config.privateKeyBase64 && !config.privateKeyPath) missing.push('WECHAT_PAY_PRIVATE_KEY_PATH')
  if (!config.publicKeyBase64 && !config.publicKeyPath) missing.push('WECHAT_PAY_PUBLIC_KEY_PATH')
  if (missing.length > 0) {
    throw providerError(`微信支付配置不完整：${missing.join(', ')}`, 'wechat_pay_not_configured', 503)
  }
  if (Buffer.byteLength(config.apiV3Key) !== 32) {
    throw providerError('微信支付 APIv3 密钥必须是 32 字节', 'wechat_pay_bad_api_v3_key', 503)
  }
}

function privateKey(config = wechatPayConfig()) {
  assertWechatPayConfigured(config)
  return readPem({
    base64: config.privateKeyBase64,
    path: config.privateKeyPath,
    label: '微信支付商户私钥',
  })
}

function publicKey(config = wechatPayConfig()) {
  assertWechatPayConfigured(config)
  return readPem({
    base64: config.publicKeyBase64,
    path: config.publicKeyPath,
    label: '微信支付公钥',
  })
}

function nonce(size = 16) {
  return randomBytes(size).toString('hex')
}

function signWithPrivateKey(message, config = wechatPayConfig()) {
  const signer = createSign('RSA-SHA256')
  signer.update(message)
  signer.end()
  return signer.sign(privateKey(config), 'base64')
}

function verifyWithPublicKey(message, signature, config = wechatPayConfig()) {
  const verifier = createVerify('RSA-SHA256')
  verifier.update(message)
  verifier.end()
  return verifier.verify(publicKey(config), signature, 'base64')
}

function headerValue(headers, key) {
  if (!headers) return ''
  if (typeof headers.get === 'function') return headers.get(key) || headers.get(key.toLowerCase()) || ''
  return headers[key] || headers[key.toLowerCase()] || ''
}

function assertWechatSignature(headers, body, config = wechatPayConfig()) {
  const timestamp = headerValue(headers, 'wechatpay-timestamp')
  const nonceStr = headerValue(headers, 'wechatpay-nonce')
  const signature = headerValue(headers, 'wechatpay-signature')
  const serial = headerValue(headers, 'wechatpay-serial')
  if (!timestamp || !nonceStr || !signature || !serial) {
    throw providerError('微信支付签名头缺失', 'wechat_pay_signature_missing', 401)
  }
  if (config.publicKeyId && serial !== config.publicKeyId) {
    throw providerError('微信支付公钥 ID 不匹配', 'wechat_pay_public_key_mismatch', 401, { serial })
  }
  const message = `${timestamp}\n${nonceStr}\n${body}\n`
  if (!verifyWithPublicKey(message, signature, config)) {
    throw providerError('微信支付签名验证失败', 'wechat_pay_signature_invalid', 401)
  }
}

export function formatWechatPayAuthorizationHeader({ mchId, nonceStr, signature, timestamp, serialNo }) {
  return `WECHATPAY2-SHA256-RSA2048 ${[
    `mchid="${mchId}"`,
    `nonce_str="${nonceStr}"`,
    `signature="${signature}"`,
    `timestamp="${timestamp}"`,
    `serial_no="${serialNo}"`,
  ].join(',')}`
}

function authorization(method, urlPath, body, config = wechatPayConfig()) {
  const timestamp = Math.floor(Date.now() / 1000).toString()
  const nonceStr = nonce()
  const message = `${method}\n${urlPath}\n${timestamp}\n${nonceStr}\n${body}\n`
  const signature = signWithPrivateKey(message, config)
  return formatWechatPayAuthorizationHeader({
    mchId: config.mchId,
    nonceStr,
    signature,
    timestamp,
    serialNo: config.mchSerialNo,
  })
}

async function requestWechat(method, path, payload = null, config = wechatPayConfig()) {
  assertWechatPayConfigured(config)
  const body = payload ? JSON.stringify(payload) : ''
  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort(), config.timeoutMs)
  let response
  try {
    response = await fetch(`${config.apiBaseUrl}${path}`, {
      method,
      signal: controller.signal,
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
        Authorization: authorization(method, path, body, config),
      },
      body: body || undefined,
    })
  } catch (error) {
    const timedOut = error.name === 'AbortError'
    throw providerError(timedOut ? '微信支付网关请求超时' : '微信支付网关连接失败', timedOut ? 'wechat_pay_timeout' : 'wechat_pay_unreachable', 502, {
      cause: error.message,
    })
  } finally {
    clearTimeout(timer)
  }

  const text = await response.text()
  if (response.headers.get('wechatpay-signature')) {
    assertWechatSignature(response.headers, text, config)
  }
  let responsePayload = {}
  try {
    responsePayload = text ? JSON.parse(text) : {}
  } catch {
    throw providerError('微信支付网关返回异常', 'wechat_pay_bad_response', 502, { status: response.status })
  }
  if (!response.ok) {
    throw providerError(responsePayload.message || responsePayload.code || '微信支付请求失败', 'wechat_pay_provider_error', 502, {
      status: response.status,
      provider: responsePayload,
    })
  }
  return responsePayload
}

function formatTimeExpire(value) {
  const date = value ? new Date(value) : new Date(Date.now() + 30 * 60 * 1000)
  return date.toISOString()
}

function descriptionFor(orderDescription) {
  const value = String(orderDescription || '大连市大数据研究院订单').replace(/\s+/g, ' ').trim()
  return value.length > 80 ? `${value.slice(0, 77)}...` : value
}

export function createJsapiPaymentParams(prepayId, config = wechatPayConfig()) {
  assertWechatPayConfigured(config)
  const timeStamp = Math.floor(Date.now() / 1000).toString()
  const nonceStr = nonce()
  const packageValue = `prepay_id=${prepayId}`
  const message = `${config.appId}\n${timeStamp}\n${nonceStr}\n${packageValue}\n`
  return {
    timeStamp,
    nonceStr,
    package: packageValue,
    signType: 'RSA',
    paySign: signWithPrivateKey(message, config),
  }
}

export async function createWechatJsapiPayment({ paymentNo, amount, description, openid, expiresAt }) {
  const config = wechatPayConfig()
  assertWechatPayConfigured(config)
  if (!openid) {
    throw providerError('当前账号缺少小程序 openid，无法发起小程序支付', 'wechat_pay_openid_missing', 409)
  }
  const requestPayload = {
    appid: config.appId,
    mchid: config.mchId,
    description: descriptionFor(description),
    out_trade_no: paymentNo,
    time_expire: formatTimeExpire(expiresAt),
    notify_url: config.notifyUrl,
    amount: {
      total: amount,
      currency: 'CNY',
    },
    payer: {
      openid,
    },
  }
  const responsePayload = await requestWechat('POST', '/v3/pay/transactions/jsapi', requestPayload, config)
  if (!responsePayload.prepay_id) {
    throw providerError('微信支付未返回 prepay_id', 'wechat_pay_prepay_missing', 502, { responsePayload })
  }
  return {
    requestPayload,
    responsePayload,
    prepayId: responsePayload.prepay_id,
    jsapiParams: createJsapiPaymentParams(responsePayload.prepay_id, config),
  }
}

export async function createWechatNativePayment({ paymentNo, amount, description, expiresAt }) {
  const config = wechatPayConfig()
  assertWechatPayConfigured(config)
  const requestPayload = {
    appid: config.appId,
    mchid: config.mchId,
    description: descriptionFor(description),
    out_trade_no: paymentNo,
    time_expire: formatTimeExpire(expiresAt),
    notify_url: config.notifyUrl,
    amount: {
      total: amount,
      currency: 'CNY',
    },
  }
  const responsePayload = await requestWechat('POST', '/v3/pay/transactions/native', requestPayload, config)
  if (!responsePayload.code_url) {
    throw providerError('微信支付未返回二维码链接', 'wechat_pay_code_url_missing', 502, { responsePayload })
  }
  return {
    requestPayload,
    responsePayload,
    codeUrl: responsePayload.code_url,
  }
}

export async function closeWechatPayment(paymentNo) {
  const config = wechatPayConfig()
  assertWechatPayConfigured(config)
  const outTradeNo = encodeURIComponent(String(paymentNo || '').trim())
  if (!outTradeNo) {
    throw providerError('微信支付商户支付单号不能为空', 'wechat_pay_payment_no_required', 400)
  }
  await requestWechat('POST', `/v3/pay/transactions/out-trade-no/${outTradeNo}/close`, {
    mchid: config.mchId,
  }, config)
  return { ok: true }
}

export function decryptWechatPayResource(resource) {
  const config = wechatPayConfig()
  assertWechatPayConfigured(config)
  if (!resource?.ciphertext || !resource?.nonce) {
    throw providerError('微信支付回调资源缺失', 'wechat_pay_resource_missing', 400)
  }
  const ciphertext = Buffer.from(resource.ciphertext, 'base64')
  const authTag = ciphertext.subarray(ciphertext.length - 16)
  const encrypted = ciphertext.subarray(0, ciphertext.length - 16)
  const decipher = createDecipheriv('aes-256-gcm', Buffer.from(config.apiV3Key), Buffer.from(resource.nonce))
  decipher.setAuthTag(authTag)
  if (resource.associated_data) decipher.setAAD(Buffer.from(resource.associated_data))
  const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf8')
  return JSON.parse(decrypted)
}

export function verifyWechatPayNotification(headers, rawBody) {
  const config = wechatPayConfig()
  assertWechatSignature(headers, rawBody, config)
  let payload
  try {
    payload = rawBody ? JSON.parse(rawBody) : {}
  } catch {
    throw providerError('微信支付回调 JSON 无效', 'wechat_pay_notify_bad_json', 400)
  }
  return {
    payload,
    resource: decryptWechatPayResource(payload.resource),
  }
}
