// EMVCo & Raast Standard QR Generator for Pakistani Banking Apps (Meezan, HBL, Bank Alfalah, Easypaisa, JazzCash, Alfa, Nayapay, Sadapay, Soneri, etc.)

function calculateCRC16(data: string): string {
  let crc = 0xffff;
  for (let i = 0; i < data.length; i++) {
    const byte = data.charCodeAt(i);
    crc ^= byte << 8;
    for (let j = 0; j < 8; j++) {
      if ((crc & 0x8000) !== 0) {
        crc = ((crc << 1) ^ 0x1021) & 0xffff;
      } else {
        crc = (crc << 1) & 0xffff;
      }
    }
  }
  return crc.toString(16).toUpperCase().padStart(4, '0');
}

function formatTLV(tag: string, value: string): string {
  if (!value) return '';
  const length = value.length.toString().padStart(2, '0');
  return `${tag}${length}${value}`;
}

export interface QRPayloadOptions {
  iban: string;
  accountNumber: string;
  accountTitle: string;
  bankName: string;
  amount: number;
  orderId?: string;
}

/**
 * Generates an EMVCo compliant QR payload string.
 * When scanned by Pakistani banking apps (Raast, Meezan, HBL, Easypaisa, JazzCash, etc.),
 * it automatically populates the IBAN / Account, Beneficiary Title, Currency (PKR), and Exact Order Amount.
 */
export function generateBankingQRPayload(options: QRPayloadOptions): string {
  const { iban, accountNumber, accountTitle, bankName, amount, orderId } = options;

  // Clean IBAN and Account Number (strip spaces, hyphens, non-alphanumeric)
  const cleanIban = (iban || '').replace(/[^a-zA-Z0-9]/g, '').toUpperCase();
  const cleanAccount = (accountNumber || '').replace(/[^a-zA-Z0-9]/g, '').toUpperCase();

  // Prefer clean IBAN if valid (>= 15 chars, standard PK IBAN is 24 chars), otherwise fallback to clean account number
  const targetId = cleanIban.length >= 15 ? cleanIban : cleanAccount;

  // Format amount clean to up to 2 decimal places or clean string
  const numericAmount = Math.max(0, Number(amount) || 0);
  const amountStr = Number.isInteger(numericAmount)
    ? numericAmount.toString()
    : numericAmount.toFixed(2);

  // Clean title & bank name for ASCII TLV compliance (max 25 chars for EMVCo Tag 59 / Tag 28)
  const cleanTitle = (accountTitle || 'Time N Life')
    .replace(/[^\x20-\x7E]/g, '')
    .trim()
    .substring(0, 25);

  const cleanBank = (bankName || 'Meezan Bank')
    .replace(/[^\x20-\x7E]/g, '')
    .trim()
    .substring(0, 25);

  const refId = (orderId || 'TNL-ORDER').replace(/[^a-zA-Z0-9-]/g, '').substring(0, 25);

  // Sub-tags for Merchant Account Information (Tag 26 - Raast Standard)
  const tag26Sub00 = formatTLV('00', 'PK.RAAST');
  const tag26Sub01 = formatTLV('01', targetId);
  const tag26Value = `${tag26Sub00}${tag26Sub01}`;

  // Sub-tags for 1LINK / Local Bank Transfer (Tag 28)
  const tag28Sub00 = formatTLV('00', 'PK.BANKS');
  const tag28Sub01 = formatTLV('01', targetId);
  const tag28Sub02 = formatTLV('02', cleanBank);
  const tag28Value = `${tag28Sub00}${tag28Sub01}${tag28Sub02}`;

  // Tag 62 (Additional Data - Reference/Order ID)
  const tag62Sub01 = formatTLV('01', refId);

  // Construct EMVCo Payload Parts
  let payload = '';
  payload += formatTLV('00', '01'); // Payload Format Indicator
  payload += formatTLV('01', '12'); // Point of Initiation (12 = Dynamic with Amount)
  payload += formatTLV('26', tag26Value); // Raast Merchant Info
  payload += formatTLV('28', tag28Value); // 1LINK / Local Bank Info
  payload += formatTLV('52', '5999'); // Merchant Category Code
  payload += formatTLV('53', '586'); // Currency (586 = PKR - Pakistani Rupee)
  if (numericAmount > 0) {
    payload += formatTLV('54', amountStr); // Exact Transaction Amount
  }
  payload += formatTLV('58', 'PK'); // Country Code (Pakistan)
  payload += formatTLV('59', cleanTitle); // Account Title / Merchant Name
  payload += formatTLV('60', 'Karachi'); // City
  if (tag62Sub01) {
    payload += formatTLV('62', tag62Sub01); // Additional Data (Order ID)
  }

  // Tag 63 is CRC16 Checksum
  payload += '6304';
  const checksum = calculateCRC16(payload);

  return `${payload}${checksum}`;
}

