#!/usr/bin/env python3
import csv, io, os
from pathlib import Path

from PyPDF2 import PdfReader, PdfWriter
from PyPDF2.generic import NameObject, ArrayObject
from reportlab.pdfgen import canvas
from reportlab.lib.colors import black, white
import pikepdf

# ─── USER CONFIGURATION ────────────────────────────────────────────────────────
EXHIBIT_CSV   = "pdfData.csv"
# This script now reads from pdfData.csv, which is shared with csv_to_pdf.py.
# Structure (repeating groups of 3 rows, no header row):
#   Row 1, col 0: CSV file name  -> used to infer the PDF file name (same base, .pdf)
#   Row 2, col 0: Exhibit Name   -> used for the cover page title
#   Row 3, col 0: Exhibit Label  -> used for the footer / page labeling
#
# Only column 0 is used here; all other columns in pdfData.csv are ignored by this script.

# COVER
COVER_FONT    = "Times-Bold"
COVER_SIZE    = 36
COVER_OFFSET  = 0    # vertical shift from true center

# PAGE NUMBERS (centered)
PN_FONT       = "Times-Roman"
PN_SIZE       = 13
PN_Y          = 40   # pts from bottom

# FOOTER (bottom‐right “ExhibitLabel | p. N”)
FOOT_FONT       = "Times-Bold"
FOOT_SIZE       = 20
FOOT_Y          = 6     # pts from bottom
FOOT_RIGHT_IN   = 20    # pts in from right edge
FOOT_DIGIT_GAP  = 0     # pts gap between label end and digit region
MAX_DIGITS      = 3     # reserve width for up to 3 digits
FOOT_COLOR      = (0.0, 0.0, 0.0)   # RGB tuple

# CORNER (top‐left)
CORNER_FONT   = "Times-Roman"
CORNER_SIZE   = 14
CORNER_X      = 38    # pts from left
CORNER_Y      = 50    # pts down from top

# BOTTOM-LEFT
BL_FONT       = "Times-Roman"
BL_SIZE       = 12
BL_X          = 29    # pts from left
BL_Y          = 8     # pts from bottom

# TOP-RIGHT
TR_FONT       = "Times-Roman"
TR_SIZE       = 12
TR_RIGHT_IN   = 18
TR_TOP_IN     = 18

# ─── HELPERS ──────────────────────────────────────────────────────────────────
def read_exhibits(path):
    """
    Read exhibit configuration from pdfData.csv.

    pdfData.csv is structured as repeating groups of 3 rows per CSV/PDF:
        Row 1, col 0: CSV file name (e.g. "my_table.csv")
        Row 2, col 0: Exhibit Name (for cover page title)
        Row 3, col 0: Exhibit Label (for footer and internal labeling)

    Only the FIRST column of each row is used here; the remaining columns
    are reserved for other scripts (column widths, hyperlink specs, etc.).
    """
    base_dir = Path(__file__).resolve().parent
    csv_path = base_dir / path

    records = []
    with open(csv_path, newline="", encoding="utf-8") as f:
        reader = csv.reader(f)
        rows = list(reader)

    # Walk in groups of 3 rows: [CSV filename], [Exhibit Name], [Exhibit Label]
    for i in range(0, len(rows), 3):
        group = rows[i:i+3]
        if len(group) < 3:
            continue

        row1, row2, row3 = group

        # Row 1, col 0: CSV filename
        csv_name = ""
        if row1 and len(row1) > 0 and row1[0] is not None:
            csv_name = row1[0].strip()
        if not csv_name:
            # Nothing to do for this group
            continue

        # Convert CSV filename to PDF filename (same base, .pdf extension)
        pdf_name = os.path.splitext(csv_name)[0] + ".pdf"
        src_path = str((base_dir / pdf_name).resolve())

        # Row 2, col 0: Exhibit Name (cover title)
        exhibit_name = ""
        if row2 and len(row2) > 0 and row2[0] is not None:
            exhibit_name = row2[0].strip()
        if not exhibit_name:
            exhibit_name = os.path.splitext(os.path.basename(pdf_name))[0]

        # Row 3, col 0: Exhibit Label (footer label)
        exhibit_label = ""
        if row3 and len(row3) > 0 and row3[0] is not None:
            exhibit_label = row3[0].strip()
        if not exhibit_label:
            exhibit_label = exhibit_name

        records.append(
            {
                "PDF File Name": src_path,
                "Exhibit Name": exhibit_name,
                "Exhibit Label": exhibit_label,
            }
        )

    return records


def make_pdf_bytes(w, h, draw_fn):
    buf = io.BytesIO()
    c = canvas.Canvas(buf, pagesize=(w, h))
    draw_fn(c)
    c.showPage()
    c.save()
    buf.seek(0)
    return buf.getvalue()

def make_cover_bytes(w, h, title):
    def draw(c):
        c.setFillColor(white)
        c.rect(0, 0, w, h, fill=1, stroke=0)
        c.setFont(COVER_FONT, COVER_SIZE)
        tw = c.stringWidth(title, COVER_FONT, COVER_SIZE)
        x = (w - tw) / 2
        y = h / 2 + COVER_OFFSET
        c.setFillColor(black)
        c.drawString(x, y, title)
    return make_pdf_bytes(w, h, draw)

def make_overlay_bytes(w, h, page_no, exhibit_label,
                       corner_txt, corner_url,
                       bl_txt, bl_url,
                       tr_txt, tr_url):
    def draw(c):
        # --- centered page #
        pstr = str(page_no)
        c.setFont(PN_FONT, PN_SIZE)
        twp = c.stringWidth(pstr, PN_FONT, PN_SIZE)
        c.setFillColor(black)
        c.drawString((w - twp) / 2, PN_Y, pstr)

        # --- bottom‐right exhibit label + digits
        c.setFont(FOOT_FONT, FOOT_SIZE)
        c.setFillColorRGB(*FOOT_COLOR)
        lbl = exhibit_label + " "
        twl = c.stringWidth(lbl, FOOT_FONT, FOOT_SIZE)
        # reserve digit area width:
        max_digit_w = c.stringWidth("9" * MAX_DIGITS, PN_FONT, PN_SIZE)
        # fixed X for label so it never moves:
        x_label = w - FOOT_RIGHT_IN - max_digit_w - FOOT_DIGIT_GAP - twl
        c.drawString(x_label, FOOT_Y, lbl)
        # digits: draw left‐justified into reserved region:
        x_digits = x_label + twl + FOOT_DIGIT_GAP
        c.drawString(x_digits, FOOT_Y, pstr)

        # --- top‐left corner (disabled via empty text in this pipeline)
        if corner_txt:
            c.setFont(CORNER_FONT, CORNER_SIZE)
            c.setFillColor(black)
            y0 = h - CORNER_Y
            c.drawString(CORNER_X, y0, corner_txt)
            if corner_url:
                tw0 = c.stringWidth(corner_txt, CORNER_FONT, CORNER_SIZE)
                c.linkURL(
                    corner_url,
                    rect=(CORNER_X, y0 - CORNER_SIZE * 0.2,
                          CORNER_X + tw0, y0 + CORNER_SIZE * 0.8),
                    relative=0, thickness=0
                )

        # --- bottom‐left (disabled via empty text)
        if bl_txt:
            c.setFont(BL_FONT, BL_SIZE)
            c.setFillColor(black)
            c.drawString(BL_X, BL_Y, bl_txt)
            if bl_url:
                tw1 = c.stringWidth(bl_txt, BL_FONT, BL_SIZE)
                c.linkURL(
                    bl_url,
                    rect=(BL_X, BL_Y - BL_SIZE * 0.2,
                          BL_X + tw1, BL_Y + BL_SIZE * 0.8),
                    relative=0, thickness=0
                )

        # --- top‐right (disabled via empty text)
        if tr_txt:
            c.setFont(TR_FONT, TR_SIZE)
            c.setFillColor(black)
            y1 = h - TR_TOP_IN
            tw2 = c.stringWidth(tr_txt, TR_FONT, TR_SIZE)
            x2 = w - TR_RIGHT_IN - tw2
            c.drawString(x2, y1, tr_txt)
            if tr_url:
                c.linkURL(
                    tr_url,
                    rect=(x2, y1 - TR_SIZE * 0.2,
                          x2 + tw2, y1 + TR_SIZE * 0.8),
                    relative=0, thickness=0
                )
    return make_pdf_bytes(w, h, draw)

# ─── MAIN PROCESS ─────────────────────────────────────────────────────────────
def process_record(rec):
    src_path      = rec["PDF File Name"]
    cover_title   = rec["Exhibit Name"]
    exhibit_label = rec.get("Exhibit Label", "").strip() or cover_title

    # Numbering now always starts at 1 for this use case.
    start = 1

    # Corner / bottom-left / top-right text and URL functionality
    # are disabled for this pipeline; we keep the knobs in place
    # but feed empty strings so no extra text or links are drawn.
    corner_txt = ""
    corner_url = ""
    bl_txt     = ""
    bl_url     = ""
    tr_txt     = ""
    tr_url     = ""

    if not os.path.exists(src_path):
        print(f"⚠️ Source PDF not found, skipping: {src_path}")
        return

    reader = PdfReader(src_path)
    writer = PdfWriter()

    # infer page size from first page
    m = reader.pages[0].mediabox
    w, h = float(m.width), float(m.height)

    # 1) cover
    cover_pdf = PdfReader(io.BytesIO(make_cover_bytes(w, h, cover_title)))
    writer.add_page(cover_pdf.pages[0])

    # 2) content pages with overlay (page number + exhibit footer)
    pn = start
    for pg in reader.pages:
        orig_annots = pg.get("/Annots", None)

        ov_pdf = PdfReader(
            io.BytesIO(
                make_overlay_bytes(
                    w, h, pn, exhibit_label,
                    corner_txt, corner_url,
                    bl_txt, bl_url,
                    tr_txt, tr_url,
                )
            )
        )
        overlay = ov_pdf.pages[0]
        pg.merge_page(overlay)

        # merge annotations (if any) from overlay and original
        ov_annots = overlay.get("/Annots", None)
        if ov_annots:
            combined = []
            if orig_annots:
                combined.extend(orig_annots)
            combined.extend(ov_annots)
            pg[NameObject("/Annots")] = ArrayObject(combined)

        writer.add_page(pg)
        pn += 1

    # Output file: add "_labeled" to the base name
    src_path_obj = Path(src_path)
    out_path = src_path_obj.with_name(f"{src_path_obj.stem}_labeled.pdf")
    with open(out_path, "wb") as f:
        writer.write(f)

    # 3) optimize
    pdf = pikepdf.Pdf.open(out_path, allow_overwriting_input=True)
    pdf.remove_unreferenced_resources()
    pdf.save(out_path)
    pdf.close()

    print("✅ Wrote", out_path)

if __name__ == "__main__":
    for record in read_exhibits(EXHIBIT_CSV):
        process_record(record)
