const express = require('express');
const axios = require('axios');
const https = require('https');
const { buildFromOperations, EposMessageBuilder } = require('./eposBuilder');
const Jimp = require('jimp');

const app = express();
app.use(express.json({ limit: '1mb' }));
app.use(express.static('public'));

// Config
const PRINTER_HOST = process.env.PRINTER_HOST || '192.168.87.147';
const PRINTER_DEVICE_ID = process.env.PRINTER_DEVICE_ID || 'matricial2';
const PRINTER_TIMEOUT_MS = parseInt(process.env.PRINTER_TIMEOUT_MS || '60000', 10);

function buildSoapEnvelope(inner) {
  return (
    '<?xml version="1.0" encoding="utf-8"?>' +
    '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">' +
    '<s:Body>' +
    '<epos-print xmlns="http://www.epson-pos.com/schemas/2011/03/epos-print">' +
    inner +
    '</epos-print>' +
    '</s:Body>' +
    '</s:Envelope>'
  );
}

async function sendToPrinter(xml) {
  const url = `https://${PRINTER_HOST}/cgi-bin/epos/service.cgi?devid=${encodeURIComponent(PRINTER_DEVICE_ID)}&timeout=${PRINTER_TIMEOUT_MS}`;
  const httpsAgent = new https.Agent({ rejectUnauthorized: false }); // self-signed OK
  const res = await axios.post(url, xml, {
    headers: { 'Content-Type': 'text/xml; charset=utf-8' },
    httpsAgent,
    timeout: PRINTER_TIMEOUT_MS + 5000,
    validateStatus: () => true,
  });
  return { status: res.status, headers: res.headers, data: typeof res.data === 'string' ? res.data : String(res.data) };
}

async function handlePrintOps(res, operations = [], dryRun = false) {
  const inner = buildFromOperations(operations);
  const soap = buildSoapEnvelope(inner);
  if (dryRun) {
    return res.json({ ok: true, dryRun: true, soap });
  }
  const result = await sendToPrinter(soap);
  const success = /success\s*=\s*"true"/.test(result.data);
  const codeMatch = result.data.match(/code=\"([^\"]*)\"/);
  const code = codeMatch ? codeMatch[1] : '';
  return res.json({ ok: success, httpStatus: result.status, code, raw: result.data });
}

// Generic endpoint: accepts a list of operations and optional dryRun
app.post('/api/print', async (req, res) => {
  try {
    const { operations = [], dryRun = false } = req.body || {};
    await handlePrintOps(res, operations, dryRun);
  } catch (err) {
    return res.status(500).json({ ok: false, error: err.message });
  }
});

// Convenience endpoints
app.post('/api/print/text', async (req, res) => {
  try {
    const { text = '', options = {} } = req.body || {};
    const ops = [];
    if (options.align) ops.push({ op: 'textAlign', align: options.align });
    if (options.font) ops.push({ op: 'textFont', font: options.font });
    if (options.size) ops.push({ op: 'textSize', width: options.size.width, height: options.size.height });
    if (options.style) ops.push({ op: 'textStyle', ...options.style });
    ops.push({ op: 'text', value: text });
    if (options.feedLines) ops.push({ op: 'feedLine', line: options.feedLines });
    if (options.cut) ops.push({ op: 'cut', type: options.cut });
    await handlePrintOps(res, ops, false);
  } catch (err) {
    return res.status(500).json({ ok: false, error: err.message });
  }
});

app.post('/api/print/cut', async (req, res) => {
  try {
    const type = (req.body && req.body.type) || 'feed';
    await handlePrintOps(res, [{ op: 'cut', type }], false);
  } catch (err) {
    return res.status(500).json({ ok: false, error: err.message });
  }
});

app.post('/api/print/pulse', async (req, res) => {
  try {
    const { drawer, time } = req.body || {};
    await handlePrintOps(res, [{ op: 'pulse', drawer, time }], false);
  } catch (err) {
    return res.status(500).json({ ok: false, error: err.message });
  }
});

// Image print endpoint: reads an image file and sends as mono raster
app.post('/api/print/image', async (req, res) => {
  try {
    const { path, width, threshold = 128, mode = 'mono' } = req.body || {};
    if (!path) return res.status(400).json({ ok: false, error: 'path required' });

    const img = await Jimp.read(path);
    let targetWidth = width || img.bitmap.width;
    if (targetWidth <= 0) targetWidth = img.bitmap.width;
    const scale = targetWidth / img.bitmap.width;
    const targetHeight = Math.max(1, Math.round(img.bitmap.height * scale));
    img.resize(targetWidth, targetHeight, Jimp.RESIZE_BILINEAR);
    img.grayscale();

    // pack bits MSB first per byte
    const bytesPerRow = Math.ceil(targetWidth / 8);
    const out = Buffer.alloc(bytesPerRow * targetHeight);
    let outIdx = 0;
    for (let y = 0; y < targetHeight; y++) {
      let byte = 0; let bit = 7; let rowBytes = 0;
      for (let x = 0; x < targetWidth; x++) {
        const idx = (y * targetWidth + x) * 4;
        const rgba = img.bitmap.data;
        const r = rgba[idx], g = rgba[idx + 1], b = rgba[idx + 2];
        const lum = 0.299 * r + 0.587 * g + 0.114 * b;
        const isBlack = lum < threshold; // simple threshold
        if (isBlack) byte |= (1 << bit);
        bit--;
        if (bit < 0) {
          out[outIdx++] = byte;
          rowBytes++;
          byte = 0; bit = 7;
        }
      }
      // pad remaining bits
      if (bit !== 7) { out[outIdx++] = byte; rowBytes++; }
      // pad to full row bytes if needed
      while (rowBytes < bytesPerRow) { out[outIdx++] = 0; rowBytes++; }
    }

    const base64 = out.toString('base64');
    const builder = new EposMessageBuilder();
    builder.imageRaw({ width: targetWidth, height: targetHeight, mode, base64 });
    const soap = buildSoapEnvelope(builder.build());
    const result = await sendToPrinter(soap);
    const success = /success\s*=\s*"true"/.test(result.data);
    const codeMatch = result.data.match(/code=\"([^\"]*)\"/);
    const code = codeMatch ? codeMatch[1] : '';
    return res.json({ ok: success, httpStatus: result.status, code, raw: result.data, width: targetWidth, height: targetHeight });
  } catch (err) {
    return res.status(500).json({ ok: false, error: err.message });
  }
});

const PORT = process.env.PORT || 3030;
app.listen(PORT, () => {
  console.log(`Printer Central UI listening on http://localhost:${PORT}`);
  console.log(`Target printer: https://${PRINTER_HOST} devid=${PRINTER_DEVICE_ID}`);
});
