/**
 * Client-side QRPH / EMV QRCPS utilities.
 *
 * All operations run in the browser — no external API calls.
 *
 * Exports:
 *   parseTlv(str)                      → { tag: value, … }
 *   buildTlv(fields)                   → EMV TLV string (sorted, CRC appended)
 *   injectAmount(qrphStr, amount)      → new QRPH string with amount + fresh CRC
 *   generateQrDataUrl(text, opts)      → Promise<dataURL> via qrcode library
 */

// ── CRC-16 / CCITT-FALSE (polynomial 0x1021, seed 0xFFFF) ──────────────────
function crc16(data) {
    let crc = 0xFFFF;
    for (let i = 0; i < data.length; i++) {
        crc ^= data.charCodeAt(i) << 8;
        for (let j = 0; j < 8; j++) {
            crc = (crc & 0x8000) ? ((crc << 1) ^ 0x1021) & 0xFFFF : (crc << 1) & 0xFFFF;
        }
    }
    return crc.toString(16).toUpperCase().padStart(4, '0');
}

// ── TLV parser ──────────────────────────────────────────────────────────────
export function parseTlv(data) {
    const fields = {};
    let pos = 0;
    while (pos + 4 <= data.length) {
        const tag = data.substring(pos, pos + 2);
        const len = parseInt(data.substring(pos + 2, pos + 4), 10);
        const val = data.substring(pos + 4, pos + 4 + len);
        fields[tag] = val;
        pos += 4 + len;
        if (tag === '63') break;
    }
    return fields;
}

// ── TLV builder ─────────────────────────────────────────────────────────────
// Reassembles fields in ascending tag order, appends a fresh CRC-16.
function buildTlv(fields) {
    // Sort numerically, excluding CRC (will be appended fresh)
    const sorted = Object.keys(fields)
        .filter(t => t !== '63')
        .sort((a, b) => parseInt(a, 10) - parseInt(b, 10));

    let body = '';
    for (const tag of sorted) {
        const val  = fields[tag];
        const lpad = String(val.length).padStart(2, '0');
        body += tag + lpad + val;
    }

    // Append CRC header then compute over the full string including header
    const withHeader = body + '6304';
    return withHeader + crc16(withHeader);
}

// ── Amount injector ─────────────────────────────────────────────────────────
/**
 * Returns a new QRPH string with field 54 (Transaction Amount) set to
 * the given amount and a freshly computed CRC.
 *
 * @param {string} qrphStr  Base QRPH string (stored static QR)
 * @param {number} amount   PHP amount, e.g. 500
 * @returns {string}        Modified QRPH string
 */
export function injectAmount(qrphStr, amount) {
    const fields = parseTlv(qrphStr);

    if (!amount || amount <= 0) {
        // Remove amount field and rebuild
        delete fields['54'];
    } else {
        // EMV spec: amount as decimal string, e.g. "500.00"
        fields['54'] = parseFloat(amount).toFixed(2);
    }

    return buildTlv(fields);
}

// ── QR image renderer ───────────────────────────────────────────────────────
/**
 * Renders a string as a QR code and returns a data URL (PNG).
 *
 * @param {string} text
 * @param {object} opts  qrcode options (width, margin, color…)
 * @returns {Promise<string>} data URL
 */
export async function generateQrDataUrl(text, opts = {}) {
    const QRCode = (await import('qrcode')).default;
    return QRCode.toDataURL(text, {
        width:            opts.width   ?? 260,
        margin:           opts.margin  ?? 2,
        errorCorrectionLevel: opts.ecl ?? 'M',
        color: {
            dark:  opts.dark  ?? '#000000',
            light: opts.light ?? '#FFFFFF',
        },
    });
}
