"""
Script 2 (analyze) – parser jednoho PDF denního záznamu do strukturovaných dat.

Zatím jen parsování + výpis JSON pro ověření na reálných PDF, bez zápisu
do DB (to přidáme, až bude parser ověřený na víc dnech).

Použití:
    venv/bin/python parse_pdf.py PDF/kongres_2026-08-11.pdf
"""

import json
import re
import sys

import pdfplumber

WEATHER_ROW_RE = re.compile(
    r"(\d{2}:\d{2})\s+(\S+)\s+([\d,]+)°C\s+([\d,]+)\s*mm/h\s+([\d,]+)\s*m/s\s+(\d+)°\s+(\d+)%"
)


def _extract_section(text: str, start_marker: str, end_markers: list[str]) -> str:
    start = text.find(start_marker)
    if start == -1:
        return ""
    start += len(start_marker)
    end = len(text)
    for marker in end_markers:
        idx = text.find(marker, start)
        if idx != -1:
            end = min(end, idx)
    return text[start:end]


def is_closed(text: str) -> bool:
    """Uzavřený den má na konci sekci 'Podpisy:' se skutečným podpisem."""
    return bool(re.search(r"\bPodpisy\s*:", text))


def parse_weather(text: str) -> list[dict]:
    rows = []
    for m in WEATHER_ROW_RE.finditer(text):
        time_s, condition, temp, precip, wind_speed, wind_dir, humidity = m.groups()
        rows.append({
            "cas": time_s,
            "stav": condition,
            "teplota_c": float(temp.replace(",", ".")),
            "srazky_mm": float(precip.replace(",", ".")),
            "vitr_ms": float(wind_speed.replace(",", ".")),
            "smer_vetru_deg": int(wind_dir),
            "vlhkost_pct": int(humidity),
        })
    return rows


def _group_by_row(words: list[dict], y_tol: float = 2.0) -> list[list[dict]]:
    """Seskupí slova do řádků podle Y pozice (top), seřazené shora dolů."""
    rows: list[list[dict]] = []
    for w in sorted(words, key=lambda w: w["top"]):
        if rows and abs(w["top"] - rows[-1][0]["top"]) <= y_tol:
            rows[-1].append(w)
        else:
            rows.append([w])
    for row in rows:
        row.sort(key=lambda w: w["x0"])
    return rows


def parse_osoby(page) -> list[dict]:
    """Tabulka 'Osoby na staveništi' má 3 sloupcové skupiny vedle sebe a
    dlouhé názvy dodavatelů se lámou na druhý řádek – při čtení jako
    lineární text se pak zalomený zbytek spojí s nesouvisejícím sloupcem
    na stejném fyzickém řádku (viz komentáře v historii souboru/gitu).
    Řešíme proto podle skutečných X/Y souřadnic slov na stránce, ne textem.
    """
    words = page.extract_words(x_tolerance=1)

    section_top = next(
        (w["top"] for w in words if w["text"].upper().startswith("OSOBY")), None
    )
    if section_top is None:
        return []
    section_bottom = next(
        (w["top"] for w in words if w["top"] > section_top and w["text"].upper().startswith("MECHANIZMY")),
        None,
    )
    if section_bottom is None:
        section_bottom = max(w["bottom"] for w in words) + 1

    section_words = [w for w in words if section_top < w["top"] < section_bottom]
    header_rows = [r for r in _group_by_row(section_words) if any(w["text"] == "NÁZEV" for w in r)]
    if not header_rows:
        return []
    header_row = header_rows[0]
    header_top = header_row[0]["top"]

    col_starts = sorted(w["x0"] for w in header_row if w["text"] == "NÁZEV")
    col_bounds = list(zip(col_starts, col_starts[1:] + [1_000_000.0]))

    def col_index(x0: float) -> int | None:
        for i, (lo, hi) in enumerate(col_bounds):
            if lo - 2 <= x0 < hi:
                return i
        return None

    data_words = [w for w in section_words if w["top"] > header_top + 2]

    per_column: dict[int, list[str]] = {i: [] for i in range(len(col_bounds))}
    for row in _group_by_row(data_words):
        by_col: dict[int, list[str]] = {}
        for w in row:
            ci = col_index(w["x0"])
            if ci is not None:
                by_col.setdefault(ci, []).append(w["text"])
        for ci, texts in by_col.items():
            per_column[ci].append(" ".join(texts))

    rows = []
    for lines in per_column.values():
        last_row = None
        for line in lines:
            m = re.search(r"(\d+)\s+(\d+)\s*$", line)
            if not m:
                # Řádek bez čísel = zalomený zbytek názvu z řádku NAD ním
                # (čísla už byla vyčtena tam) – přilep jako příponu k
                # poslednímu záznamu v tomhle sloupci, ne jako předponu dalšímu.
                if last_row is not None and line.strip():
                    last_row["dodavatel"] += " " + line.strip()
                continue
            name = line[: m.start()].strip()
            if not name:
                continue
            last_row = {"dodavatel": name, "pocet": int(m.group(1)), "hodin": int(m.group(2))}
            rows.append(last_row)
    return rows


def parse_mechanizmy(text: str, known_suppliers: list[str]) -> list[dict]:
    block = _extract_section(text, "MECHANIZMY:", ["PROVEDENÉ PRÁCE:", "DALŠÍ ZÁZNAMY:"])
    block = re.sub(r"NÁZEV PROSTŘEDKU\s+NÁZEV DODAVATELE\s+POSÁDKA\s+POČ\.\s+HOD\.", " ", block)

    rows = []
    prev_end = 0
    for m in re.finditer(r"(\d+)\s+(\d+)\s+(\d+)(?=\s|$)", block):
        text_part = " ".join(block[prev_end:m.start()].split())
        prev_end = m.end()
        if not text_part:
            continue
        posadka, pocet, hodin = int(m.group(1)), int(m.group(2)), int(m.group(3))

        resource, supplier = text_part, None
        for sup in sorted(known_suppliers, key=len, reverse=True):
            if text_part.endswith(sup):
                resource = text_part[: -len(sup)].strip()
                supplier = sup
                break

        rows.append({
            "nazev": resource, "dodavatel": supplier,
            "posadka": posadka, "pocet": pocet, "hodin": hodin,
        })
    return rows


def parse_prace(text: str, known_suppliers: list[str]) -> list[dict]:
    block = _extract_section(text, "PROVEDENÉ PRÁCE:", ["DALŠÍ ZÁZNAMY:"])
    block = re.sub(r"POPIS\s+NÁZEV DODAVATELE\s+MN\s+MJ", " ", block)

    # Dodavatel se v extrahovaném textu neobjevuje na vlastním řádku, ale
    # přilepený na konec jednoho z bullet řádků dané skupiny (a není předem
    # jasné, jestli je to první, poslední nebo jiný řádek skupiny) – hranici
    # skupiny proto poznáme podle toho, že řádek KONČÍ známým dodavatelem
    # (ze seznamu z parse_osoby), stejně jako u parse_mechanizmy.
    suppliers_sorted = sorted(known_suppliers, key=len, reverse=True)

    # Dodavatel je "top-aligned" k PRVNÍMU bullet řádku své skupiny (ne
    # k poslednímu) – řádek s dodavatelem proto novou skupinu ZAKLÁDÁ,
    # ne uzavírá tu předchozí.
    groups: list[dict] = []
    current: dict | None = None
    for line in block.splitlines():
        line = line.strip()
        if not line.startswith("-"):
            continue
        line = line.lstrip("- ").strip()

        dodavatel = None
        for sup in suppliers_sorted:
            if line.endswith(sup):
                line = line[: -len(sup)].strip()
                dodavatel = sup
                break

        if dodavatel is not None:
            if current is not None:
                groups.append(current)
            current = {"popis": [line] if line else [], "dodavatel": dodavatel}
        else:
            if current is None:
                current = {"popis": [], "dodavatel": None}
            if line:
                current["popis"].append(line)

    if current is not None:
        groups.append(current)
    return groups


def parse_dalsi_zaznamy(text: str) -> list[dict]:
    # "Podpisy:" se objevuje jen jednou, na samém konci (jen u uzavřených
    # dnů) – patičky stránek jsou z textu už odstraněné v parse_pdf(), takže
    # tu dřív používaná značka konce sekce by useknout obsah po 1. straně.
    block = _extract_section(text, "DALŠÍ ZÁZNAMY:", ["Podpisy:"])
    chunks = re.split(r"\n(?=AUTOR\s)", block.strip())

    entries = []
    for chunk in chunks:
        chunk = chunk.strip()
        if not chunk.startswith("AUTOR"):
            continue
        lines = chunk.splitlines()
        header = lines[0]

        if "NÁZEV DODAVATELE" in header:
            author_part, dodavatel = header.split("NÁZEV DODAVATELE", 1)
        else:
            author_part, dodavatel = header, ""
        author_part = author_part.replace("AUTOR", "", 1).strip()

        role_match = re.search(r"\((.+?)\)\s*$", author_part)
        role = role_match.group(1) if role_match else None
        author = author_part[: role_match.start()].strip() if role_match else author_part

        rest = lines[1:]
        if "PŘÍLOHY" in rest:
            idx = rest.index("PŘÍLOHY")
            body_lines, attach_lines = rest[:idx], rest[idx + 1:]
        else:
            body_lines, attach_lines = rest, []

        entries.append({
            "autor": author,
            "role": role,
            "dodavatel": dodavatel.strip(),
            "text": " ".join(l.strip() for l in body_lines if l.strip()),
            # Raw text – přesné názvy souborů se z PDF textu spolehlivě
            # nerekonstruují (ikony příloh se lámou přes řádky).
            "prilohy_raw": " ".join(l.strip() for l in attach_lines if l.strip()) or None,
        })
    return entries


def parse_pdf(pdf_path: str) -> dict:
    with pdfplumber.open(pdf_path) as pdf:
        # Výchozí x_tolerance (3) byl u téhle PDF šablony moc benevolentní –
        # sousední slova se lepila bez mezery ("Avant 745" -> "Avant745").
        # Nižší práh donutí extrakci vložit mezeru i při menší mezeře.
        text = "\n".join(page.extract_text(x_tolerance=1) or "" for page in pdf.pages)

        # Patička se opakuje na každé straně ("ČÁST B - DENNÍ ZÁZNAMY STRANA
        # <n> KCP - STAVEBNÍ DENÍK - <datum>.PDF") – odstranit globálně, ať
        # nepůsobí jako falešná hranice konce sekce a nekouše text napůl.
        text = re.sub(
            r"ČÁST B - DENNÍ ZÁZNAMY STRANA\s*\d*\s*KCP - STAVEBNÍ DENÍK - \d{4}-\d{2}-\d{2}\.PDF\s*",
            "\n", text,
        )

        closed = is_closed(text)
        result = {
            "closed": closed,
            "dalsi_zaznamy": parse_dalsi_zaznamy(text),
        }
        if closed:
            # Osoby na staveništi řešíme přes souřadnice slov (viz parse_osoby),
            # sekce je vždy na 1. straně – musí se volat, dokud je PDF otevřené.
            osoby = parse_osoby(pdf.pages[0])
            suppliers = [o["dodavatel"] for o in osoby]
            result["pocasi"] = parse_weather(text)
            result["osoby"] = osoby
            result["mechanizmy"] = parse_mechanizmy(text, suppliers)
            result["prace"] = parse_prace(text, suppliers)
    return result


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Použití: python parse_pdf.py <cesta_k_pdf>")
        sys.exit(1)
    print(json.dumps(parse_pdf(sys.argv[1]), ensure_ascii=False, indent=2))
