import assert from 'node:assert/strict';
import test from 'node:test';
import {
  buildV2JsapiParams,
  buildXml,
  createWechatPayClient,
  loadWechatPayConfig,
  parseXmlFields,
  signV2Params,
} from './wechat-pay.mjs';

test('signV2Params is deterministic and uppercase MD5', () => {
  const params = {
    appid: 'wx8888888888888888',
    mch_id: '1900000109',
    body: 'test',
    nonce_str: 'ibuaiVcKdpRxkhJA',
  };
  const apiKey = '8934e7d15453e97507ef794cf7b0519d';
  const sign = signV2Params(params, apiKey);
  assert.match(sign, /^[0-9A-F]{32}$/);
  assert.equal(sign, signV2Params(params, apiKey));
  assert.notEqual(sign, signV2Params({ ...params, body: 'other' }, apiKey));
});

test('parseXmlFields and buildXml round-trip', () => {
  const xml = buildXml({
    return_code: 'SUCCESS',
    out_trade_no: 'TK123',
    total_fee: '500',
  });
  const fields = parseXmlFields(xml);
  assert.equal(fields.return_code, 'SUCCESS');
  assert.equal(fields.out_trade_no, 'TK123');
  assert.equal(fields.total_fee, '500');
});

test('loadWechatPayConfig selects v2 when APIv2 key is configured', () => {
  const previous = {
    enabled: process.env.H5_WECHAT_PAY_ENABLED,
    appId: process.env.H5_WECHAT_APP_ID,
    mchId: process.env.H5_WECHAT_MCH_ID,
    apiV2Key: process.env.H5_WECHAT_API_V2_KEY,
    apiVersion: process.env.H5_WECHAT_API_VERSION,
    notifyUrl: process.env.H5_WECHAT_NOTIFY_URL,
    v3Key: process.env.H5_WECHAT_API_V3_KEY,
    serialNo: process.env.H5_WECHAT_SERIAL_NO,
    privateKey: process.env.H5_WECHAT_PRIVATE_KEY,
  };
  process.env.H5_WECHAT_PAY_ENABLED = '1';
  process.env.H5_WECHAT_APP_ID = 'wxtestappid';
  process.env.H5_WECHAT_MCH_ID = '1234567890';
  process.env.H5_WECHAT_API_V2_KEY = 'test-api-v2-key-32chars-long!!!!';
  process.env.H5_WECHAT_NOTIFY_URL = 'https://example.com/webhooks/wechat-pay/notify';
  delete process.env.H5_WECHAT_API_VERSION;
  delete process.env.H5_WECHAT_API_V3_KEY;
  delete process.env.H5_WECHAT_SERIAL_NO;
  delete process.env.H5_WECHAT_PRIVATE_KEY;
  try {
    const config = loadWechatPayConfig();
    assert.equal(config.apiVersion, 'v2');
    assert.equal(config.enabled, true);
    const client = createWechatPayClient(config);
    assert.equal(client.apiVersion, 'v2');
    assert.equal(client.enabled, true);
  } finally {
    for (const [key, value] of Object.entries(previous)) {
      const envKey = {
        enabled: 'H5_WECHAT_PAY_ENABLED',
        appId: 'H5_WECHAT_APP_ID',
        mchId: 'H5_WECHAT_MCH_ID',
        apiV2Key: 'H5_WECHAT_API_V2_KEY',
        apiVersion: 'H5_WECHAT_API_VERSION',
        notifyUrl: 'H5_WECHAT_NOTIFY_URL',
        v3Key: 'H5_WECHAT_API_V3_KEY',
        serialNo: 'H5_WECHAT_SERIAL_NO',
        privateKey: 'H5_WECHAT_PRIVATE_KEY',
      }[key];
      if (value === undefined) delete process.env[envKey];
      else process.env[envKey] = value;
    }
  }
});

test('buildV2JsapiParams signs prepay package for WeixinJSBridge', () => {
  const config = {
    appId: 'wxtestappid',
    apiKey: 'test-api-v2-key-32chars-long!!!!',
  };
  const params = buildV2JsapiParams(config, 'wx201410272009395522657a690389285100');
  assert.equal(params.appId, 'wxtestappid');
  assert.equal(params.package, 'prepay_id=wx201410272009395522657a690389285100');
  assert.equal(params.signType, 'MD5');
  assert.match(params.paySign, /^[0-9A-F]{32}$/);
});

test('verifyNotify validates v2 callback signature', () => {
  const apiKey = 'test-api-v2-key-32chars-long!!!!';
  const fields = {
    return_code: 'SUCCESS',
    result_code: 'SUCCESS',
    out_trade_no: 'TKORDER001',
    transaction_id: '4200001234',
    total_fee: '1000',
    nonce_str: 'abc123',
  };
  fields.sign = signV2Params(fields, apiKey);
  const body = buildXml(fields);
  const client = createWechatPayClient({
    enabled: true,
    apiVersion: 'v2',
    appId: 'wxtest',
    mchId: '123',
    apiKey,
    notifyUrl: 'https://example.com/notify',
    skipNotifyVerify: false,
  });
  const { transaction } = client.verifyNotify({ headers: {}, body });
  assert.equal(transaction.out_trade_no, 'TKORDER001');
  assert.equal(transaction.trade_state, 'SUCCESS');
  assert.equal(transaction.amount.total, 1000);
});
