const crypto = require('crypto')
const https = require('https')
const fs = require('fs')
const config = require('../config')

function isConfigured() {
  const c = config.wxpay
  return !!(c.mchId && c.apiV3Key && c.mchSerialNo && (c.privateKey || c.privateKeyPath))
}

let _cachedPrivateKey = null
function getPrivateKey() {
  if (_cachedPrivateKey) return _cachedPrivateKey
  let key = ''
  if (config.wxpay.privateKey) {
    key = config.wxpay.privateKey.replace(/\\n/g, '\n')
  } else if (config.wxpay.privateKeyPath) {
    key = fs.readFileSync(config.wxpay.privateKeyPath, 'utf8')
  } else {
    throw new Error('WeChat Pay private key not configured')
  }
  if (!key.includes('-----BEGIN')) throw new Error('Invalid private key format (missing PEM header)')
  _cachedPrivateKey = key
  return _cachedPrivateKey
}

function generateNonce() {
  return crypto.randomBytes(16).toString('hex')
}

function buildSignMessage(method, url, timestamp, nonce, body) {
  return method + '\n' + url + '\n' + timestamp + '\n' + nonce + '\n' + (body || '') + '\n'
}

function signSHA256WithRSA(message) {
  const sign = crypto.createSign('RSA-SHA256')
  sign.update(message)
  return sign.sign(getPrivateKey(), 'base64')
}

function buildAuthHeader(method, url, body) {
  const timestamp = Math.floor(Date.now() / 1000).toString()
  const nonce = generateNonce()
  const message = buildSignMessage(method, url, timestamp, nonce, body || '')
  const signature = signSHA256WithRSA(message)
  return 'WECHATPAY2-SHA256-RSA2048 mchid="' + config.wxpay.mchId + '",nonce_str="' + nonce + '",signature="' + signature + '",timestamp="' + timestamp + '",serial_no="' + config.wxpay.mchSerialNo + '"'
}

function httpsRequest(method, path, body) {
  return new Promise((resolve, reject) => {
    const bodyStr = body ? JSON.stringify(body) : ''
    const auth = buildAuthHeader(method, path, bodyStr)
    const options = {
      hostname: 'api.mch.weixin.qq.com',
      port: 443,
      path: path,
      method: method,
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Authorization': auth,
        'User-Agent': 'jw-beauty-server/1.0'
      }
    }
    if (bodyStr) options.headers['Content-Length'] = Buffer.byteLength(bodyStr)

    const req = https.request(options, res => {
      let raw = ''
      res.on('data', chunk => { raw += chunk })
      res.on('end', () => {
        try {
          const data = JSON.parse(raw)
          if (res.statusCode >= 200 && res.statusCode < 300) {
            resolve(data)
          } else {
            reject(new Error(data.message || 'wxpay request failed: ' + res.statusCode))
          }
        } catch (e) {
          reject(new Error('wxpay response parse error'))
        }
      })
    })
    req.setTimeout(15000, () => { req.destroy(new Error('wxpay request timeout (15s)')) })
    req.on('error', reject)
    if (bodyStr) req.write(bodyStr)
    req.end()
  })
}

async function createPrepayOrder({ openid, orderId, amountFen, description }) {
  const path = '/v3/pay/transactions/jsapi'
  const body = {
    appid: config.wxpay.appid || config.wechat.appid,
    mchid: config.wxpay.mchId,
    description: description || 'LumiFlow 订阅',
    out_trade_no: orderId,
    notify_url: config.wxpay.notifyUrl,
    amount: { total: amountFen, currency: 'CNY' },
    payer: { openid: openid }
  }
  const result = await httpsRequest('POST', path, body)
  if (!result.prepay_id) throw new Error('prepay_id not returned')
  return result.prepay_id
}

function generatePaymentParams(prepayId) {
  const appId = config.wxpay.appid || config.wechat.appid
  const timeStamp = Math.floor(Date.now() / 1000).toString()
  const nonceStr = generateNonce()
  const pkg = 'prepay_id=' + prepayId
  const message = appId + '\n' + timeStamp + '\n' + nonceStr + '\n' + pkg + '\n'
  const paySign = signSHA256WithRSA(message)
  return { timeStamp, nonceStr, package: pkg, signType: 'RSA', paySign }
}

let _platformCerts = {}
let _platformCertsExpiry = 0

async function fetchPlatformCertificates() {
  if (_platformCertsExpiry > Date.now()) return _platformCerts
  try {
    const path = '/v3/certificates'
    const result = await httpsRequest('GET', path)
    const certs = {}
    for (const item of (result.data || [])) {
      const resource = item.encrypt_certificate
      if (!resource) continue
      const certPem = decryptResource(resource)
      certs[item.serial_no] = certPem
    }
    if (Object.keys(certs).length > 0) {
      _platformCerts = certs
      _platformCertsExpiry = Date.now() + 12 * 3600 * 1000
    }
  } catch (err) {
    console.error('[WXPAY] cert refresh failed, keeping old certs:', err.message)
    if (Object.keys(_platformCerts).length === 0) throw err
  }
  return _platformCerts
}

async function verifyNotifySignature(headers, rawBody) {
  const timestamp = headers['wechatpay-timestamp']
  const nonce = headers['wechatpay-nonce']
  const signature = headers['wechatpay-signature']
  const serial = headers['wechatpay-serial']
  if (!timestamp || !nonce || !signature || !serial) throw new Error('missing wechatpay headers')

  const now = Math.floor(Date.now() / 1000)
  if (Math.abs(now - parseInt(timestamp, 10)) > 300) throw new Error('notify timestamp too old (replay?)')

  try {
    const certs = await fetchPlatformCertificates()
    const publicKey = certs[serial]
    if (!publicKey) throw new Error('unknown platform certificate serial: ' + serial)
    const message = timestamp + '\n' + nonce + '\n' + rawBody + '\n'
    const verify = crypto.createVerify('RSA-SHA256')
    verify.update(message)
    if (!verify.verify(publicKey, signature, 'base64')) {
      throw new Error('notify signature verification failed')
    }
  } catch (err) {
    console.error('[WXPAY] signature verification failed:', err.message)
    throw err
  }
  return true
}

function decryptResource(resource) {
  if (!resource || !resource.ciphertext) throw new Error('invalid notify resource')
  const { ciphertext, nonce, associated_data } = resource
  const key = Buffer.from(config.wxpay.apiV3Key, 'utf8')
  const iv = Buffer.from(nonce, 'utf8')
  const aad = Buffer.from(associated_data || '', 'utf8')
  const data = Buffer.from(ciphertext, 'base64')
  const authTag = data.slice(data.length - 16)
  const encrypted = data.slice(0, data.length - 16)
  const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv)
  decipher.setAuthTag(authTag)
  decipher.setAAD(aad)
  let decrypted = decipher.update(encrypted, null, 'utf8')
  decrypted += decipher.final('utf8')
  return decrypted
}

function decryptNotifyResource(resource) {
  return JSON.parse(decryptResource(resource))
}

async function queryOrder(orderId) {
  const path = '/v3/pay/transactions/out-trade-no/' + orderId + '?mchid=' + config.wxpay.mchId
  return httpsRequest('GET', path)
}

module.exports = {
  isConfigured,
  createPrepayOrder,
  generatePaymentParams,
  verifyNotifySignature,
  decryptNotifyResource,
  queryOrder
}
