import QRCode from 'qrcode';

import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/ae_api/api';

import { db_core } from '$lib/ae_core/db_core';

const ae_promises: key_val = {};

// I recently made significant updates to this with the help of Copilot. I think it is correct.
// Updated 2025-10-03
export async function generate_qr_code({
    api_cfg,
    account_id,
    qr_type, // mecard, obj, str, vcard
    qr_id, // This is essentially the filename it can be found at /qr/{account_id}/{qr_id}
    qr_data, // vcard fields:
    obj_type,
    obj_id,
    str, // For encoding a string (like a URL) into a QR code.
    return_blob = true, // blob or url?
    log_lvl = 0
}: {
    api_cfg: any;
    account_id: string;
    qr_type: string;
    qr_id: string;
    qr_data?: any;
    obj_type?: string;
    obj_id?: string;
    str?: string;
    return_blob?: boolean;
    log_lvl?: number;
}) {
    if (log_lvl) {
        console.log(`*** generate_qr_code() *** qr_id=${qr_id}`);
    }

    const endpoint = `/qr/${account_id}/${qr_id}`;
    if (log_lvl) {
        console.log('Endpoint', endpoint);
    }
    const params: key_val = {
        regen: true, // Regenerate the file even if nothing has changed.
        return_file: return_blob,
        qr_type: qr_type, // mecard, obj, vcard
        qr_send: return_blob
    };

    if (qr_type == 'vcard') {
        if (qr_data.informal_name) {
            params['n'] =
                `${qr_data.family_name};${qr_data.given_name};${qr_data.informal_name}`;
        } else {
            params['n'] = `${qr_data.family_name};${qr_data.given_name}`;
        }
        params['fn'] = qr_data.full_name_override;
        if (qr_data.affiliations) {
            params['org'] = qr_data.affiliations;
        }
        params['email'] = qr_data.email;
        if (qr_data.phone) {
            params['tel'] = qr_data.phone;
        }
        params['adr'] = qr_data.location_override;
        if (qr_data.address_line_1) {
            params['adr_str'] = qr_data.address_line_1;
        }
        params['adr_loc'] = qr_data.city;
        params['adr_reg'] = qr_data.state_province;
        params['adr_postal'] = qr_data.postal_code;
        params['adr_country'] = qr_data.country;
    } else if (qr_type == 'obj') {
        params['obj_type'] = obj_type;
        params['obj_id'] = obj_id;
    } else if (qr_type == 'str') {
        params['str'] = str;
    }

    if (log_lvl) {
        console.log('Params', params);
    }

    const filename = null;

    // Await the API call
    ae_promises.generate_qr_code = await api.get_object({
        api_cfg: api_cfg,
        endpoint: endpoint,
        params: params,
        return_blob: return_blob,
        filename: filename,
        auto_download: false,
        log_lvl: log_lvl
    });

    if (log_lvl) {
        console.log('QR code API response:', ae_promises.generate_qr_code);
    }

    // If return_blob is true, ensure we return an object URL for use in <img src=...>
    if (return_blob) {
        const data =
            ae_promises.generate_qr_code.data ?? ae_promises.generate_qr_code;

        // If already a Blob, use it directly
        if (data instanceof Blob) {
            return URL.createObjectURL(data);
        }

        // If it's a Response (from fetch), convert to Blob
        if (data instanceof Response) {
            const blob = await data.blob();
            return URL.createObjectURL(blob);
        }

        // If it's an ArrayBuffer or Uint8Array, convert to Blob
        if (data instanceof ArrayBuffer || data instanceof Uint8Array) {
            const blob = new Blob([data as BlobPart], { type: 'image/png' });
            return URL.createObjectURL(blob);
        }

        // If it's a base64 string, return as data URL
        if (typeof data === 'string' && data.startsWith('data:image')) {
            return data;
        }

        // If it's a raw string (base64), convert to data URL
        if (typeof data === 'string') {
            return `data:image/png;base64,${data}`;
        }

        // Fallback: try to create a Blob from whatever is left
        try {
            const blob = new Blob([data as BlobPart], { type: 'image/png' });
            return URL.createObjectURL(blob);
        } catch (e) {
            if (log_lvl)
                console.error('Could not create QR code image blob:', e, data);
            return null;
        }
    }

    // If not returning a blob, return the raw API response
    return ae_promises.generate_qr_code;
}

// Updated 2025-10-09
/**
 * Generates a QR code image as a Base64 data URL based on provided parameters.
 *
 * NOTE: This function handles the data formatting and QR generation,
 * but does NOT handle server-side logic like file saving, disk path checks,
 * or database lookups (like redis_lookup_id_random).
 *
 * @param {string} qr_type - The type of data format ('mecard', 'vcard', 'obj', 'kv', 'js', 'str').
 * @param {Object} params - An object containing all necessary query parameters (n, fn, url, email, etc.).
 * @returns {Promise<string>} A promise that resolves to a Base64 data URL of the QR code image.
 * @throws {Error} If the qr_type is unknown or data is missing.
 */
export async function js_generate_qr_code(
    qr_type: string,
    params: key_val = {}
) {
    const {
        n = '',
        fn = '',
        org = '',
        url = '',
        email = '',
        tel = '',
        adr_poa = '',
        adr_ext = '',
        adr_str = '',
        adr_loc = '',
        adr_reg = '',
        adr_postal = '',
        adr_country = '',
        obj_type,
        obj_id,
        key,
        val,
        js,
        str,
        log_lvl = 0
    } = params;

    if (log_lvl >= 2)
        console.log(`*** js_generate_qr_code() *** qr_type=${qr_type}`, params);

    let qr_data: string | null = null;

    // --- 1. Replicate Data Formatting Logic ---
    switch (qr_type) {
        case 'mecard':
            // Format: MECARD:N:name;EMAIL:email;ADR:address;;
            // Note: The original Python code had adr, but we'll use n and email as a minimum.
            qr_data = `MECARD:N:${n};EMAIL:${email};;`;
            // You can enhance this with other MeCard fields if needed.
            break;

        case 'vcard':
            // Format: BEGIN:VCARD...END:VCARD
            qr_data = `BEGIN:VCARD\nVERSION:3.0\nN:${n}\nFN:${fn}\nORG:${org}\nEMAIL:${email}\n`;

            if (url) {
                qr_data += `URL:${url}\n`;
            }
            if (tel) {
                qr_data += `TEL:${tel}\n`;
            }
            if (adr_loc) {
                // ADR format: TYPE=postal:Po box;Ext;Street;Locality;Region;Postal code;Country
                qr_data += `ADR:${adr_poa};${adr_ext};${adr_str};${adr_loc};${adr_reg};${adr_postal};${adr_country}\n`;
            }
            qr_data += 'END:VCARD';
            break;

        case 'obj':
            // Custom format: OBJ:ot:obj_type,oi:obj_id
            if (!obj_type || !obj_id)
                throw new Error('Missing obj_type or obj_id for type "obj".');
            qr_data = `OBJ:ot:${obj_type},oi:${obj_id}`;
            break;

        case 'kv':
            // Custom format: KV:k:"key",v:"val"
            if (!key || !val)
                throw new Error('Missing key or val for type "kv".');
            qr_data = `KV:k:"${key}",v:"${val}"`;
            break;

        case 'js':
            // Assumes 'js' is a stringified JSON object
            if (!js) throw new Error('Missing js string for type "js".');
            qr_data = `JS:${js}`;
            break;

        case 'str':
            // Raw string data
            if (!str)
                throw new Error('Missing raw string data for type "str".');
            qr_data = str;
            break;

        default:
            throw new Error(`Unknown QR type: ${qr_type}`);
    }

    if (!qr_data) {
        throw new Error('Failed to create QR code data string.');
    }

    // --- 2. Generate QR Code Image ---
    try {
        // Options match the Python 'qrcode' library defaults closely:
        // error_correction = qrcode.constants.ERROR_CORRECT_M
        // box_size = 10, border = 1
        const data_url = await QRCode.toDataURL(qr_data, {
            errorCorrectionLevel: 'M',
            margin: 1, // Corresponds to border
            scale: 10, // Corresponds to box_size
            type: 'image/png'
        });
        return data_url;
    } catch (error) {
        console.error('Error generating QR code:', error);
        throw new Error('Could not generate QR code image.');
    }
}
