#!/usr/bin/env python3
"""
pdf_to_html.py  —  Reads an invoice-style PDF and outputs a faithful HTML table.

Usage:
    python pdf_to_html.py input.pdf              # saves input_table.html
    python pdf_to_html.py input.pdf output.html  # saves to custom path

Requirements:
    pip install pdfplumber pypdf
"""

import sys
import os
import re
import pdfplumber
from pypdf import PdfReader


# ── helpers ───────────────────────────────────────────────────────────────────

def esc(text: str) -> str:
    return (
        str(text)
        .replace("&", "&amp;")
        .replace("<", "&lt;")
        .replace(">", "&gt;")
        .replace('"', "&quot;")
    )


def extract_metadata(pdf_path: str) -> dict:
    with pdfplumber.open(pdf_path) as pdf:
        first_text = pdf.pages[0].extract_text() or ""

    lines = [l.strip() for l in first_text.splitlines() if l.strip()]
    meta = {"auftraggeber": "", "date_range": "", "baustellenname": "", "teilrechnung": "", "kst_nr": ""}

    for line in lines:
        if "Auftraggeber" in line:
            parts = re.split(r"Auftraggeber:\s*", line, maxsplit=1)
            if len(parts) == 2:
                date_m = re.search(r"(\d{2}\.\d{2}\.\s*[-–]\s*\d{2}\.\d{2}\.\d{4})", parts[1])
                if date_m:
                    meta["date_range"]   = date_m.group(1).strip()
                    meta["auftraggeber"] = parts[1][:date_m.start()].strip()
                else:
                    meta["auftraggeber"] = parts[1].strip()
        elif "Baustellenname" in line:
            parts = re.split(r"Baustellenname:\s*", line, maxsplit=1)
            if len(parts) == 2:
                meta["baustellenname"] = parts[1].strip()
        elif re.match(r"Teilrechnung\s+\d+", line):
            meta["teilrechnung"] = line.strip()
        elif "Kst" in line and "Nr" in line:
            kst_m = re.search(r"Kst\.?Nr\.?:?\s*(\S+)", line)
            if kst_m:
                meta["kst_nr"] = kst_m.group(1).strip()

    return meta


def extract_rows(pdf_path: str) -> list:
    """
    Extract data rows from the PDF text.
    This PDF has no grid lines — pdfplumber returns flat text per row.
    The € symbol appears BEFORE each number: € 500,00
    """
    with pdfplumber.open(pdf_path) as pdf:
        text = "\n".join(p.extract_text() or "" for p in pdf.pages)

    pattern = re.compile(
        r"^(\d{2})\s+"                     # Pos. Nr.
        r"(.+?)\s+"                         # Positionstext
        r"(\d[\d\s]*,\d{2})\s+"            # Menge
        r"(Stk|m|kg|h|St\.?)\s+"           # Einheit
        r"€\s*([\d\s]+,\d{2})\s+"          # Einheitspreis (€ before number)
        r"€\s*([\d\s]+,\d{2})\s*"          # Betrag        (€ before number)
        r"(.+)?$",                          # Anmerkungen
        re.MULTILINE,
    )

    rows = []
    for m in pattern.finditer(text):
        pos, desc, menge, einheit, ep, betrag, anm = m.groups()
        rows.append([
            pos.strip(),
            desc.strip(),
            menge.strip(),
            einheit.strip(),
            f"€ {ep.strip()}",
            f"€ {betrag.strip()}",
            (anm or "").strip(),
        ])
    return rows


def extract_total(pdf_path: str) -> str:
    with pdfplumber.open(pdf_path) as pdf:
        text = "\n".join(p.extract_text() or "" for p in pdf.pages)
    m = re.search(r"Summe\s+€\s*([\d\s]+,\d{2})", text)
    return f"€ {m.group(1).strip()}" if m else ""


# ── CSS ───────────────────────────────────────────────────────────────────────

CSS = """
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&family=IBM+Plex+Sans:wght@400;600;700&display=swap');

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: 'IBM Plex Sans', sans-serif;
  background: #f4f1ec;
  color: #1a1a1a;
  padding: 3rem 2rem;
  font-size: 13px;
}

.page {
  max-width: 960px;
  margin: 0 auto;
  background: #fff;
  border: 1px solid #ccc;
  padding: 2.5rem 3rem;
  box-shadow: 4px 4px 0 #c8c0b0;
}

.header-meta {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 0.3rem;
  font-size: 12px;
}

.header-meta .right {
  text-align: right;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
}

h1 { font-size: 15px; font-weight: 700; margin-bottom: 0.15rem; }

.sub-meta {
  font-size: 11.5px;
  color: #555;
  margin-bottom: 1.6rem;
  font-family: 'IBM Plex Mono', monospace;
}

table { width: 100%; border-collapse: collapse; font-size: 12px; }

thead tr {
  border-top: 2px solid #1a1a1a;
  border-bottom: 2px solid #1a1a1a;
}

thead th {
  padding: 5px 8px;
  text-align: left;
  font-weight: 700;
  font-size: 11.5px;
  white-space: nowrap;
}

thead th.num { text-align: right; }
tbody tr { border-bottom: 1px solid #e0ddd8; }
tbody tr:hover { background: #faf8f4; }
td { padding: 5px 8px; vertical-align: top; line-height: 1.4; }

td.num {
  text-align: right;
  font-family: 'IBM Plex Mono', monospace;
  white-space: nowrap;
}

td.pos {
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 600;
  white-space: nowrap;
}

td.anmerkung { color: #444; font-size: 11.5px; }
tfoot tr { border-top: 2px solid #1a1a1a; }
tfoot td { padding: 7px 8px; font-weight: 700; font-size: 13px; }
tfoot td.num { font-family: 'IBM Plex Mono', monospace; text-align: right; }
"""

HEADERS  = ["Pos. Nr.", "Positionstext", "Menge", "Einheit", "Einheitspreis", "Betrag", "Anmerkungen"]
NUM_COLS = {2, 4, 5}


# ── HTML builder ──────────────────────────────────────────────────────────────

def build_html(meta, rows, total, pdf_name):
    auftraggeber   = esc(meta.get("auftraggeber", ""))
    date_range     = esc(meta.get("date_range", ""))
    baustellenname = esc(meta.get("baustellenname", ""))
    teilrechnung   = esc(meta.get("teilrechnung", ""))
    kst_nr         = esc(meta.get("kst_nr", ""))

    th_cells = "".join(
        f'<th{" class=\"num\"" if i in NUM_COLS else ""}>{esc(h)}</th>'
        for i, h in enumerate(HEADERS)
    )

    tbody = ""
    for row in rows:
        row = list(row) + [""] * (7 - len(row))
        tds = ""
        for i, cell in enumerate(row[:7]):
            if i == 0:
                tds += f'<td class="pos">{esc(cell)}</td>'
            elif i in NUM_COLS:
                tds += f'<td class="num">{esc(cell)}</td>'
            elif i == 6:
                tds += f'<td class="anmerkung">{esc(cell)}</td>'
            else:
                tds += f"<td>{esc(cell)}</td>"
        tbody += f"    <tr>{tds}</tr>\n"

    tfoot = ""
    if total:
        tfoot = (
            f'\n    <tfoot><tr><td colspan="5"><strong>Summe</strong></td>'
            f'<td class="num">{esc(total)}</td><td></td></tr></tfoot>'
        )

    sub_parts = [p for p in [teilrechnung, f"Kst.Nr.: {kst_nr}" if kst_nr else ""] if p]
    sub_line  = " &nbsp;|&nbsp; ".join(sub_parts)

    return f"""<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>{esc(pdf_name)}</title>
<style>{CSS}</style>
</head>
<body>
<div class="page">

  <div class="header-meta">
    <div><strong>Auftraggeber:</strong> {auftraggeber}</div>
    <div class="right">{date_range}</div>
  </div>
  {"<h1>Baustellenname: " + baustellenname + "</h1>" if baustellenname else ""}
  {"<div class='sub-meta'>" + sub_line + "</div>" if sub_line else ""}

  <table>
    <thead><tr>{th_cells}</tr></thead>
    <tbody>
{tbody}    </tbody>{tfoot}
  </table>

</div>
</body>
</html>
"""


# ── main ──────────────────────────────────────────────────────────────────────

def main():
    if len(sys.argv) < 2:
        print("Usage: python pdf_to_html.py input.pdf [output.html]")
        sys.exit(1)

    pdf_path = sys.argv[1]
    if not os.path.exists(pdf_path):
        print(f"❌  File not found: {pdf_path}")
        sys.exit(1)

    output_path = (
        sys.argv[2] if len(sys.argv) > 2
        else os.path.splitext(pdf_path)[0] + "_table.html"
    )

    print(f"📄  Reading: {pdf_path}")
    meta  = extract_metadata(pdf_path)
    rows  = extract_rows(pdf_path)
    total = extract_total(pdf_path)

    print(f"   Rows found : {len(rows)}")
    print(f"   Total      : {total or '(not found)'}")

    if not rows:
        print("⚠️  No data rows extracted.")
        sys.exit(1)

    html = build_html(meta, rows, total, os.path.basename(pdf_path))

    with open(output_path, "w", encoding="utf-8") as f:
        f.write(html)

    print(f"✅  Saved: {output_path}")


if __name__ == "__main__":
    main()

