import ReceiptPrinterEncoder from '@point-of-sale/receipt-printer-encoder';
import { useCallback, useRef, useState } from 'react';

export type PrinterConnectionType = 'bluetooth' | 'usb';
export type PaperWidth = 58 | 80;

interface PrinterState {
    connected: boolean;
    connectionType: PrinterConnectionType | null;
    printerName: string | null;
}

/* ── Receipt Builder (persis seperti contoh Vue) ── */

export function getPaperColumns(paperWidth: PaperWidth): number {
    return paperWidth === 58 ? 32 : 48;
}

function dashedLine(columns: number): string {
    return '-'.repeat(columns);
}

export type OrderReceiptItem = {
    product_name: string;
    variant_name: string;
    quantity: string;
    unit_price: string;
    subtotal: string;
};

export type OrderReceiptData = {
    order_number: string;
    created_at: string;
    customer_name: string | null;
    cashier_name: string;
    items: OrderReceiptItem[];
    subtotal: string;
    discount: string;
    nego_price: string | null;
    total_amount: string;
    notes: string | null;
};

export type EncodeOrderReceiptOptions = {
    storeName: string;
    storeAddress: string;
    paperWidth: PaperWidth;
};

export function encodeOrderReceipt(
    order: OrderReceiptData,
    options: EncodeOrderReceiptOptions,
): Uint8Array {
    const columns = getPaperColumns(options.paperWidth);
    const amountColumnWidth = 14;
    const labelColumnWidth = columns - amountColumnWidth;

    const encoder = new ReceiptPrinterEncoder({ columns });

    encoder
        .initialize()
        .align('center')
        .bold(true)
        .text('')
        .newline()
        .text(options.storeName)
        .newline()
        .bold(false)
        .newline();

    for (const addressLine of options.storeAddress.split('\n')) {
        const trimmed = addressLine.trim();

        if (trimmed) {
            encoder.align('center').text(trimmed).newline();
        }
    }

    encoder.align('center').line(dashedLine(columns));

    encoder
        .align('left')
        .line(`No   : ${order.order_number}`)
        .line(`Tgl  : ${order.created_at}`);

    if (order.customer_name) {
        encoder.line(`Cust : ${order.customer_name}`);
    }

    encoder
        .line(`Kasir: ${order.cashier_name}`)
        .line(dashedLine(columns));

    for (const item of order.items) {
        encoder
            .line(item.product_name)
            .line(`  ${item.variant_name}`)
            .table(
                [
                    { width: labelColumnWidth, align: 'left' },
                    { width: amountColumnWidth, align: 'right' },
                ],
                [
                    [
                        `${item.quantity} x ${item.unit_price}`,
                         
                        (rowEncoder: any) =>
                            rowEncoder
                                .bold()
                                .text(item.subtotal)
                                .bold(false),
                    ],
                ],
            )
            .newline();
    }

    encoder.line(dashedLine(columns));

    const summaryRows = [
        ['Subtotal', order.subtotal],
        ['Diskon', order.discount],
    ];

    if (order.nego_price) {
        summaryRows.push(['Harga Nego', order.nego_price]);
    }

    summaryRows.push(['Total', order.total_amount]);

    encoder.table(
        [
            { width: labelColumnWidth, align: 'left' },
            { width: amountColumnWidth, align: 'right' },
        ],
        summaryRows,
    );

    encoder
        .newline()
        .line(dashedLine(columns))
        .align('center')
        .line('Terima kasih atas kunjungannya')
        .line('Silakan berbelanja kembali')
        .newline(3)
        .cut();

    return encoder.encode();
}

/* ── Printer Connection Hook ── */

export function useThermalPrinter() {
    const [state, setState] = useState<PrinterState>({
        connected: false,
        connectionType: null,
        printerName: null,
    });

    const bluetoothDeviceRef = useRef<BluetoothDevice | null>(null);
    const bluetoothCharacteristicRef =
        useRef<BluetoothCharacteristic | null>(null);
    const usbDeviceRef = useRef<USBDevice | null>(null);

    const disconnect = useCallback(async () => {
        try {
            if (
                state.connectionType === 'bluetooth' &&
                bluetoothDeviceRef.current
            ) {
                bluetoothDeviceRef.current.gatt?.disconnect();
                bluetoothDeviceRef.current = null;
                bluetoothCharacteristicRef.current = null;
            }

            if (state.connectionType === 'usb' && usbDeviceRef.current) {
                usbDeviceRef.current.close();
                usbDeviceRef.current = null;
            }
        } catch {
            // silent
        }

        setState({
            connected: false,
            connectionType: null,
            printerName: null,
        });
    }, [state.connectionType]);

    const connectBluetooth = useCallback(async () => {
        if (!navigator.bluetooth) {
            throw new Error(
                'Web Bluetooth tidak didukung di browser ini. Gunakan Chrome atau Edge.',
            );
        }

        const device = await navigator.bluetooth.requestDevice({
            acceptAllDevices: true,
            optionalServices: [
                '000018f0-0000-1000-8000-00805f9b34fb',
                '00001800-0000-1000-8000-00805f9b34fb',
                '00001801-0000-1000-8000-00805f9b34fb',
                '00001101-0000-1000-8000-00805f9b34fb',
                '0000ffe0-0000-1000-8000-00805f9b34fb',
                '0000fee7-0000-1000-8000-00805f9b34fb',
            ],
        });

        const server = await device.gatt?.connect();

        if (!server) {
            throw new Error('Gagal terhubung ke perangkat Bluetooth.');
        }

        let characteristic: BluetoothCharacteristic | null = null;

        const services = await server.getPrimaryServices();

        for (const svc of services) {
            try {
                const chars = await svc.getCharacteristics();
                const writable = chars.find(
                    (c) =>
                        c.properties.writeWithoutResponse ||
                        c.properties.write,
                );

                if (writable) {
                    characteristic = writable;
                    break;
                }
            } catch {
                // skip
            }
        }

        if (!characteristic) {
            throw new Error(
                'Tidak ditemukan characteristic yang bisa ditulis di printer ini.',
            );
        }

        bluetoothDeviceRef.current = device;
        bluetoothCharacteristicRef.current = characteristic;

        setState({
            connected: true,
            connectionType: 'bluetooth',
            printerName: device.name ?? 'Printer Bluetooth',
        });

        return device.name ?? 'Printer Bluetooth';
    }, []);

    const connectUSB = useCallback(async () => {
        if (!navigator.usb) {
            throw new Error(
                'Web USB tidak didukung di browser ini. Gunakan Chrome atau Edge.',
            );
        }

        const device = await navigator.usb.requestDevice({ filters: [] });
        await device.open();

        if (device.configuration === null) {
            await device.selectConfiguration(1);
        }

        const iface =
            device.configuration?.interfaces.find((i) =>
                i.alternate.endpoints.some((e) => e.direction === 'out'),
            ) ?? device.configuration?.interfaces[0];

        if (iface) {
            await device.claimInterface(iface.interfaceNumber);
        }

        usbDeviceRef.current = device;

        setState({
            connected: true,
            connectionType: 'usb',
            printerName: device.productName ?? 'Printer USB',
        });

        return device.productName ?? 'Printer USB';
    }, []);

    const sendToBluetooth = useCallback(async (data: Uint8Array) => {
        const characteristic = bluetoothCharacteristicRef.current;

        if (!characteristic) {
            throw new Error(
                'Printer Bluetooth tidak terhubung.',
            );
        }

        const useWriteWithoutResponse =
            characteristic.properties.writeWithoutResponse;
        const chunkSize = 128;

        for (let i = 0; i < data.length; i += chunkSize) {
            const chunk = data.slice(i, i + chunkSize);

            if (useWriteWithoutResponse) {
                await characteristic.writeValueWithoutResponse(chunk);
            } else {
                await characteristic.writeValueWithResponse(chunk);
            }

            await new Promise((r) => setTimeout(r, 10));
        }
    }, []);

    const sendToUSB = useCallback(async (data: Uint8Array) => {
        const device = usbDeviceRef.current;

        if (!device) {
            throw new Error('Printer USB tidak terhubung.');
        }

        const iface = device.configuration?.interfaces.find((i) =>
            i.alternate.endpoints.some((e) => e.direction === 'out'),
        );
        const endpoint =
            iface?.alternate.endpoints.find(
                (e) => e.direction === 'out',
            )?.endpointNumber ?? 1;

        const chunkSize = 512;

        for (let i = 0; i < data.length; i += chunkSize) {
            const chunk = data.slice(i, i + chunkSize);
            await device.transferOut(endpoint, chunk);
        }
    }, []);

    const print = useCallback(
        async (data: Uint8Array) => {
            if (!state.connected) {
                throw new Error('Tidak ada printer yang terhubung.');
            }

            if (state.connectionType === 'bluetooth') {
                await sendToBluetooth(data);
            } else if (state.connectionType === 'usb') {
                await sendToUSB(data);
            }
        },
        [state.connected, state.connectionType, sendToBluetooth, sendToUSB],
    );

    const connect = useCallback(
        async (type: PrinterConnectionType) => {
            if (state.connected) {
                await disconnect();
            }

            if (type === 'bluetooth') {
                return await connectBluetooth();
            }

            return await connectUSB();
        },
        [state.connected, disconnect, connectBluetooth, connectUSB],
    );

    return {
        connected: state.connected,
        connectionType: state.connectionType,
        printerName: state.printerName,
        connect,
        disconnect,
        print,
    };
}
