#!/usr/bin/env python3
"""
Jarvis PDF Template — Apple-aesthetic generator.

USAGE: Edit the CONTENT section at the bottom. Run: python3 jarvis-pdf-template.py
Output goes to OUTPUT_PATH (default: ./output.pdf).

DESIGN SPEC (locked):
- Helvetica family
- Slate accent #2c5282
- Ink body #1a202c, slate gray #4a5568, hush #a0aec0
- 1.2" margins, generous whitespace
- Smart quotes, em-dashes
- Title page with section meta + thin accent rule
- Footer with doc title (small caps left) + page number (right) + hairline rule
- Section numbers (§ 01, § 02...) above each H1
- 2pt 8%-wide accent rule under each H1
- Pull quotes: thin left border in accent color

DO NOT modify the styling. Only edit the CONTENT section.
"""

from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT
from reportlab.lib.colors import HexColor
from reportlab.lib.units import inch
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, PageBreak,
    HRFlowable, Table, TableStyle,
)
import re
import os

# ─── Locked palette — DO NOT CHANGE ────────────────────────────────
INK    = HexColor("#1a202c")
DIM    = HexColor("#4a5568")
HUSH   = HexColor("#a0aec0")
ACCENT = HexColor("#2c5282")
PAPER  = HexColor("#f7fafc")
RULE   = HexColor("#e2e8f0")

PAGE_W, PAGE_H = letter
MARGIN = 1.2 * inch

# ─── Styles ────────────────────────────────────────────────────────
S = {
    "title": ParagraphStyle(
        "Title", fontName="Helvetica", fontSize=44, leading=52,
        textColor=INK, alignment=TA_LEFT, spaceAfter=10,
    ),
    "subtitle": ParagraphStyle(
        "Subtitle", fontName="Helvetica-Oblique", fontSize=16, leading=22,
        textColor=DIM, alignment=TA_LEFT, spaceAfter=4,
    ),
    "meta": ParagraphStyle(
        "Meta", fontName="Helvetica", fontSize=9, leading=13,
        textColor=HUSH, alignment=TA_LEFT, spaceAfter=2,
    ),
    "h1": ParagraphStyle(
        "H1", fontName="Helvetica-Bold", fontSize=24, leading=30,
        textColor=INK, spaceBefore=24, spaceAfter=8, keepWithNext=1,
    ),
    "section_num": ParagraphStyle(
        "SectionNum", fontName="Helvetica", fontSize=9, leading=12,
        textColor=ACCENT, spaceBefore=18, spaceAfter=2, keepWithNext=1,
    ),
    "h2": ParagraphStyle(
        "H2", fontName="Helvetica-Bold", fontSize=16, leading=22,
        textColor=INK, spaceBefore=16, spaceAfter=8, keepWithNext=1,
    ),
    "h3": ParagraphStyle(
        "H3", fontName="Helvetica-Bold", fontSize=12, leading=16,
        textColor=DIM, spaceBefore=10, spaceAfter=4, keepWithNext=1,
    ),
    "body": ParagraphStyle(
        "Body", fontName="Helvetica", fontSize=11, leading=18,
        textColor=INK, spaceAfter=10, alignment=TA_LEFT,
    ),
    "bullet": ParagraphStyle(
        "Bullet", fontName="Helvetica", fontSize=11, leading=18,
        textColor=INK, leftIndent=24, bulletIndent=10, spaceAfter=4,
    ),
    "quote": ParagraphStyle(
        "Quote", fontName="Helvetica-Oblique", fontSize=12, leading=20,
        textColor=DIM, leftIndent=22, rightIndent=22,
        spaceBefore=14, spaceAfter=14, alignment=TA_LEFT,
    ),
    "caption": ParagraphStyle(
        "Caption", fontName="Helvetica", fontSize=9, leading=12,
        textColor=HUSH, alignment=TA_LEFT, spaceAfter=4,
    ),
}

# ─── Footer ────────────────────────────────────────────────────────
def make_footer(doc_title):
    def page_footer(canvas, doc):
        canvas.saveState()
        if canvas.getPageNumber() > 1:
            canvas.setFont("Helvetica", 8)
            canvas.setFillColor(HUSH)
            canvas.drawString(MARGIN, 0.55 * inch, doc_title.upper())
            canvas.drawRightString(PAGE_W - MARGIN, 0.55 * inch, str(canvas.getPageNumber()))
            canvas.setStrokeColor(RULE)
            canvas.setLineWidth(0.4)
            canvas.line(MARGIN, 0.72 * inch, PAGE_W - MARGIN, 0.72 * inch)
        canvas.restoreState()
    return page_footer

# ─── Markdown → flowables ──────────────────────────────────────────
def esc(s):
    return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")

def inline(s):
    s = esc(s)
    s = re.sub(r"\*\*(.+?)\*\*", r"<b>\1</b>", s)
    s = re.sub(r"(?<!\*)\*([^\*\n]+)\*(?!\*)", r"<i>\1</i>", s)
    s = s.replace("--", "—")
    s = re.sub(r'"(\w)', r'“\1', s)
    s = re.sub(r'(\w)"', r'\1”', s)
    s = s.replace("'s ", "’s ").replace("'t ", "’t ").replace("'re ", "’re ").replace("'ll ", "’ll ").replace("'ve ", "’ve ").replace("'d ", "’d ").replace("'m ", "’m ")
    return s

def md_to_flowables(md):
    flow, lines, i, section_counter = [], md.split("\n"), 0, 0
    while i < len(lines):
        line = lines[i].rstrip()
        if not line.strip():
            i += 1; continue
        if line.strip() in ("---", "***", "___"):
            flow.append(Spacer(1, 0.1 * inch))
            flow.append(HRFlowable(width="15%", thickness=0.6, color=ACCENT,
                                    spaceBefore=4, spaceAfter=14, hAlign='LEFT'))
            i += 1; continue
        if line.startswith("# "):
            section_counter += 1
            flow.append(Paragraph(f"§ {section_counter:02d}", S["section_num"]))
            flow.append(Paragraph(inline(line[2:]), S["h1"]))
            flow.append(HRFlowable(width="8%", thickness=2, color=ACCENT,
                                    spaceBefore=2, spaceAfter=14, hAlign='LEFT'))
            i += 1; continue
        if line.startswith("## "):
            flow.append(Paragraph(inline(line[3:]), S["h2"]))
            i += 1; continue
        if line.startswith("### "):
            flow.append(Paragraph(inline(line[4:]), S["h3"]))
            i += 1; continue
        if re.match(r"^[\-\*•] ", line):
            flow.append(Paragraph("•&nbsp;&nbsp;" + inline(line[2:].strip()), S["bullet"]))
            i += 1; continue
        m = re.match(r"^(\d+)\. (.+)$", line)
        if m:
            flow.append(Paragraph(f"<b>{m.group(1)}.</b>&nbsp;&nbsp;{inline(m.group(2))}", S["bullet"]))
            i += 1; continue
        if line.startswith("> "):
            quote_lines = []
            while i < len(lines) and lines[i].startswith("> "):
                quote_lines.append(lines[i][2:].strip()); i += 1
            quoted = " ".join(quote_lines)
            t = Table([[Paragraph(f'<i>{inline(quoted)}</i>', S["quote"])]],
                      colWidths=[PAGE_W - 2 * MARGIN])
            t.setStyle(TableStyle([
                ("LINEBEFORE", (0, 0), (0, -1), 2, ACCENT),
                ("LEFTPADDING", (0, 0), (-1, -1), 16),
                ("RIGHTPADDING", (0, 0), (-1, -1), 0),
                ("TOPPADDING", (0, 0), (-1, -1), 6),
                ("BOTTOMPADDING", (0, 0), (-1, -1), 6),
            ]))
            flow.append(t); flow.append(Spacer(1, 0.1 * inch)); continue
        para_lines = [line]; i += 1
        while i < len(lines) and lines[i].strip() and not re.match(r"^(#|\-|\*|\d+\.|>|---|\*\*\*)", lines[i]):
            para_lines.append(lines[i].rstrip()); i += 1
        flow.append(Paragraph(inline(" ".join(para_lines)), S["body"]))
    return flow

# ─── Build PDF ─────────────────────────────────────────────────────
def build_pdf(title, subtitle, content_md, output_path,
              series_label=None, compile_date=None, author=None):
    """
    title:         large title text for cover page
    subtitle:      italic subtitle below title
    content_md:    markdown content (# H1, ## H2, ### H3, bullets, > quotes, --- breaks)
    output_path:   where to write the .pdf
    series_label:  optional uppercase label (e.g. "THIEL MODULE · FILE 1 OF 4")
    compile_date:  optional date string for footer (e.g. "Compiled May 10, 2026")
    author:        optional vault owner line
    """
    doc = SimpleDocTemplate(
        output_path, pagesize=letter,
        leftMargin=MARGIN, rightMargin=MARGIN,
        topMargin=MARGIN, bottomMargin=MARGIN,
        title=title, author=author or "Aron Ecoff",
    )
    flow = []
    # Title page
    flow.append(Spacer(1, 2.6 * inch))
    if series_label:
        flow.append(Paragraph(series_label, S["meta"]))
        flow.append(Spacer(1, 0.15 * inch))
    flow.append(Paragraph(title, S["title"]))
    flow.append(Spacer(1, 0.2 * inch))
    if subtitle:
        flow.append(Paragraph(subtitle, S["subtitle"]))
    flow.append(Spacer(1, 0.5 * inch))
    flow.append(HRFlowable(width="12%", thickness=1.5, color=ACCENT, hAlign='LEFT'))
    flow.append(Spacer(1, 0.5 * inch))
    if compile_date:
        flow.append(Paragraph(compile_date, S["meta"]))
    if author:
        flow.append(Paragraph(f"For the personal vault of {author}", S["meta"]))
    flow.append(PageBreak())
    # Content
    flow.extend(md_to_flowables(content_md))
    doc.build(flow,
              onFirstPage=make_footer(title),
              onLaterPages=make_footer(title))
    print(f"PDF written: {output_path}")
    print(f"Bytes: {os.path.getsize(output_path)}")

# ═══════════════════════════════════════════════════════════════════
# CONTENT — EDIT ONLY THIS SECTION
# ═══════════════════════════════════════════════════════════════════

if __name__ == "__main__":
    TITLE = "Giants 3 — Pirates 13"
    SUBTITLE = "May 9, 2026 · Oracle Park"
    SERIES_LABEL = "GAME RECAP · 2026 SEASON"
    COMPILE_DATE = "Compiled May 10, 2026"
    AUTHOR = "Aron Ecoff"
    OUTPUT_PATH = "/tmp/giants-recap.pdf"

    CONTENT = """
# Final

Pittsburgh Pirates defeated the San Francisco Giants 13–3 at Oracle Park. Attendance: 40,417. Game time: 3:04.

The Pirates broke it open early and never let off, recording a season-high 20 hits. Every Pittsburgh starter logged at least one hit.

# Pirates — Winners

**Braxton Ashcraft (W, 2-2)** — 7 IP, 6 H, 6 K

- Nick Gonzales: 4 hits
- Joey Bart: 4 hits
- Oneil Cruz: 3 hits
- Brandon Lowe: 2 hits, 4 RBI
- Ryan O'Hearn: 2 hits

# Giants — Losers

**Landon Roupp (L, 5-3)** — 4+ IP, 1 R, 8 K

- Bryce Eldridge: solo home run in the 5th — his first career MLB HR
- Christian Koss: hit by pitch in the 9th, left the game
- The bullpen collapsed in the 7th — four runs scored off Brubaker and Santos to push the lead to 10-1

# Turning Point

The seventh inning. Pittsburgh tacked on four runs against the Giants bullpen — first off JT Brubaker, then Gregory Santos — turning a manageable 6-1 deficit into a rout.

# Source

ESPN game recap: https://www.espn.com/mlb/recap/_/gameId/401815281
"""

    build_pdf(
        title=TITLE,
        subtitle=SUBTITLE,
        content_md=CONTENT,
        output_path=OUTPUT_PATH,
        series_label=SERIES_LABEL,
        compile_date=COMPILE_DATE,
        author=AUTHOR,
    )
