#!/usr/bin/env python3
"""
BlueGreenPlanet PDF Generator
Usage: python pdf_generator.py <type> <json_data>
  type: 'invoice' or 'receipt'
  json_data: JSON string with document data
Outputs PDF bytes to stdout.
"""
import sys, json, io
from datetime import datetime
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_RIGHT, TA_CENTER, TA_LEFT
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table,
                                 TableStyle, HRFlowable)
from reportlab.pdfgen import canvas as pdfcanvas

# ── Brand colours ─────────────────────────────────────────────────
BGP_GREEN  = colors.HexColor('#2d6a4f')
BGP_LIGHT  = colors.HexColor('#d8f3dc')
GRAY_800   = colors.HexColor('#1f2937')
GRAY_500   = colors.HexColor('#6b7280')
GRAY_200   = colors.HexColor('#e5e7eb')
WHITE      = colors.white

W, H = A4
MARGIN = 20 * mm

def fmt_currency(v):
    try:    return f'${float(v):,.2f}'
    except: return str(v)

def fmt_date(s):
    if not s: return '—'
    for fmt in ('%Y-%m-%dT%H:%M:%S.%f', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%d'):
        try:
            return datetime.strptime(str(s)[:19], fmt[:len(str(s)[:19])]).strftime('%-d %B %Y')
        except: pass
    return str(s)[:10]

def status_colour(st):
    return {
        'paid':    colors.HexColor('#16a34a'),
        'partial': colors.HexColor('#d97706'),
        'pending': colors.HexColor('#dc2626'),
        'void':    GRAY_500,
        'completed': colors.HexColor('#16a34a'),
        'refunded':  colors.HexColor('#7c3aed'),
        'failed':    colors.HexColor('#dc2626'),
    }.get((st or '').lower(), GRAY_500)

# ── Number-on-every-page canvas ───────────────────────────────────
class NumberedCanvas(pdfcanvas.Canvas):
    def __init__(self, *a, **kw):
        super().__init__(*a, **kw)
        self._saved = []
    def showPage(self):
        self._saved.append(dict(self.__dict__))
        self._startPage()
    def save(self):
        n = len(self._saved)
        for i, page in enumerate(self._saved):
            self.__dict__.update(page)
            self.setFont('Helvetica', 8)
            self.setFillColor(GRAY_500)
            self.drawRightString(W - MARGIN, 12*mm, f'Page {i+1} of {n}')
            super().showPage()
        super().save()

def build_header(story, styles, title, subtitle=None):
    """Common branded header block."""
    story.append(Table(
        [[
            Paragraph('<font color="#2d6a4f"><b>BlueGreenPlanet</b></font>',
                      ParagraphStyle('brand', fontSize=20, leading=24)),
            Paragraph(f'<b>{title}</b>',
                      ParagraphStyle('doctitle', fontSize=18, leading=22, alignment=TA_RIGHT,
                                     textColor=GRAY_800)),
        ]],
        colWidths=[W - 2*MARGIN - 80*mm, 80*mm],
        style=TableStyle([
            ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
            ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ])
    ))
    if subtitle:
        story.append(Paragraph(subtitle,
            ParagraphStyle('sub', fontSize=9, textColor=GRAY_500, alignment=TA_RIGHT)))
    story.append(HRFlowable(width='100%', thickness=2, color=BGP_GREEN, spaceAfter=8))

def build_invoice(data):
    buf = io.BytesIO()
    doc = SimpleDocTemplate(buf, pagesize=A4,
                            leftMargin=MARGIN, rightMargin=MARGIN,
                            topMargin=MARGIN, bottomMargin=MARGIN)
    styles = getSampleStyleSheet()
    story  = []

    inv    = data.get('invoice', {})
    user   = data.get('user',    {})
    payments = data.get('payments', [])

    build_header(story, styles, 'INVOICE',
                 f'Invoice #{inv.get("InvoiceNumber", "")}')

    # ── Bill To / Invoice Details two-col ──────────────────────────
    name   = f'{user.get("FirstName","")} {user.get("LastName","")}'.strip() or user.get("Username","")
    details_left = f'''<b>Bill To</b><br/>
{name}<br/>
{user.get("Email","")}<br/>
{user.get("Company","") or ""}'''

    due  = fmt_date(inv.get('DueDate'))
    stat = (inv.get('Status') or 'pending').upper()
    sc   = status_colour(inv.get('Status', 'pending'))
    details_right = f'''<b>Invoice Date:</b> {fmt_date(inv.get("CreatedAt"))}<br/>
<b>Due Date:</b> {due}<br/>
<b>Status:</b> <font color="{sc.hexval()}"><b>{stat}</b></font>'''

    story.append(Table(
        [[
            Paragraph(details_left,  ParagraphStyle('dl', fontSize=9, leading=14)),
            Paragraph(details_right, ParagraphStyle('dr', fontSize=9, leading=14, alignment=TA_RIGHT)),
        ]],
        colWidths=[(W-2*MARGIN)*0.5]*2,
        style=TableStyle([('VALIGN',(0,0),(-1,-1),'TOP'), ('TOPPADDING',(0,0),(-1,-1),4)])
    ))
    story.append(Spacer(1, 6*mm))

    # ── Line items table ───────────────────────────────────────────
    tbl_data = [
        [Paragraph('<b>Description</b>', ParagraphStyle('th', fontSize=9, textColor=WHITE)),
         Paragraph('<b>Amount</b>',      ParagraphStyle('th', fontSize=9, textColor=WHITE, alignment=TA_RIGHT))],
        [Paragraph(inv.get('Description') or '—', ParagraphStyle('td', fontSize=9, leading=13)),
         Paragraph(fmt_currency(inv.get('Amount',0)), ParagraphStyle('td', fontSize=9, alignment=TA_RIGHT))],
        ['', ''],
        [Paragraph('<b>Total Due</b>', ParagraphStyle('tot', fontSize=10, textColor=GRAY_800)),
         Paragraph(f'<b>{fmt_currency(inv.get("Amount",0))}</b>',
                   ParagraphStyle('tot', fontSize=10, alignment=TA_RIGHT, textColor=GRAY_800))],
    ]
    col_w = [W - 2*MARGIN - 50*mm, 50*mm]
    tbl = Table(tbl_data, colWidths=col_w)
    tbl.setStyle(TableStyle([
        ('BACKGROUND',   (0,0), (-1,0),  BGP_GREEN),
        ('TEXTCOLOR',    (0,0), (-1,0),  WHITE),
        ('FONTSIZE',     (0,0), (-1,-1), 9),
        ('ROWBACKGROUNDS', (0,1), (-1,-2), [WHITE, BGP_LIGHT]),
        ('LINEBELOW',    (0,-1), (-1,-1), 1, BGP_GREEN),
        ('LINEABOVE',    (0,-1), (-1,-1), 1, GRAY_200),
        ('TOPPADDING',   (0,0), (-1,-1), 5),
        ('BOTTOMPADDING',(0,0), (-1,-1), 5),
        ('LEFTPADDING',  (0,0), (-1,-1), 8),
        ('RIGHTPADDING', (0,0), (-1,-1), 8),
        ('VALIGN',       (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story.append(tbl)

    # ── Payments received ──────────────────────────────────────────
    if payments:
        story.append(Spacer(1, 8*mm))
        story.append(Paragraph('<b>Payments Received</b>',
                               ParagraphStyle('ph', fontSize=10, textColor=BGP_GREEN)))
        story.append(Spacer(1, 2*mm))
        ph = [['Date', 'Reference', 'Method', 'Amount']]
        total_paid = 0
        for p in payments:
            ph.append([
                fmt_date(p.get('CreatedAt')),
                p.get('Reference',''),
                p.get('PaymentMethodSnapshot',''),
                fmt_currency(p.get('Amount',0)),
            ])
            total_paid += float(p.get('Amount',0) or 0)
        ph.append(['', '', Paragraph('<b>Total Paid</b>', ParagraphStyle('tp', fontSize=9)),
                   Paragraph(f'<b>{fmt_currency(total_paid)}</b>',
                              ParagraphStyle('tp', fontSize=9))])
        ptbl = Table(ph, colWidths=[30*mm, 45*mm, 60*mm, None])
        ptbl.setStyle(TableStyle([
            ('BACKGROUND',   (0,0), (-1,0),  BGP_GREEN),
            ('TEXTCOLOR',    (0,0), (-1,0),  WHITE),
            ('FONTSIZE',     (0,0), (-1,-1), 8),
            ('ROWBACKGROUNDS',(0,1),(-1,-2), [WHITE, BGP_LIGHT]),
            ('TOPPADDING',   (0,0), (-1,-1), 4),
            ('BOTTOMPADDING',(0,0), (-1,-1), 4),
            ('LEFTPADDING',  (0,0), (-1,-1), 6),
            ('RIGHTPADDING', (0,0), (-1,-1), 6),
            ('ALIGN',        (3,0), (3,-1),  'RIGHT'),
        ]))
        story.append(ptbl)

    # ── Notes ──────────────────────────────────────────────────────
    if inv.get('Notes'):
        story.append(Spacer(1, 6*mm))
        story.append(Paragraph('<b>Notes</b>', ParagraphStyle('nh', fontSize=9, textColor=GRAY_500)))
        story.append(Paragraph(inv['Notes'], ParagraphStyle('nb', fontSize=9, leading=13)))

    # ── Footer ─────────────────────────────────────────────────────
    story.append(Spacer(1, 10*mm))
    story.append(HRFlowable(width='100%', thickness=0.5, color=GRAY_200))
    story.append(Paragraph('Thank you for your business. BlueGreenPlanet · admin@bluegreenplanet.com',
                            ParagraphStyle('footer', fontSize=8, textColor=GRAY_500, alignment=TA_CENTER)))

    doc.build(story, canvasmaker=NumberedCanvas)
    return buf.getvalue()


def build_receipt(data):
    buf = io.BytesIO()
    doc = SimpleDocTemplate(buf, pagesize=A4,
                            leftMargin=MARGIN, rightMargin=MARGIN,
                            topMargin=MARGIN, bottomMargin=MARGIN)
    styles = getSampleStyleSheet()
    story  = []

    pay  = data.get('payment', {})
    user = data.get('user',    {})
    inv  = data.get('invoice', {})

    build_header(story, styles, 'RECEIPT',
                 f'Reference: {pay.get("Reference","")}')

    # ── Paid By / Details ─────────────────────────────────────────
    name = f'{user.get("FirstName","")} {user.get("LastName","")}'.strip() or user.get("Username","")
    stat = (pay.get('Status') or 'completed').upper()
    sc   = status_colour(pay.get('Status','completed'))

    story.append(Table(
        [[
            Paragraph(f'<b>Received From</b><br/>{name}<br/>{user.get("Email","")}',
                      ParagraphStyle('dl', fontSize=9, leading=14)),
            Paragraph(
                f'<b>Date:</b> {fmt_date(pay.get("CreatedAt"))}<br/>'
                f'<b>Status:</b> <font color="{sc.hexval()}"><b>{stat}</b></font>',
                ParagraphStyle('dr', fontSize=9, leading=14, alignment=TA_RIGHT)),
        ]],
        colWidths=[(W-2*MARGIN)*0.5]*2,
        style=TableStyle([('VALIGN',(0,0),(-1,-1),'TOP'), ('TOPPADDING',(0,0),(-1,-1),4)])
    ))
    story.append(Spacer(1, 6*mm))

    # ── Amount box ────────────────────────────────────────────────
    story.append(Table(
        [[Paragraph(f'<b>Amount Paid</b>',
                    ParagraphStyle('al', fontSize=12, textColor=WHITE)),
          Paragraph(f'<b>{fmt_currency(pay.get("Amount",0))}</b>',
                    ParagraphStyle('ar', fontSize=18, textColor=WHITE, alignment=TA_RIGHT))]],
        colWidths=[(W-2*MARGIN)*0.5]*2,
        style=TableStyle([
            ('BACKGROUND', (0,0), (-1,-1), BGP_GREEN),
            ('LEFTPADDING', (0,0), (-1,-1), 10),
            ('RIGHTPADDING',(0,0), (-1,-1), 10),
            ('TOPPADDING',  (0,0), (-1,-1), 10),
            ('BOTTOMPADDING',(0,0),(-1,-1),10),
            ('VALIGN',      (0,0), (-1,-1), 'MIDDLE'),
        ])
    ))
    story.append(Spacer(1, 6*mm))

    # ── Detail rows ───────────────────────────────────────────────
    rows = [
        ['Payment Method', pay.get('PaymentMethodSnapshot','—')],
    ]
    if inv:
        rows.append(['Invoice',
                     f'{inv.get("InvoiceNumber","")} — {inv.get("Description","") or ""}'])
    if pay.get('Message'):
        rows.append(['Message', pay['Message']])

    dtbl = Table(rows, colWidths=[45*mm, W-2*MARGIN-45*mm])
    dtbl.setStyle(TableStyle([
        ('FONTSIZE',     (0,0), (-1,-1), 9),
        ('ROWBACKGROUNDS',(0,0),(-1,-1), [WHITE, BGP_LIGHT]),
        ('TOPPADDING',   (0,0), (-1,-1), 5),
        ('BOTTOMPADDING',(0,0), (-1,-1), 5),
        ('LEFTPADDING',  (0,0), (-1,-1), 8),
        ('RIGHTPADDING', (0,0), (-1,-1), 8),
        ('FONTNAME',     (0,0), (0,-1),  'Helvetica-Bold'),
        ('TEXTCOLOR',    (0,0), (0,-1),  GRAY_800),
    ]))
    story.append(dtbl)

    # ── Footer ─────────────────────────────────────────────────────
    story.append(Spacer(1, 10*mm))
    story.append(HRFlowable(width='100%', thickness=0.5, color=GRAY_200))
    story.append(Paragraph('This receipt confirms payment received. BlueGreenPlanet · admin@bluegreenplanet.com',
                            ParagraphStyle('footer', fontSize=8, textColor=GRAY_500, alignment=TA_CENTER)))

    doc.build(story, canvasmaker=NumberedCanvas)
    return buf.getvalue()


if __name__ == '__main__':
    if len(sys.argv) < 3:
        sys.stderr.write('Usage: pdf_generator.py <invoice|receipt> <json>\n')
        sys.exit(1)
    doc_type = sys.argv[1]
    data     = json.loads(sys.argv[2])
    if doc_type == 'invoice':
        pdf_bytes = build_invoice(data)
    elif doc_type == 'receipt':
        pdf_bytes = build_receipt(data)
    else:
        sys.stderr.write(f'Unknown type: {doc_type}\n')
        sys.exit(1)
    sys.stdout.buffer.write(pdf_bytes)
