const PDFDocument = require('pdfkit');
const db = require('../models');
const asyncHandler = require('../middlewares/asyncHandler');
const { sendError } = require('../utils/response');

const toNum = (value) => {
  const parsed = parseFloat(value);
  return Number.isFinite(parsed) ? parsed : 0;
};

const fmtDate = (value) => {
  if (!value) return '';
  const d = new Date(value);
  if (Number.isNaN(d.getTime())) return '';
  const dd = String(d.getDate()).padStart(2, '0');
  const mm = String(d.getMonth() + 1).padStart(2, '0');
  return `${dd}/${mm}/${d.getFullYear()}`;
};

const amountToWords = (num) => {
  const n = Math.floor(toNum(num));
  if (n === 0) return 'Zero Rupees Only';
  const ones = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten',
    'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen'];
  const tens = ['', '', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety'];

  const toWordsBelowThousand = (x) => {
    let s = '';
    const hundred = Math.floor(x / 100);
    const rem = x % 100;
    if (hundred > 0) s += `${ones[hundred]} Hundred`;
    if (rem > 0) {
      if (s) s += ' ';
      if (rem < 20) s += ones[rem];
      else {
        s += tens[Math.floor(rem / 10)];
        if (rem % 10) s += ` ${ones[rem % 10]}`;
      }
    }
    return s.trim();
  };

  let value = n;
  const parts = [];
  const crore = Math.floor(value / 10000000);
  if (crore) {
    parts.push(`${toWordsBelowThousand(crore)} Crore`);
    value %= 10000000;
  }
  const lakh = Math.floor(value / 100000);
  if (lakh) {
    parts.push(`${toWordsBelowThousand(lakh)} Lakh`);
    value %= 100000;
  }
  const thousand = Math.floor(value / 1000);
  if (thousand) {
    parts.push(`${toWordsBelowThousand(thousand)} Thousand`);
    value %= 1000;
  }
  if (value) parts.push(toWordsBelowThousand(value));
  return `${parts.join(' ').trim()} Rupees Only`;
};

exports.generatePDF = asyncHandler(async (req, res) => {
  const { id } = req.params;

  const receipt = await db.ReceiptEntry.findByPk(id, {
    include: [
      { model: db.Company, as: 'company', attributes: ['id', 'name', 'email', 'phone'] },
      { model: db.Plant, as: 'plant', attributes: ['id', 'name'] },
      {
        model: db.BusinessPartner,
        as: 'customer',
        attributes: ['id', 'bp_number', 'name', 'phone_numbers'],
        include: [
          { model: db.BPAddress, as: 'addresses', attributes: ['phone', 'mobile', 'email', 'status', 'is_default'], required: false },
          { model: db.BPGSTIN, as: 'gstins', attributes: ['gstin', 'status', 'is_default'], required: false }
        ]
      },
      { model: db.SalesInvoice, as: 'invoice', attributes: ['id', 'invoice_number', 'invoice_date', 'total_amount'] },
      {
        model: db.DeliveryChallan,
        as: 'deliveryChallan',
        attributes: ['id', 'challan_no', 'customer_po_no', 'delivery_address'],
        include: [
          {
            model: db.DeliveryChallanItem,
            as: 'items',
            attributes: ['item_code', 'item_name', 'delivered_qty'],
            include: [{ model: db.Material, as: 'material', attributes: ['material_number', 'description'] }]
          }
        ]
      }
    ]
  });

  if (!receipt) return sendError(res, 'Payment receipt not found', 404);

  const receiptItems = await db.ReceiptEntryItem.findAll({
    where: { receipt_entry_id: receipt.id },
    order: [['line_no', 'ASC']]
  });
  let paymentModeRows = [];
  try {
    paymentModeRows = await db.PaymentMode.findAll({
      where: {
        company_id: receipt.company_id,
        status: 'ACTIVE'
      },
      attributes: ['mode_code', 'mode_name'],
      order: [['mode_name', 'ASC']]
    });
  } catch (_) {
    paymentModeRows = [];
  }

  const doc = new PDFDocument({ margin: 30, size: 'A4' });
  const safeNo = String(receipt.receipt_no || receipt.id).replace(/[^\w.-]+/g, '_');
  const safeCustomer = String(receipt.customer?.name || 'Customer').replace(/[^\w.-]+/g, '_');
  const filename = `Payment_Receipt_${safeNo}_${safeCustomer}.pdf`;

  res.setHeader('Content-Type', 'application/pdf');
  res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
  doc.pipe(res);

  const pageWidth = 595.28;
  const margin = 30;
  const contentWidth = pageWidth - margin * 2;
  const drawBorder = (x, y, w, h) => doc.rect(x, y, w, h).stroke('#000000');
  const drawKeyValue = ({
    x,
    y,
    keyWidth,
    valueWidth,
    label,
    value,
    fontSize = 8,
    lineHeight = 11,
    labelAlign = 'right',
    labelBold = true
  }) => {
    doc.font(labelBold ? 'Helvetica-Bold' : 'Helvetica').fontSize(fontSize).text(label, x, y, { width: keyWidth, align: labelAlign });
    doc.font('Helvetica').fontSize(fontSize).text(':', x + keyWidth + 2, y, { width: 8, align: 'center' });
    doc.font('Helvetica').fontSize(fontSize).text(value || '', x + keyWidth + 12, y, { width: valueWidth });
    return lineHeight;
  };
  const pickPrimaryAddress = (addresses = []) => {
    const active = (addresses || []).filter((a) => String(a?.status || '').toUpperCase() !== 'INACTIVE');
    return active.find((a) => !!a?.is_default) || active[0] || addresses[0] || null;
  };
  const pickPrimaryGSTIN = (gstins = []) => {
    const active = (gstins || []).filter((g) => String(g?.status || '').toUpperCase() !== 'INACTIVE');
    return active.find((g) => !!g?.is_default) || active[0] || gstins[0] || null;
  };

  let y = margin;

  drawBorder(margin, y, contentWidth, 20);
  doc.font('Helvetica-Bold').fontSize(12).text('CUSTOMER PAYMENT RECEIPT', margin, y + 6, { width: contentWidth, align: 'center' });
  y += 20;

  const leftW = contentWidth * 0.56;
  const rightW = contentWidth - leftW;
  const primaryAddress = pickPrimaryAddress(receipt.customer?.addresses || []);
  const primaryGSTIN = pickPrimaryGSTIN(receipt.customer?.gstins || []);
  const customerContact = primaryAddress?.mobile || primaryAddress?.phone || receipt.customer?.phone_numbers || '';
  const customerEmail = primaryAddress?.email || '';
  const customerGSTIN = primaryGSTIN?.gstin || '';
  const leftRows = [
    ['Customer', receipt.customer?.name || ''],
    ['Customer PO', receipt.deliveryChallan?.customer_po_no || ''],
    ['Delivery Address', receipt.deliveryChallan?.delivery_address || ''],
    ['Contact', customerContact],
    ['Customer Email', customerEmail],
    ['Customer GSTIN', customerGSTIN]
  ];
  const rightRows = [
    ['Receipt No', receipt.receipt_no || ''],
    ['Receipt Date', fmtDate(receipt.receipt_date)],
    ['Invoice No', receipt.invoice?.invoice_number || ''],
    ['Invoice Date', fmtDate(receipt.invoice?.invoice_date)],
    ['Delivery Challan No', receipt.deliveryChallan?.challan_no || '']
  ];

  const detailLineH = 10;
  const detailH = Math.max(leftRows.length, rightRows.length) * detailLineH + 10;
  drawBorder(margin, y, contentWidth, detailH);
  doc.moveTo(margin + leftW, y).lineTo(margin + leftW, y + detailH).stroke();

  let ly = y + 5;
  leftRows.forEach(([k, v]) => {
    ly += drawKeyValue({
      x: margin + 6,
      y: ly,
      keyWidth: 96,
      valueWidth: leftW - 118,
      label: k,
      value: v,
      fontSize: 8,
      lineHeight: detailLineH,
      labelAlign: 'left',
      labelBold: true
    });
  });

  let ry = y + 5;
  rightRows.forEach(([k, v]) => {
    ry += drawKeyValue({
      x: margin + leftW + 5,
      y: ry,
      keyWidth: 96,
      valueWidth: rightW - 110,
      label: k,
      value: v,
      fontSize: 8,
      lineHeight: detailLineH,
      labelAlign: 'left',
      labelBold: true
    });
  });

  y += detailH;

  drawBorder(margin, y, contentWidth, 26);
  doc.font('Helvetica-Bold').fontSize(8).text('Invoice Amount', margin + 6, y + 5);
  doc.font('Helvetica').fontSize(8).text(toNum(receipt.invoice_total).toFixed(2), margin + 90, y + 5, { width: 70, align: 'right' });
  doc.font('Helvetica-Bold').fontSize(8).text('Paid Amount', margin + 190, y + 5);
  doc.font('Helvetica').fontSize(8).text(toNum(receipt.paid_amount).toFixed(2), margin + 255, y + 5, { width: 70, align: 'right' });
  doc.font('Helvetica-Bold').fontSize(8).text('Balance Amount', margin + 360, y + 5);
  doc.font('Helvetica').fontSize(8).text(toNum(receipt.balance_amount).toFixed(2), margin + 450, y + 5, { width: 80, align: 'right' });
  y += 26;

  const challanItems = receipt.deliveryChallan?.items || [];
  if (challanItems.length) {
    const itemCols = [
      { label: 'No', w: 26 },
      { label: 'Item', w: contentWidth - 106 },
      { label: 'Qty', w: 80 }
    ];
    const itemHeaderH = 20;
    drawBorder(margin, y, contentWidth, itemHeaderH);
    let ix = margin;
    itemCols.forEach((c, idx) => {
      if (idx > 0) doc.moveTo(ix, y).lineTo(ix, y + itemHeaderH).stroke();
      doc.font('Helvetica-Bold').fontSize(7).text(c.label, ix + 2, y + 6, { width: c.w - 4, align: 'center' });
      ix += c.w;
    });
    y += itemHeaderH;

    challanItems.slice(0, 5).forEach((it, idx) => {
      const rowH = 18;
      drawBorder(margin, y, contentWidth, rowH);
      const itemText = `${it.item_code || it.material?.material_number || ''} ${it.item_name || it.material?.description || ''}`.trim();
      const vals = [String(idx + 1), itemText, toNum(it.delivered_qty).toFixed(3)];
      let rx = margin;
      itemCols.forEach((c, cIdx) => {
        if (cIdx > 0) doc.moveTo(rx, y).lineTo(rx, y + rowH).stroke();
        doc.font('Helvetica').fontSize(7).text(vals[cIdx], rx + 2, y + 5, { width: c.w - 4, align: cIdx === 0 ? 'right' : (cIdx === 2 ? 'right' : 'left') });
        rx += c.w;
      });
      y += rowH;
    });
  }

  const items = Array.isArray(receiptItems) && receiptItems.length > 0
    ? receiptItems
    : [{
      line_no: 1,
      payment_mode: receipt.payment_mode,
      amount: receipt.paid_amount,
      bank_name: receipt.bank_name,
      ifsc_code: receipt.ifsc_code,
      reference_no: receipt.reference_no,
      remarks: receipt.remarks
    }];
  const fallbackModes = ['BANK', 'UPI', 'CASH', 'CHEQUE', 'NEFT'];
  const configuredModes = (paymentModeRows || [])
    .map((pm) => String(pm.mode_code || '').toUpperCase())
    .filter(Boolean);
  const allModes = Array.from(new Set([...(configuredModes.length ? configuredModes : fallbackModes), ...fallbackModes]));
  const amountByMode = new Map();
  items.forEach((it) => {
    const mode = String(it.payment_mode || '').toUpperCase();
    amountByMode.set(mode, toNum(amountByMode.get(mode)) + toNum(it.amount));
  });
  const paymentLines = allModes.map((mode) => `${mode} : ${toNum(amountByMode.get(mode)).toFixed(2)}`);
  const paymentInfoH = Math.max(40, 20 + (paymentLines.length * 10));
  const paymentLeftW = contentWidth / 2;
  const paymentRightW = contentWidth - paymentLeftW;
  drawBorder(margin, y, contentWidth, paymentInfoH);
  doc.moveTo(margin + paymentLeftW, y).lineTo(margin + paymentLeftW, y + paymentInfoH).stroke();
  doc.font('Helvetica-Bold').fontSize(8).text('Payment Modes', margin + 5, y + 8, { width: 88, align: 'left' });
  doc.font('Helvetica').fontSize(8).text(':', margin + 92, y + 8, { width: 8, align: 'center' });
  paymentLines.forEach((line, idx) => {
    doc.font('Helvetica').fontSize(8).text(line, margin + 105, y + 18 + (idx * 10), { width: paymentLeftW - 110, align: 'left' });
  });
  doc.font('Helvetica-Bold').fontSize(8).text('Amount in Words', margin + paymentLeftW + 5, y + 13, { width: 95, align: 'right' });
  doc.font('Helvetica').fontSize(8).text(`: ${amountToWords(receipt.paid_amount)}`, margin + paymentLeftW + 104, y + 13, { width: paymentRightW - 109 });
  y += paymentInfoH;

  const sigH = 44;
  const sigW = contentWidth / 2;
  drawBorder(margin, y, sigW, sigH);
  drawBorder(margin + sigW, y, sigW, sigH);
  doc.font('Helvetica-Bold').fontSize(8).text('Authorized Signature', margin + 8, y + sigH - 14, { width: sigW - 16, align: 'left' });
  doc.font('Helvetica-Bold').fontSize(8).text('Customer Signature', margin + sigW + 8, y + sigH - 14, { width: sigW - 16, align: 'left' });

  doc.end();
});

