#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#python parse_berechtigungen_to_xlsx.py export.txt
#python parse_berechtigungen_to_xlsx.py export.txt -o berechtigungen.xlsx

from __future__ import annotations

import argparse
import re
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Optional, Tuple

from openpyxl import Workbook
from openpyxl.styles import Alignment, Font
from openpyxl.utils import get_column_letter


PERMS = {"A", "P", "M"}
RE_AREA = re.compile(r"\bAnwendungsgebiet:([A-Za-z0-9_#\-]+)\b")


@dataclass
class PermissionEntry:
    area: str
    level: int
    name: str
    perm: str
    raw: str


def _is_noise_line(line: str) -> bool:
    s = line.strip()
    if not s:
        return True
    if set(s) <= {"-", " "}:
        return True
    if s.startswith("I ") and s.endswith(" I"):
        return True
    if s.startswith("Vorgabewerte der Liste:"):
        return True
    if s == "Funktion Berechtigung":
        return True
    if s.startswith("------------------------------------------------------------"):
        return True
    if "E n d e d e r L i s t e" in s:
        return True
    return False


def _extract_perm_and_text(line: str) -> Tuple[Optional[str], str]:
    s = line.rstrip("\n")
    parts = s.rstrip().split()
    if not parts:
        return None, ""
    last = parts[-1].strip()
    if last in PERMS:
        text = " ".join(parts[:-1]).rstrip()
        return last, text
    return None, s.rstrip()


def _compute_level_and_clean_name(text: str) -> Tuple[int, str]:
    s = text.lstrip()
    level = 0

    while s.startswith("|"):
        level += 1
        s = s[1:]
        s = s.lstrip()

    if s.startswith("- "):
        level += 1
        s = s[2:].lstrip()

    return level, s.rstrip()


def parse_permissions_file(path: Path) -> List[PermissionEntry]:
    lines = path.read_text(encoding="utf-8", errors="replace").splitlines()

    current_area: Optional[str] = None
    in_table = False
    entries: List[PermissionEntry] = []

    for line in lines:
        m = RE_AREA.search(line)
        if m:
            current_area = m.group(1)
            in_table = False
            continue

        if "Funktion Berechtigung" in line:
            in_table = True
            continue

        if not in_table or not current_area:
            continue

        if _is_noise_line(line):
            continue

        perm, text = _extract_perm_and_text(line)
        if not perm:
            continue

        level, name = _compute_level_and_clean_name(text)
        if not name:
            continue

        entries.append(
            PermissionEntry(
                area=current_area,
                level=level,
                name=name,
                perm=perm,
                raw=line.rstrip("\n"),
            )
        )

    return entries


def _sanitize_sheet_title(title: str) -> str:
    # Excel: max 31 Zeichen, keine : \ / ? * [ ]
    bad = r'[:\\/?*\[\]]'
    t = re.sub(bad, "_", title)
    return t[:31] if len(t) > 31 else t


def write_xlsx(entries: List[PermissionEntry], out_path: Path) -> None:
    wb = Workbook()
    # Standard-Sheet entfernen
    wb.remove(wb.active)

    by_area: Dict[str, List[PermissionEntry]] = {}
    for e in entries:
        by_area.setdefault(e.area, []).append(e)

    header_font = Font(bold=True)
    mono = Font(name="Calibri")
    perm_font = Font(bold=True)
    wrap = Alignment(wrap_text=True)

    for area in sorted(by_area.keys()):
        ws = wb.create_sheet(_sanitize_sheet_title(area))

        # Excel Outline aktivieren
        ws.sheet_properties.outlinePr.summaryBelow = True

        # Header
        ws.append(["Name", "Ausprägung"])
        ws["A1"].font = header_font
        ws["B1"].font = header_font
        ws.freeze_panes = "A2"

        row_idx = 2
        max_level = 0

        for e in by_area[area]:
            ws.cell(row=row_idx, column=1, value=e.name).font = mono
            ws.cell(row=row_idx, column=2, value=e.perm).font = perm_font

            # Sichtbarer Einzug im Text
            ws.cell(row=row_idx, column=1).alignment = Alignment(indent=e.level, wrap_text=True)

            # Outline Level für einklappbare Struktur
            ws.row_dimensions[row_idx].outlineLevel = e.level
            max_level = max(max_level, e.level)

            row_idx += 1

        # Spaltenbreiten + Format
        ws.column_dimensions["A"].width = 70
        ws.column_dimensions["B"].width = 12
        ws.auto_filter.ref = f"A1:B{row_idx-1}"

        # Optional: Standardmäßig alles eingeklappt anzeigen
        # -> Excel klappt bis zu "max_level" ein, indem tiefere Ebenen ausgeblendet werden.
        # (Du kannst das deaktivieren, indem du die nächste Schleife entfernst.)
        for r in range(2, row_idx):
            lvl = ws.row_dimensions[r].outlineLevel
            if lvl >= 2:  # ab Ebene 2 ausblenden (anpassbar)
                ws.row_dimensions[r].hidden = True
        ws.sheet_properties.outlinePr.applyStyles = True

    wb.save(out_path)


def main() -> None:
    ap = argparse.ArgumentParser(
        description="TXT-Auswertung parsen und als Excel (.xlsx) mit Hierarchie-Outline ausgeben."
    )
    ap.add_argument("input", type=Path, help="Pfad zur .txt Datei")
    ap.add_argument("-o", "--output", type=Path, default=None, help="Pfad zur .xlsx (Default: input.xlsx)")
    args = ap.parse_args()

    out = args.output or args.input.with_suffix(".xlsx")

    entries = parse_permissions_file(args.input)
    if not entries:
        print("Keine Berechtigungs-Einträge gefunden. Prüfe, ob die Datei den erwarteten Aufbau hat.")
        return

    write_xlsx(entries, out)
    print(f"Excel exportiert nach: {out}")


if __name__ == "__main__":
    main()
