"""
thermal_receipt.py
==================
Drop-in replacement for _generate_thermal_receipt_pdf().
Uses fpdf2 only — no WeasyPrint, no heavy deps.

Design decisions
----------------
- 80mm thermal width → FPDF page width 80mm, margins 2mm each side → 76mm usable
- Courier font for all numeric/tabular content (monospace = aligned columns)
- Helvetica for headings, labels, branding (proportional = compact)
- Column layout: ITEM 38mm | QTY 12mm | PRICE 26mm = 76mm total
- Dashed dividers via set_dash_pattern() — cleaner than '.' * N hack
- Branded top stripe via filled rect in forest green (#417A52)
- PAID badge via small filled rect next to receipt number
- Serial numbers as indented sub-line under item name
- QR code bottom-right, footer text bottom-left
- All amounts right-aligned in Courier for clean decimal alignment
"""

from fpdf import FPDF
from datetime import datetime
import tempfile
import os


# ── Constants ────────────────────────────────────────────────────────────────

PAGE_W        = 80          # mm — standard thermal roll width
MARGIN        = 2           # mm — left/right margin
USABLE_W      = PAGE_W - (MARGIN * 2)   # 76mm

# Column widths (must sum to USABLE_W)
COL_ITEM      = 38
COL_QTY       = 12
COL_PRICE     = 26

# Brand colours (RGB)
FOREST        = (65, 122, 82)    # #417A52
FOREST_DARK   = (44, 87, 57)     # #2C5739
WHITE         = (255, 255, 255)
BLACK         = (26, 26, 26)
GREY_DARK     = (80, 80, 80)
GREY_MID      = (120, 120, 120)
GREY_LIGHT    = (180, 180, 180)


# ── Helper: thin FPDF subclass ────────────────────────────────────────────────

class ThermalPDF(FPDF):
    """80mm thermal receipt canvas."""

    def __init__(self):
        super().__init__(unit='mm', format=(PAGE_W, 400))   # tall enough; trimmed by content
        self.set_margins(MARGIN, 0, MARGIN)
        self.set_auto_page_break(auto=False)
        self.add_page()

    # ── Low-level drawing helpers ─────────────────────────────────────────────

    def solid_line(self, lw=0.25):
        self.set_line_width(lw)
        self.set_draw_color(*GREY_DARK)
        self.line(MARGIN, self.get_y(), PAGE_W - MARGIN, self.get_y())

    def dashed_line(self, lw=0.2):
        self.set_font('Courier', '', 6)
        self.set_text_color(*GREY_LIGHT)
        self.cell(0, 2, '-' * 60, ln=True, align='C')
        self.set_text_color(*BLACK)

    def vspace(self, h=2):
        self.ln(h)

    # ── Typography helpers ────────────────────────────────────────────────────

    def h(self, font='Helvetica', style='', size=8):
        """Set font shorthand."""
        self.set_font(font, style, size)

    def text_color(self, rgb):
        self.set_text_color(*rgb)

    # ── Branded top stripe ────────────────────────────────────────────────────

    def stripe(self):
        """3mm forest-green bar across the top."""
        self.set_fill_color(*FOREST)
        self.rect(0, 0, PAGE_W, 3, style='F')
        self.set_y(3)
        self.vspace(2)

    # ── Section label (spaced caps, grey) ────────────────────────────────────

    def section_label(self, text):
        self.vspace(2)
        self.set_font('Helvetica', '', 7)
        self.set_text_color(*GREY_MID)
        self.cell(0, 3, text.upper(), ln=True, align='C')
        self.set_text_color(*BLACK)
        self.vspace(1)

    # ── Small inline badge (e.g. PAID) ───────────────────────────────────────

    def inline_badge(self, text, x, y, rgb_fill=FOREST, rgb_text=WHITE):
        """Draw a small filled badge. Returns width used."""
        self.set_font('Helvetica', 'B', 6)
        w = self.get_string_width(text) + 3
        self.set_fill_color(*rgb_fill)
        self.set_text_color(*rgb_text)
        self.rect(x, y + 0.8, w, 3, style='F')
        self.set_xy(x, y + 0.5)
        self.cell(w, 3, text, align='C')
        self.set_text_color(*BLACK)
        return w


# ── Main receipt function ─────────────────────────────────────────────────────

def _generate_thermal_receipt_pdf(sale):
    """
    Generate a professional 80mm thermal receipt PDF.

    Args
    ----
    sale : Sale model instance (Django ORM object)

    Returns
    -------
    bytes — raw PDF bytes ready to serve or write to disk.
    """

    pdf = ThermalPDF()

    # ── STRIPE ────────────────────────────────────────────────────────────────
    pdf.stripe()

    # ── BUSINESS HEADER ───────────────────────────────────────────────────────
    pdf.set_font('Helvetica', 'B', 13)
    pdf.set_text_color(*BLACK)
    pdf.cell(0, 5, sale.business.name.upper(), ln=True, align='C')

    pdf.set_font('Helvetica', '', 7.5)
    pdf.set_text_color(*GREY_DARK)

    biz = sale.business
    if getattr(biz, 'email', None):
        pdf.cell(0, 3, biz.email, ln=True, align='C')
    if getattr(biz, 'address_line1', None):
        for line in biz.address_line1.split('\n')[:2]:
            pdf.cell(0, 3, line.strip(), ln=True, align='C')
    if getattr(biz, 'phone_number', None):
        pdf.cell(0, 3, f"Tel: {biz.phone_number}", ln=True, align='C')
    if getattr(biz, 'tax_id', None):
        pdf.cell(0, 3, f"PIN: {biz.tax_id}", ln=True, align='C')

    pdf.set_text_color(*BLACK)
    pdf.vspace(2)
    pdf.solid_line()
    pdf.vspace(2)

    # ── RECEIPT META ──────────────────────────────────────────────────────────
    pdf.set_font('Helvetica', '', 8)

    # Row 1: date | SALES RECEIPT label
    pdf.cell(40, 3.5, sale.sale_date.strftime('%d/%m/%Y'), align='L')
    pdf.cell(36, 3.5, 'SALES RECEIPT', align='R', ln=True)

    # Row 2: time | receipt number + PAID badge
    pdf.cell(40, 3.5, sale.sale_date.strftime('%I:%M %p').upper(), align='L')
    receipt_no = f"#{sale.sale_number}"
    pdf.set_font('Helvetica', 'B', 8)
    pdf.cell(36, 3.5, receipt_no, align='R')
    pdf.ln(0)

    # PAID badge — positioned after receipt number
    badge_x = PAGE_W - MARGIN - 1   # start near right edge; badge draws leftward
    badge_text = ' PAID '
    badge_w = pdf.get_string_width(badge_text) + 3
    # pdf.inline_badge("", PAGE_W - MARGIN - badge_w - 1, pdf.get_y() - 3.5)
    pdf.ln(3.5)

    pdf.vspace(2)

    # Customer info
    if sale.customer:
        pdf.set_font('Helvetica', 'B', 8)
        pdf.set_text_color(*BLACK)
        name_line = sale.customer.name.upper()
        if getattr(sale.customer, 'phone', None):
            name_line += f"  .  {sale.customer.phone}"
        pdf.cell(0, 3.5, name_line, ln=True)
        
    pdf.vspace(2)
    # Served by
    if getattr(sale, 'salesperson', None):
        pdf.set_font('Helvetica', '', 7.5)
        pdf.set_text_color(*GREY_DARK)
        pdf.cell(0, 3, f"Served by: {sale.salesperson.get_full_name()}", ln=True, align='R')
        pdf.set_text_color(*BLACK)

    pdf.vspace(2)
    pdf.dashed_line()
    pdf.vspace(2)

    # ── ITEMS TABLE HEADER ────────────────────────────────────────────────────
    pdf.set_font('Helvetica', 'B', 7.5)
    pdf.set_text_color(*GREY_DARK)
    pdf.cell(COL_ITEM,  3, 'ITEM',   align='L')
    pdf.cell(COL_QTY,   3, 'QTY',   align='C')
    pdf.cell(COL_PRICE, 3, 'AMOUNT', align='R', ln=True)
    pdf.set_text_color(*BLACK)
    pdf.vspace(1)
    pdf.dashed_line()
    pdf.vspace(2)

    # ── ITEMS ─────────────────────────────────────────────────────────────────
    for sale_item in sale.items.all():
        actual = None
        if sale_item.item_type == 'inventory_item' and sale_item.inventory_item:
            actual = sale_item.inventory_item
        elif sale_item.item_type == 'accessory_item' and sale_item.accessory_item:
            actual = sale_item.accessory_item

        item_name = sale_item.item_name.upper()
        serial    = (
            actual.serial_number
            if actual and getattr(actual, 'serial_number', None)
            else None
        )

        # Word-wrap item name into COL_ITEM width
        # fpdf2's multi_cell would shift the cursor — handle manually
        words        = item_name.split()
        lines        = []
        current_line = ''
        pdf.set_font('Helvetica', 'B', 8)

        for word in words:
            test = (current_line + ' ' + word).strip()
            if pdf.get_string_width(test) <= COL_ITEM - 0.5:
                current_line = test
            else:
                if current_line:
                    lines.append(current_line)
                current_line = word
        if current_line:
            lines.append(current_line)

        # First line: name + qty + price
        row_y = pdf.get_y()
        pdf.set_font('Helvetica', 'B', 8)
        pdf.cell(COL_ITEM,  3.5, lines[0], align='L')
        pdf.set_font('Courier', '', 8)
        pdf.cell(COL_QTY,   3.5, str(sale_item.quantity), align='C')
        pdf.cell(COL_PRICE, 3.5, f"KES {float(sale_item.unit_price):,.2f}", align='R', ln=True)

        # Overflow name lines (rare, but handled)
        if len(lines) > 1:
            pdf.set_font('Helvetica', '', 7.5)
            pdf.set_text_color(*GREY_DARK)
            for extra in lines[1:]:
                pdf.cell(COL_ITEM, 3, extra, align='L', ln=True)
            pdf.set_text_color(*BLACK)

        # Serial number sub-line
        if serial:
            pdf.set_font('Helvetica', '', 7)
            pdf.set_text_color(*GREY_MID)
            pdf.cell(0, 2.8, f"  S/N: {serial}", align='L', ln=True)
            pdf.set_text_color(*BLACK)

        pdf.vspace(1)

    # ── TOTALS ────────────────────────────────────────────────────────────────
    pdf.vspace(1)
    pdf.dashed_line()
    pdf.vspace(2)

    def total_row(label, value, bold=False, large=False):
        size  = 10 if large else 8.5
        style = 'B' if bold else ''
        pdf.set_font('Helvetica', style, size)
        pdf.cell(COL_ITEM + COL_QTY, size * 0.45 + 1.5, label, align='R')
        pdf.set_font('Courier', style, size)
        pdf.cell(COL_PRICE, size * 0.45 + 1.5, value, align='R', ln=True)

    total_row('Subtotal', f"KES {float(sale.subtotal):,.2f}")

    if sale.discount_amount > 0:
        total_row('Discount', f"-KES {float(sale.discount_amount):,.2f}")

    if sale.tax_amount > 0:
        total_row('VAT (16%)', f"KES {float(sale.tax_amount):,.2f}")

    pdf.vspace(1)

    # thin solid line before grand total
    # pdf.set_dash_pattern(dash=0, gap=0)
    pdf.set_line_width(0.2)
    pdf.set_draw_color(*GREY_DARK)
    # right-aligned short line (totals column only)
    line_x = MARGIN + COL_ITEM + COL_QTY
    pdf.line(line_x, pdf.get_y(), PAGE_W - MARGIN, pdf.get_y())

    pdf.vspace(1)
    total_row('TOTAL    ', f"KES {float(sale.total_amount):,.2f}", bold=True, large=True)

    pdf.vspace(2)
    pdf.solid_line()
    pdf.vspace(3)

    # ── PAYMENT DETAILS ───────────────────────────────────────────────────────
    if sale.payments.exists():
        for payment in sale.payments.all():
            method = payment.payment_mode.get_name_display().upper()
            pdf.set_font('Helvetica', '', 8.5)
            pdf.cell(COL_ITEM + COL_QTY, 3.5, method, align='R')
            pdf.set_font('Courier', '', 8.5)
            pdf.cell(COL_PRICE, 3.5, f"KES {float(payment.amount):,.2f}", align='R', ln=True)

            if payment.reference_number:
                pdf.set_font('Helvetica', '', 7.5)
                pdf.set_text_color(*GREY_MID)
                pdf.cell(0, 3, f"Ref: {payment.reference_number}", align='R', ln=True)
                pdf.set_text_color(*BLACK)

        total_paid = sum(float(p.amount) for p in sale.payments.all())
        change     = total_paid - float(sale.total_amount)
        if change > 0.005:
            pdf.vspace(1)
            pdf.set_font('Helvetica', 'B', 8.5)
            pdf.cell(COL_ITEM + COL_QTY, 3.5, 'CHANGE', align='R')
            pdf.set_font('Courier', 'B', 8.5)
            pdf.cell(COL_PRICE, 3.5, f"KES {change:,.2f}", align='R', ln=True)

        pdf.vspace(2)

    pdf.dashed_line()
    pdf.vspace(2)

    # ── WARRANTY NOTES ────────────────────────────────────────────────────────
    pdf.set_font('Helvetica', 'B', 7)
    pdf.set_text_color(*GREY_DARK)
    pdf.cell(0, 3, 'WARRANTY & POLICY', ln=True)
    pdf.vspace(1)

    pdf.set_font('Helvetica', '', 7)
    notes = [
        "I.   New equipment - 12 months warranty",
        "II.  Refurbished - 6 months warranty",
        "III. Adapters, RAM, storage & software excluded",
        "IV.  Exchanges for defective items within warranty only",
    ]
    for note in notes:
        pdf.cell(0, 3, note, ln=True)

    pdf.set_text_color(*BLACK)
    pdf.vspace(2)
    pdf.solid_line()
    pdf.vspace(3)

    # ── FOOTER: branding left | QR right ─────────────────────────────────────
    footer_y  = pdf.get_y()
    qr_size   = 16      # mm
    text_w    = USABLE_W - qr_size - 2
    qr_path   = None

    # Generate QR
    try:
        import qrcode

        qr = qrcode.QRCode(
            version=1,
            error_correction=qrcode.constants.ERROR_CORRECT_M,
            box_size=4,
            border=1,
        )
        qr.add_data('https://www.trato.binbyte.co.ke')
        qr.make(fit=True)
        qr_img = qr.make_image(fill_color='black', back_color='white')

        with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmp:
            qr_img.save(tmp.name, format='PNG')
            qr_path = tmp.name
    except Exception:
        pass

    # Branding text (left side)
    pdf.set_font('Helvetica', 'B', 8.5)
    pdf.set_text_color(*BLACK)
    pdf.cell(text_w, 3.5, sale.business.name.upper(), ln=True)

    pdf.set_font('Helvetica', 'B', 7.5)
    pdf.set_text_color(*FOREST)
    pdf.cell(text_w, 3, 'Powered by Trato · BinByte', ln=True)

    pdf.set_font('Helvetica', '', 7)
    pdf.set_text_color(*GREY_MID)
    pdf.cell(text_w, 2.8, 'trato.binbyte.co.ke', ln=True)
    pdf.cell(text_w, 2.8, '+254 798 969 800', ln=True)
    pdf.set_text_color(*BLACK)

    # QR code (right side, anchored to footer_y)
    if qr_path:
        qr_x = PAGE_W - MARGIN - qr_size
        try:
            pdf.image(qr_path, x=qr_x, y=footer_y, w=qr_size, h=qr_size)
        except Exception:
            pass
        finally:
            try:
                os.unlink(qr_path)
            except Exception:
                pass

    # Advance past the taller of text block or QR
    footer_end_y = max(pdf.get_y(), footer_y + qr_size)
    pdf.set_y(footer_end_y)
    pdf.vspace(3)

    # ── THANK YOU ─────────────────────────────────────────────────────────────
    pdf.solid_line(lw=0.3)
    pdf.vspace(3)

    pdf.set_font('Helvetica', 'B', 9)
    pdf.set_text_color(*BLACK)
    pdf.cell(0, 4, 'THANK YOU FOR YOUR PURCHASE!', ln=True, align='C')

    pdf.vspace(1)
    pdf.set_font('Helvetica', '', 7)
    pdf.set_text_color(*GREY_MID)
    pdf.cell(0, 3, f"Printed: {datetime.now().strftime('%d/%m/%Y %I:%M %p')}", ln=True, align='C')
    pdf.set_text_color(*BLACK)

    pdf.vspace(3)

    # ── TEAR-OFF DASHES ───────────────────────────────────────────────────────
    pdf.set_font('Helvetica', '', 7)
    pdf.set_text_color(*GREY_LIGHT)
    pdf.cell(0, 3, '- ' * 19, ln=True, align='C')
    pdf.set_text_color(*BLACK)

    pdf.vspace(2)

    # ── TRIM PAGE TO CONTENT ──────────────────────────────────────────────────
    # Resize the page height to match actual content
    # content_h = pdf.get_y() + 2
    # pdf.pages[1]['h'] = content_h

    # ── OUTPUT ────────────────────────────────────────────────────────────────
    # result = pdf.output(dest='S')
    # if isinstance(result, (bytes, bytearray)):
    #     return bytes(result)
    # return result.encode('latin-1')
    
    pdf_output = pdf.output(dest='S')
    if isinstance(pdf_output, (bytes, bytearray)):
        return bytes(pdf_output)
    else:
        return pdf_output.encode('latin-1')    
    