import * as crypto from 'crypto';

/**
 * Generates a valid VNPay callback payload with a correct HMAC-SHA512 signature.
 *
 * Used in E2E tests to simulate a real VNPay callback without needing
 * the actual sandbox environment.
 */
export function buildVnpayCallbackData(
  hashSecret: string,
  opts: {
    orderId: string;
    amountVND: number;
    responseCode?: string;
    transactionNo?: string;
  },
): Record<string, string> {
  const params: Record<string, string> = {
    vnp_TxnRef: opts.orderId,
    vnp_ResponseCode: opts.responseCode ?? '00',
    vnp_Amount: (opts.amountVND * 100).toString(),
    vnp_TransactionNo: opts.transactionNo ?? `VNP${Date.now()}`,
    vnp_BankCode: 'NCB',
    vnp_CardType: 'ATM',
    vnp_OrderInfo: `E2E test payment ${opts.orderId}`,
    vnp_PayDate: formatVnpayDate(new Date()),
    vnp_TmnCode: process.env.VNPAY_TMN_CODE ?? 'TESTCODE',
    vnp_TransactionStatus: opts.responseCode === '00' ? '00' : '02',
  };

  const sorted = sortObject(params);
  const signData = new URLSearchParams(sorted).toString();
  const hmac = crypto.createHmac('sha512', hashSecret);
  const secureHash = hmac.update(Buffer.from(signData, 'utf-8')).digest('hex');

  return { ...sorted, vnp_SecureHash: secureHash };
}

/**
 * Generates a valid MoMo IPN callback payload with a correct HMAC-SHA256 signature.
 */
export function buildMomoCallbackData(
  secretKey: string,
  opts: {
    orderId: string;
    amount: number;
    resultCode?: number;
    transId?: string;
  },
): Record<string, string> {
  const resultCode = opts.resultCode ?? 0;
  const transId = opts.transId ?? `MOMO${Date.now()}`;
  const requestId = crypto.randomUUID();
  const extraData = '';
  const message = resultCode === 0 ? 'Successful.' : 'Transaction failed.';
  const orderInfo = `E2E test payment ${opts.orderId}`;
  const orderType = 'momo_wallet';
  const payType = 'qr';
  const responseTime = Date.now().toString();
  const partnerCode = process.env.MOMO_PARTNER_CODE ?? 'TESTPARTNER';
  const accessKey = process.env.MOMO_ACCESS_KEY ?? 'TESTACCESSKEY';

  const rawSignature = [
    `accessKey=${accessKey}`,
    `amount=${opts.amount}`,
    `extraData=${extraData}`,
    `message=${message}`,
    `orderId=${opts.orderId}`,
    `orderInfo=${orderInfo}`,
    `orderType=${orderType}`,
    `partnerCode=${partnerCode}`,
    `payType=${payType}`,
    `requestId=${requestId}`,
    `responseTime=${responseTime}`,
    `resultCode=${resultCode}`,
    `transId=${transId}`,
  ].join('&');

  const signature = crypto
    .createHmac('sha256', secretKey)
    .update(rawSignature)
    .digest('hex');

  return {
    orderId: opts.orderId,
    amount: opts.amount.toString(),
    extraData,
    message,
    orderInfo,
    orderType,
    partnerCode,
    payType,
    requestId,
    responseTime,
    resultCode: resultCode.toString(),
    transId,
    signature,
  };
}

function formatVnpayDate(date: Date): string {
  return date.toISOString().replace(/[-:T]/g, '').slice(0, 14);
}

function sortObject(obj: Record<string, string>): Record<string, string> {
  const sorted: Record<string, string> = {};
  for (const key of Object.keys(obj).sort()) {
    sorted[key] = obj[key]!;
  }
  return sorted;
}
