// Web Bluetooth API + ESC/POS for thermal printers
// Supported: Chrome Android, Chrome Desktop (not Safari/Firefox)

const ESC = 0x1B;
const GS  = 0x1D;

// Service/characteristic UUID pairs for common Bluetooth thermal printers
const BT_PROFILES = [
  // Xprinter, iDPRT, and many Chinese 58/80mm printers
  { service: '000018f0-0000-1000-8000-00805f9b34fb', char: '00002af1-0000-1000-8000-00805f9b34fb' },
  // GOOJPRT, ZJ-5802, and generic BT printers
  { service: '0000ffe0-0000-1000-8000-00805f9b34fb', char: '0000ffe1-0000-1000-8000-00805f9b34fb' },
  // Epson Mobile/TM series
  { service: 'e7810a71-73ae-499d-8c15-faa9aef0c3f2', char: 'bef8d6c9-9c21-4c9e-b632-bd58c1009f9f' },
  // Nordic UART (Zebra, Brother, some Sewoo)
  { service: '49535343-fe7d-4ae5-8fa9-9fafd205e455', char: '49535343-8841-43f4-a8d4-ecbe34729bb3' },
];

let _device = null;
let _char   = null;
let _onDisconnect = null;

export function isBluetoothSupported() {
  return typeof navigator !== 'undefined' && !!navigator.bluetooth;
}

export function isConnected() {
  return !!(_device?.gatt?.connected && _char);
}

export function getDeviceName() {
  return _device?.name || null;
}

// Connect to printer — opens browser device picker
export async function connect(onDisconnect) {
  if (!navigator.bluetooth) {
    throw new Error(
      'Browser tidak mendukung Web Bluetooth.\nGunakan Chrome di Android atau Chrome Desktop.',
    );
  }

  const device = await navigator.bluetooth.requestDevice({
    acceptAllDevices: true,
    optionalServices: BT_PROFILES.map(p => p.service),
  });

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

  // Try each profile until one works
  let char = null;
  for (const profile of BT_PROFILES) {
    try {
      const svc = await server.getPrimaryService(profile.service);
      char = await svc.getCharacteristic(profile.char);
      break;
    } catch { /* try next */ }
  }

  if (!char) {
    device.gatt.disconnect();
    throw new Error(
      'Printer tidak dikenali.\nPastikan printer sudah di-pair di pengaturan Bluetooth perangkat dan dalam jangkauan.',
    );
  }

  _device = device;
  _char   = char;
  _onDisconnect = onDisconnect;

  _device.addEventListener('gattserverdisconnected', () => {
    _device = null;
    _char   = null;
    _onDisconnect?.();
  });

  return device.name || 'Bluetooth Printer';
}

export function disconnect() {
  if (_device?.gatt?.connected) _device.gatt.disconnect();
  _device = null;
  _char   = null;
}

// ── ESC/POS builder ───────────────────────────────────────────────────────────

const COLS = 32; // 58mm paper = 32 cols; change to 48 for 80mm paper

const enc = new TextEncoder();

function t(str) { return enc.encode(str); }
function b(...bytes) { return new Uint8Array(bytes); }

const INIT     = b(ESC, 0x40);
const LF       = b(0x0A);
const CENTER   = b(ESC, 0x61, 0x01);
const LEFT     = b(ESC, 0x61, 0x00);
const BOLD_ON  = b(ESC, 0x45, 0x01);
const BOLD_OFF = b(ESC, 0x45, 0x00);
const BIG      = b(ESC, 0x21, 0x11); // double-height + bold
const NORMAL   = b(ESC, 0x21, 0x00);
const CUT      = b(GS, 0x56, 0x41, 0x06); // feed 6 lines then full cut

function join(...parts) {
  let len = 0;
  for (const p of parts) len += p.length;
  const out = new Uint8Array(len);
  let i = 0;
  for (const p of parts) { out.set(p, i); i += p.length; }
  return out;
}

function row(str = '') {
  return t(str.slice(0, COLS) + '\n');
}

function dashes() {
  return row('-'.repeat(COLS));
}

// Left-aligned + right-aligned text on same line
function lr(left, right) {
  const gap = COLS - left.length - right.length;
  return row(left + (gap > 0 ? ' '.repeat(gap) : ' ') + right);
}

function rp(n) {
  return 'Rp' + Math.round(n || 0).toLocaleString('id-ID');
}

function buildEscPos(sale, settings) {
  const name    = settings?.business_name    || 'Antang Garage Oli';
  const address = settings?.business_address || '';
  const phone   = settings?.business_phone   || '';
  const message = settings?.receipt_message  || 'Terima kasih telah berkunjung!';
  const pmLabel = { tunai: 'Tunai', transfer: 'Transfer', qris: 'QRIS' };

  const parts = [
    INIT,
    // Store header
    CENTER, BOLD_ON, BIG,
    t(name.toUpperCase() + '\n'),
    NORMAL, BOLD_OFF,
  ];

  if (address) parts.push(row(address));
  if (phone)   parts.push(row('WA: ' + phone));

  parts.push(
    LEFT, dashes(),
    lr('Invoice:', sale.invoice_no || ''),
    lr('Kasir:', sale.cashier_name || ''),
    lr('Pembayaran:', pmLabel[sale.payment_method] || sale.payment_method || ''),
    dashes(),
  );

  // Line items
  for (const item of (sale.items || [])) {
    const qtyStr = String(parseFloat(item.qty));
    parts.push(
      BOLD_ON, row(item.item_name), BOLD_OFF,
      lr('  ' + qtyStr + 'x ' + rp(item.unit_price), rp(item.subtotal)),
    );
  }

  parts.push(
    dashes(),
    BOLD_ON, lr('TOTAL', rp(sale.total_amount)), BOLD_OFF,
  );

  if (sale.payment_method === 'tunai') {
    parts.push(
      lr('Bayar', rp(sale.amount_paid)),
      BOLD_ON, lr('Kembali', rp(sale.change_amount)), BOLD_OFF,
    );
  }

  parts.push(
    dashes(),
    CENTER,
    row(message),
    LF, LF,
    CUT,
  );

  return join(...parts);
}

// Send data in small chunks — BLE has MTU limits (~20–512 bytes)
async function sendChunked(data) {
  const CHUNK = 100;
  const noResp = _char.properties.writeWithoutResponse;
  for (let i = 0; i < data.length; i += CHUNK) {
    const slice = data.slice(i, i + CHUNK);
    if (noResp) {
      await _char.writeValueWithoutResponse(slice);
    } else {
      await _char.writeValue(slice);
    }
    await new Promise(r => setTimeout(r, 25));
  }
}

export async function printReceipt(sale, settings) {
  if (!_char) throw new Error('Printer Bluetooth belum terhubung');
  const data = buildEscPos(sale, settings);
  await sendChunked(data);
}
