const fs = require("fs");
const path = require("path");
const { receiptKindToCopyType } = require("./receipt-kind");
const { getPaperMetrics } = require("./paper-metrics");
const {
  resolveLogoFile,
  findLogoPath,
  shouldShowLogo,
  resolveLogoScale,
  buildLogoEscPos,
} = require("./logo-escpos");
const { buildQrRasterEscPos, buildQrRowRasterEscPos, truncateQrLabel, qrRowLayout } = require("./qr-escpos");
const { sanitizeForPrinter, wrapForWidth } = require("./escpos-text");

const ESC = "\x1B";
const GS = "\x1D";

const CMD = {
  INIT: ESC + "@",
  ALIGN_LEFT: ESC + "a" + "\x00",
  ALIGN_CENTER: ESC + "a" + "\x01",
  ALIGN_RIGHT: ESC + "a" + "\x02",
  BOLD_ON: ESC + "E" + "\x01",
  BOLD_OFF: ESC + "E" + "\x00",
  /** Reset character size / emphasis (Epson GS ! and ESC E). */
  RESET_STYLE: GS + "!" + "\x00" + ESC + "E" + "\x00",
  CUT: GS + "V" + "\x41" + "\x00",
};

function isV2Template(t) {
  return t && t.version === 2;
}

function divider(width, style) {
  const w = Math.max(8, width || 32);
  const ch = style === "equals" ? "=" : style === "dot" ? "." : "-";
  return `${ch.repeat(w)}`;
}

function appendDividerEscPos(receipt, width, style) {
  receipt += CMD.ALIGN_LEFT;
  receipt += `${divider(width, style)}\n`;
  return receipt;
}

function formatCurrency(amount, prefix) {
  return `${prefix || "Rs "}${parseFloat(amount || 0).toFixed(2)}`;
}

function centerLine(text, width) {
  const t = String(text || "");
  const w = Math.max(8, width || 32);
  if (t.length >= w) return t.slice(0, w);
  const left = Math.floor((w - t.length) / 2);
  return `${" ".repeat(left)}${t}`;
}

function rightLine(text, width) {
  const t = String(text || "");
  const w = Math.max(8, width || 32);
  if (t.length >= w) return t.slice(0, w);
  return `${" ".repeat(w - t.length)}${t}`;
}

function footerRowWithQr(text, width, qrMarker = "[QR]") {
  const w = Math.max(8, width || 32);
  const marker = qrMarker.slice(0, 4);
  const textArea = w - marker.length;
  const centered = centerLine(text, textArea);
  return `${centered}${marker}`;
}

function resolveCopyType(order, receiptKind, template) {
  if (template?.header?.banner) return String(template.header.banner);
  if (order.copyType) return String(order.copyType).toUpperCase();
  if (receiptKind) return receiptKindToCopyType(receiptKind);
  return "COUNTER";
}

function resolveBusinessInfo(template) {
  const header = template?.header || {};
  const title = String(header.title || "").trim();
  const lines = Array.isArray(header.lines)
    ? header.lines.map((l) => String(l || "").trim()).filter(Boolean)
    : [];
  return { title, lines };
}

function getSections(template) {
  return template?.sections || {};
}

function getConditionals(template) {
  return template?.conditionals || {};
}

function showPosField(template, key, defaultOn = true) {
  const cond = getConditionals(template);
  if (cond[key] === false) return false;
  if (cond[key] === true) return true;
  return defaultOn;
}

function getVisual(template) {
  if (isV2Template(template)) {
    return {
      currencyPrefix: template.visual?.currencyPrefix || "Rs ",
      dividerStyle: template.visual?.dividerStyle || "dash",
    };
  }
  return {
    currencyPrefix: (template.styles && template.styles.currencyPrefix) || "Rs ",
    dividerStyle: "dash",
  };
}

function resolveBusinessQrLinks(template, order, businessConfig, options = {}) {
  const hasFbr = Boolean(options.hasFbr);
  const maxLinks = hasFbr ? 2 : 3;
  const cond = getConditionals(template);
  const branding = template.branding || {};
  const qr = branding.qrCode || {};
  if (!qr.enabled && !cond.showQrCode) return [];

  let links = [];
  if (Array.isArray(qr.links) && qr.links.length) {
    links = qr.links
      .map((entry) => {
        if (typeof entry === "string") return { url: entry.trim() };
        return { url: String(entry?.url || "").trim(), label: entry?.label || "" };
      })
      .filter((entry) => entry.url);
  } else if (qr.linkUrl && String(qr.linkUrl).trim()) {
    links = [{ url: String(qr.linkUrl).trim() }];
  } else if (qr.customText && String(qr.customText).trim()) {
    links = [{ url: String(qr.customText).trim() }];
  }

  if (!links.length) {
    const headerLines = (template.header && template.header.lines) || [];
    for (const line of headerLines) {
      const s = String(line || "").trim();
      if (/^https?:\/\//i.test(s)) {
        links.push({ url: s });
        break;
      }
    }
  }

  if (!links.length) {
    const cfg = businessConfig || {};
    const website =
      (order.businessWebsite && String(order.businessWebsite).trim()) ||
      (cfg.BUSINESS_WEBSITE && String(cfg.BUSINESS_WEBSITE).trim()) ||
      "";
    if (website) links = [{ url: website }];
  }

  return links.slice(0, maxLinks);
}

function resolveBusinessQrUrl(template, order, businessConfig) {
  const links = resolveBusinessQrLinks(template, order, businessConfig, { hasFbr: false });
  return links[0]?.url || null;
}

function resolveFbrQr(order) {
  if (order.fbrQrPayload && String(order.fbrQrPayload).trim()) {
    return String(order.fbrQrPayload).trim();
  }
  if (order.fbrIrn && String(order.fbrIrn).trim()) {
    return String(order.fbrIrn).trim();
  }
  return null;
}

function hasFbrSection(template, order) {
  const sections = getSections(template);
  if (sections.fbr === false) return false;
  return Boolean(resolveFbrQr(order));
}

function resolveFooterLines(template) {
  const branding = template.branding || {};
  return (
    (template.footer && Array.isArray(template.footer.lines) && template.footer.lines.length
      ? template.footer.lines
      : null) ||
    (branding.footerMessage ? [branding.footerMessage] : null) || [
      "Thank you for your order!",
    ]
  );
}

function hasLogo(template) {
  return shouldShowLogo(template) && Boolean(findLogoPath() || resolveLogoFile(template));
}

function buildBodyLines(template, order, opts = {}) {
  const lines = [];
  const t = template || {};
  const sections = getSections(t);
  const cond = getConditionals(t);
  const visual = getVisual(t);
  const metrics = getPaperMetrics(t.paperWidth || 32);
  const width = metrics.chars;
  const prefix = visual.currencyPrefix;
  const receiptKind = opts.receiptKind || t.receiptKind;
  const copyBanner = resolveCopyType(order, receiptKind, t);

  const pushDivider = () => lines.push({ text: divider(width, visual.dividerStyle), style: "divider" });
  const pushRight = (text, style = "small") => lines.push({ text, align: "right", style });
  const pushLeft = (text, style = "normal") => lines.push({ text, align: "left", style });

  if (copyBanner === "KITCHEN" || receiptKind === "kitchen_ticket") {
    lines.push({ text: "**** KITCHEN COPY ****", align: "center", style: "banner" });
    pushDivider();
  } else if (t.header?.banner) {
    lines.push({ text: t.header.banner, align: "center", style: "banner" });
    pushDivider();
  } else if (
    copyBanner &&
    copyBanner !== "COUNTER" &&
    typeof copyBanner === "string" &&
    !copyBanner.startsWith("****")
  ) {
    lines.push({ text: copyBanner, align: "center", style: "banner" });
    pushDivider();
  }

  if (sections.orderMeta !== false) {
    const now = new Date();
    const dateStr = now.toLocaleDateString("en-PK", {
      day: "2-digit",
      month: "short",
      year: "numeric",
    });
    const timeStr = now.toLocaleTimeString("en-PK", {
      hour: "2-digit",
      minute: "2-digit",
      hour12: true,
    });
    pushLeft(`Order#: ${order.orderNumber || "N/A"}`);
    pushLeft(`Type: ${order.orderType || "dine-in"}`);
    if (showPosField(t, "showTableNumber", false) && order.tableNumber) {
      pushLeft(`Table: ${order.tableNumber}${order.tableName ? ` (${order.tableName})` : ""}`);
    }
    if (order.orderTime && showPosField(t, "showOrderTime", true)) {
      const dt = new Date(order.orderTime);
      if (!Number.isNaN(dt.getTime())) {
        pushLeft(
          `Date: ${dt.toLocaleDateString("en-PK", { day: "2-digit", month: "short", year: "numeric" })}  Time: ${dt.toLocaleTimeString("en-PK", { hour: "2-digit", minute: "2-digit", hour12: true })}`,
        );
      }
    } else {
      pushLeft(`Date: ${dateStr}  Time: ${timeStr}`);
    }
    if (cond.showCustomerInfo !== false && sections.customer !== false) {
      if (order.customerName) pushLeft(`Customer: ${order.customerName}`);
      if (order.customerPhone) pushLeft(`Phone: ${order.customerPhone}`);
      if (order.customerAddress) pushLeft(`Address: ${order.customerAddress}`);
    }
    if (cond.showCashierInfo && sections.cashier !== false && order.cashierName) {
      pushLeft(`Cashier: ${order.cashierName}`);
    }
    if (showPosField(t, "showOrderNotes", true) && sections.notes !== false && order.notes) {
      pushLeft(`Notes: ${order.notes}`);
    }
    pushDivider();
  }

  if (sections.items !== false) {
    const nameW = metrics.itemNameCols;
    const moneyW = metrics.moneyCols;
    pushLeft(
      `${"Qty".padEnd(4)}${"Item".padEnd(nameW)}${"Total".padStart(moneyW)}`,
      "item-header",
    );
    pushDivider();
    const items = Array.isArray(order.items) ? order.items : [];
    if (items.length === 0) {
      pushLeft("No items");
    } else {
      items.forEach((item) => {
        const maxName = nameW;
        const name =
          item.name && item.name.length > maxName
            ? `${item.name.substring(0, Math.max(1, maxName - 3))}...`
            : item.name || "Item";
        const qty = item.quantity || 1;
        const total = item.total ?? qty * (item.price || 0);
        pushLeft(
          `${String(qty).padStart(2)}x ${name.padEnd(nameW)}${formatCurrency(total, prefix).padStart(moneyW)}`,
          "item",
        );
        if (
          showPosField(t, "showItemModifiers", true) &&
          Array.isArray(item.options) &&
          item.options.length
        ) {
          item.options.forEach((opt) => {
            pushLeft(`   - ${String(opt)}`, "small");
          });
        }
      });
    }
    pushDivider();
  }

  if (sections.totals !== false) {
    if (order.subtotal != null && showPosField(t, "showSubtotalLine", true)) {
      pushRight(`Subtotal: ${formatCurrency(order.subtotal, prefix)}`, "small");
    }
    if (order.packingCharges > 0 && showPosField(t, "showPackingLine", true)) {
      pushRight(`Packing: ${formatCurrency(order.packingCharges, prefix)}`, "small");
    }
    if (sections.discount !== false && order.discount > 0 && showPosField(t, "showDiscountLine", true)) {
      pushRight(`Discount: -${formatCurrency(order.discount, prefix)}`, "small");
      if (showPosField(t, "showDiscountPercent", false) && order.discountPercent > 0) {
        pushRight(`Discount %: ${order.discountPercent}%`, "small");
      }
    }
    if (sections.loyalty !== false && order.loyaltyDiscount > 0) {
      pushRight(`Loyalty: -${formatCurrency(order.loyaltyDiscount, prefix)}`, "small");
      if (order.loyaltyPointsApplied > 0) {
        pushRight(`Points used: ${order.loyaltyPointsApplied}`, "small");
      }
    }
    if (order.deliveryCharges > 0 && showPosField(t, "showDeliveryLine", true)) {
      pushRight(`Delivery: ${formatCurrency(order.deliveryCharges, prefix)}`, "small");
    }
    if (
      sections.tax !== false &&
      cond.showTaxDetails !== false &&
      showPosField(t, "showTaxLine", true) &&
      order.tax > 0
    ) {
      pushRight(`Tax: ${formatCurrency(order.tax, prefix)}`, "small");
    }
    pushRight(
      `TOTAL: ${formatCurrency(order.total ?? order.subtotal ?? 0, prefix)}`,
      "total",
    );
  }

  if (sections.payment !== false && order.paymentMethod) {
    pushDivider();
    pushRight(`Payment: ${order.paymentMethod}`, "normal");
    if (order.cashReceived) {
      pushRight(`Cash received: ${formatCurrency(order.cashReceived, prefix)}`, "small");
    }
    if (order.change) {
      pushRight(`Change: ${formatCurrency(order.change, prefix)}`, "small");
    }
  }

  return lines;
}

function formatLineForText(line, width) {
  const text = line.text || "";
  if (line.style === "divider") return `${text}\n`;
  if (line.align === "right") return `${rightLine(text, width)}\n`;
  if (line.align === "center") return `${centerLine(text, width)}\n`;
  return `${text}\n`;
}

function renderSocialLinks(template, width) {
  const links = template.branding?.socialLinks;
  if (!Array.isArray(links) || !links.length) return "";
  let out = "";
  links.forEach((l) => {
    out += `${centerLine(`${l.label || l.platform}: ${l.url || l.handle || ""}`, width)}\n`;
  });
  return out;
}

function renderReceiptText(template, order, opts = {}) {
  const t = template || {};
  const sections = getSections(t);
  const visual = getVisual(t);
  const metrics = getPaperMetrics(t.paperWidth || 32);
  const width = metrics.chars;
  const { title, lines: headerLines } = resolveBusinessInfo(t);

  let out = "";

  if (sections.header !== false) {
    if (hasLogo(t)) out += `${centerLine("[LOGO]", width)}\n`;
    if (title) out += `${centerLine(title, width)}\n`;
    headerLines.forEach((l) => {
      out += `${centerLine(l, width)}\n`;
    });
    out += divider(width, visual.dividerStyle) + "\n";
  }

  for (const line of buildBodyLines(t, order, opts)) {
    out += formatLineForText(line, width);
  }

  if (hasFbrSection(t, order)) {
    out += divider(width, visual.dividerStyle) + "\n";
    out += `${centerLine("FBR Digital Invoice", width)}\n`;
    if (order.fbrIrn) out += `${centerLine(`IRN: ${order.fbrIrn}`, width)}\n`;
    out += `${centerLine("[FBR QR]", width)}\n`;
  }

  const social = renderSocialLinks(t, width);
  if (social) {
    out += divider(width, visual.dividerStyle) + "\n";
    out += social;
  }

  const businessQrLinks = resolveBusinessQrLinks(t, order, opts.businessConfig, {
    hasFbr: hasFbrSection(t, order),
  });

  if (sections.footer !== false) {
    out += divider(width, visual.dividerStyle) + "\n";
    const footerLines = resolveFooterLines(t);

    footerLines.forEach((l) => {
      out += `${centerLine(l, width)}\n`;
    });

    if (t.branding?.thankYouMessage) {
      out += `${centerLine(t.branding.thankYouMessage, width)}\n`;
    }

    if (businessQrLinks.length) {
      out += `${centerLine("[QR ROW]", width)}\n`;
      const { charsPerCol, count } = qrRowLayout(t.paperWidth || 32, businessQrLinks.length);
      businessQrLinks.slice(0, count).forEach((link) => {
        out += `${centerLine(truncateQrLabel(link.url, charsPerCol), width)}\n`;
      });
    }

    if (shouldShowPoweredBy(t)) {
      out += divider(width, visual.dividerStyle) + "\n";
      getPoweredByLines().forEach((l) => {
        wrapCenteredLines(l, width).forEach((wl) => {
          out += `${wl}\n`;
        });
      });
    }
  }

  return out;
}

function shouldShowPoweredBy(template) {
  const branding = template?.branding || {};
  if (branding.showPoweredBy === false) return false;
  return true;
}

function wrapCenteredLines(text, width) {
  const w = Math.max(8, width || 32);
  const t = String(text || "").trim();
  if (!t) return [];
  if (t.length <= w) return [centerLine(t, w)];

  const out = [];
  let rest = t;
  while (rest.length > w) {
    let breakAt = rest.lastIndexOf(" ", w);
    if (breakAt < Math.floor(w * 0.4)) breakAt = w;
    out.push(centerLine(rest.slice(0, breakAt).trim(), w));
    rest = rest.slice(breakAt).trim();
  }
  if (rest) out.push(centerLine(rest, w));
  return out;
}

function getPoweredByLines() {
  return [
    "Powered by Siraya RMS",
    "Kiazend Technologies",
    "0325-4410555 - pos.siraya.food",
  ];
}

async function appendPoweredBy(receipt, template, width) {
  if (!shouldShowPoweredBy(template)) return receipt;
  receipt += CMD.RESET_STYLE;
  receipt += CMD.ALIGN_CENTER;
  receipt = appendDividerEscPos(receipt, width, "dash");
  for (const line of getPoweredByLines()) {
    wrapForWidth(line, width).forEach((wl) => {
      receipt += `${wl}\n`;
    });
  }
  receipt += CMD.ALIGN_LEFT;
  return receipt;
}

function appendStyledLine(receipt, line, width) {
  const raw = sanitizeForPrinter(line.text || "");
  if (!raw && line.style !== "divider") return receipt;

  if (line.style === "divider") {
    receipt += CMD.ALIGN_LEFT;
    receipt += `${sanitizeForPrinter(line.text)}\n`;
    return receipt;
  }

  const chunks = wrapForWidth(raw, width);
  for (const text of chunks) {
    receipt += CMD.RESET_STYLE;
    if (line.align === "right") receipt += CMD.ALIGN_RIGHT;
    else if (line.align === "center") receipt += CMD.ALIGN_CENTER;
    else receipt += CMD.ALIGN_LEFT;

    if (line.style === "total" || line.style === "item-header" || line.style === "banner") {
      receipt += CMD.BOLD_ON;
      receipt += `${text}\n`;
      receipt += CMD.BOLD_OFF;
    } else {
      receipt += `${text}\n`;
    }
  }

  receipt += CMD.RESET_STYLE;
  receipt += CMD.ALIGN_LEFT;
  return receipt;
}

async function renderReceipt(template, order, opts = {}) {
  const t = template || {};
  const sections = getSections(t);
  const visual = getVisual(t);
  const metrics = getPaperMetrics(t.paperWidth || 32);
  const width = metrics.chars;

  let receipt = CMD.INIT;

  if (hasLogo(t)) {
    const logoPath = resolveLogoFile(t);
    if (logoPath) {
      receipt += await buildLogoEscPos(logoPath, width, resolveLogoScale(t));
      receipt += CMD.RESET_STYLE + "\n";
    }
  }

  const { title, lines: headerLines } = resolveBusinessInfo(t);

  if (sections.header !== false) {
    receipt += CMD.ALIGN_CENTER;
    if (title) {
      receipt += CMD.BOLD_ON;
      wrapForWidth(sanitizeForPrinter(title), width).forEach((line) => {
        receipt += `${line}\n`;
      });
      receipt += CMD.BOLD_OFF;
    }
    headerLines.forEach((l) => {
      wrapForWidth(sanitizeForPrinter(l), width).forEach((wl) => {
        receipt += `${wl}\n`;
      });
    });
    receipt = appendDividerEscPos(receipt, width, visual.dividerStyle);
  }

  for (const line of buildBodyLines(t, order, opts)) {
    receipt = appendStyledLine(receipt, line, width);
  }

  if (hasFbrSection(t, order)) {
    receipt += CMD.ALIGN_CENTER;
    receipt = appendDividerEscPos(receipt, width, visual.dividerStyle);
    receipt += "FBR Digital Invoice\n";
    if (order.fbrIrn) receipt += `IRN: ${order.fbrIrn}\n`;
    receipt += await buildQrRasterEscPos(resolveFbrQr(order), width, "center");
    receipt += CMD.RESET_STYLE;
  }

  const fbrActive = hasFbrSection(t, order);
  const businessQrLinks = resolveBusinessQrLinks(t, order, opts.businessConfig, {
    hasFbr: fbrActive,
  });
  const footerLines = resolveFooterLines(t);

  if (sections.footer !== false) {
    receipt += CMD.ALIGN_CENTER;
    receipt = appendDividerEscPos(receipt, width, visual.dividerStyle);

    footerLines.forEach((l) => {
      wrapForWidth(sanitizeForPrinter(l), width).forEach((wl) => {
        receipt += CMD.ALIGN_CENTER;
        receipt += `${wl}\n`;
        receipt += CMD.ALIGN_LEFT;
      });
    });
    if (t.branding?.thankYouMessage) {
      wrapForWidth(sanitizeForPrinter(t.branding.thankYouMessage), width).forEach((wl) => {
        receipt += CMD.ALIGN_CENTER;
        receipt += `${wl}\n`;
        receipt += CMD.ALIGN_LEFT;
      });
    }
    if (businessQrLinks.length) {
      receipt += await buildQrRowRasterEscPos(businessQrLinks, width);
      receipt += CMD.RESET_STYLE;
    }
    receipt = await appendPoweredBy(receipt, t, width);
  }

  receipt += "\n\n\n";
  receipt += CMD.CUT;
  return receipt;
}

function buildReceiptPreviewModel(template, order, opts = {}) {
  const t = template || {};
  const sections = getSections(t);
  const metrics = getPaperMetrics(t.paperWidth || 32);
  const { title, lines: headerLines } = resolveBusinessInfo(t);
  const fbrActive = hasFbrSection(t, order);
  const businessQrLinks = resolveBusinessQrLinks(t, order, opts.businessConfig, {
    hasFbr: fbrActive,
  });
  const qrLayout = businessQrLinks.length
    ? qrRowLayout(t.paperWidth || 32, businessQrLinks.length)
    : null;

  return {
    chars: metrics.chars,
    paperWidth: t.paperWidth || 32,
    showLogo: hasLogo(t),
    logoScale: resolveLogoScale(t),
    header:
      sections.header !== false
        ? { title, lines: headerLines, align: "center" }
        : null,
    bodyLines: buildBodyLines(t, order, opts),
    fbr: fbrActive
      ? { irn: order.fbrIrn || null, qrUrl: resolveFbrQr(order) }
      : null,
    businessQrLinks: businessQrLinks.map((link) => ({
      url: link.url,
      label: truncateQrLabel(link.url, qrLayout?.charsPerCol || 10),
    })),
    maxBusinessQr: fbrActive ? 2 : 3,
    footerLines: sections.footer !== false ? resolveFooterLines(t) : [],
    footerAlign: t.footer?.align || "center",
    poweredBy: shouldShowPoweredBy(t) ? getPoweredByLines() : [],
  };
}

function getSampleOrder(withFbr = true) {
  const order = {
    orderNumber: "TEST-1001",
    orderType: "dine-in",
    items: [
      { name: "Chicken Broast", quantity: 2, price: 450, total: 900 },
      { name: "Fries", quantity: 1, price: 150, total: 150 },
    ],
    subtotal: 1050,
    discount: 50,
    tax: 50,
    total: 1050,
    paymentMethod: "Cash",
    cashReceived: 1500,
    change: 450,
    customerName: "Sample Customer",
    customerPhone: "03001234567",
    cashierName: "Ahmed",
  };
  if (withFbr) {
    order.fbrIrn = "FBR-SAMPLE-IRN-001";
    order.fbrQrPayload = "https://fbr.gov.pk/verify/sample";
  }
  return order;
}

module.exports = {
  renderReceipt,
  renderReceiptText,
  buildReceiptPreviewModel,
  getSampleOrder,
  isV2Template,
  resolveBusinessQrUrl,
  resolveBusinessQrLinks,
  resolveFbrQr,
  hasFbrSection,
  hasLogo,
  centerLine,
  rightLine,
  footerRowWithQr,
  getPaperMetrics,
  buildBodyLines,
  getPoweredByLines,
  shouldShowPoweredBy,
};
