"""
update-praesentation-lint.py

Lint-Wrapper für Slide-Files in Stefans Präsentations-Bundles.
Prüft gegen Designsystem-Tokens, Layout-Pflichten und Branding-Bezüge.

Aufruf:
    python update-praesentation-lint.py --bundle <pfad>
    python update-praesentation-lint.py --bundle <pfad> --strict
    python update-praesentation-lint.py --bundle <pfad> --install-hook

Default-Verhalten: Verstöße als Liste ausgeben, Exit-Code 0 (Build wird nicht abgebrochen).
Strict-Modus: Exit-Code 1 bei Verstößen (für CI oder Echo-internen Selbstcheck).

Lint-Regeln (initial bewusst konservativ):
- Keine fremden Hex-Farben in JSX-Files (nur via CSS-Variablen erlaubt)
- Keine externen URL-Logos (Logo muss aus _runtime/assets/ kommen)
- Cover hat span.gold-dot
- Abschluss hat span.sig-line
- Keine fremden Font-Imports (@import url(...))

Erweiterung leicht möglich — neue Regeln einfach in CHECKS-Liste anhängen.
"""

# --- Bundle-Pflicht-Sätze §1 (Wave I v0.17.0, Phase A) ---
# Pfad-Resolver-Standard (analog _cross_skill_evals/run_linkedin_wave_h_evals.py):
#   BUNDLE_ROOT = Path(__file__).resolve().parent.parent.parent.parent
#     # .skills/neue-praesentation/scripts/file.py -> Repo-Root
#   SKILL_ROOT = BUNDLE_ROOT / ".skills" / "neue-praesentation"
#   FOUNDATION = BUNDLE_ROOT / "_lib" / "designsystem" / "templates" / "praesentation"
# Echte Umlaute (ä ö ü ß) durchgehend, keine ae/oe/ue/ss-Substitutionen.

from __future__ import annotations

import argparse
import re
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Callable

RUNTIME_DIRNAME = "_runtime"


@dataclass
class Violation:
    file: str
    line: int
    rule: str
    snippet: str

    def format(self) -> str:
        return f"{self.file}:{self.line}  [{self.rule}]  {self.snippet.strip()[:120]}"


# ---------- Regel-Definitionen ----------


def _hex_color_check(file: Path, lines: list[str]) -> list[Violation]:
    """Findet Hex-Farben in Inline-Styles oder style-Strings."""
    pattern = re.compile(r"#[0-9a-fA-F]{3,8}\b")
    out = []
    for i, line in enumerate(lines, start=1):
        # Erlaubt: in Kommentaren oder als Token-Referenz "var(--color-…)"
        if line.strip().startswith("//") or line.strip().startswith("/*"):
            continue
        if "var(--" in line:
            continue
        for m in pattern.finditer(line):
            out.append(
                Violation(
                    file=file.name,
                    line=i,
                    rule="no-hex-color",
                    snippet=line.rstrip(),
                )
            )
    return out


def _external_logo_check(file: Path, lines: list[str]) -> list[Violation]:
    """Logo-/Image-Pfade dürfen nicht extern sein."""
    pattern = re.compile(r"(src|href|url)\s*=\s*[\"']https?://", re.IGNORECASE)
    out = []
    for i, line in enumerate(lines, start=1):
        if pattern.search(line):
            out.append(
                Violation(
                    file=file.name,
                    line=i,
                    rule="external-asset",
                    snippet=line.rstrip(),
                )
            )
    return out


def _font_import_check(file: Path, lines: list[str]) -> list[Violation]:
    """Keine @import-Anweisungen für Fonts in JSX/JS-Files."""
    pattern = re.compile(r"@import\s+url\s*\(")
    out = []
    for i, line in enumerate(lines, start=1):
        if pattern.search(line):
            out.append(
                Violation(
                    file=file.name,
                    line=i,
                    rule="external-font-import",
                    snippet=line.rstrip(),
                )
            )
    return out


def _cover_gold_dot_check(file: Path, lines: list[str]) -> list[Violation]:
    """Cover-Slides müssen .gold-dot enthalten."""
    text = "".join(lines)
    if not _has_layout(text, "cover"):
        return []
    if "gold-dot" not in text:
        return [Violation(file=file.name, line=1, rule="cover-missing-gold-dot", snippet="Layout cover ohne .gold-dot")]
    return []


def _abschluss_sig_line_check(file: Path, lines: list[str]) -> list[Violation]:
    """Abschluss-Slides müssen .sig-line enthalten."""
    text = "".join(lines)
    if not _has_layout(text, "abschluss"):
        return []
    if "sig-line" not in text:
        return [
            Violation(
                file=file.name,
                line=1,
                rule="abschluss-missing-sig-line",
                snippet="Layout abschluss ohne .sig-line",
            )
        ]
    return []


def _has_layout(text: str, layout: str) -> bool:
    pattern = re.compile(rf"//\s*layout\s*:\s*{re.escape(layout)}\b", re.IGNORECASE)
    return bool(pattern.search(text))


CHECKS: list[Callable[[Path, list[str]], list[Violation]]] = [
    _hex_color_check,
    _external_logo_check,
    _font_import_check,
    _cover_gold_dot_check,
    _abschluss_sig_line_check,
]


# ---------- Lint-Lauf ----------


def lint_bundle(bundle: Path) -> list[Violation]:
    runtime = bundle / RUNTIME_DIRNAME
    if not runtime.is_dir():
        sys.exit(f"FEHLER: kein _runtime/-Ordner unter {bundle}")

    violations: list[Violation] = []
    for path in sorted(runtime.rglob("*.jsx")):
        try:
            lines = path.read_text(encoding="utf-8").splitlines(keepends=True)
        except UnicodeDecodeError:
            violations.append(Violation(file=path.name, line=1, rule="encoding", snippet="Datei nicht UTF-8"))
            continue
        for check in CHECKS:
            violations.extend(check(path, lines))

    return violations


def install_hook(bundle: Path) -> Path:
    """Schreibt einen kleinen Pre-Build-Hook in _runtime/build.cmd und build.sh,
    der diesen Lint-Helper vor dem eigentlichen Build aufruft.
    """
    runtime = bundle / RUNTIME_DIRNAME
    if not runtime.is_dir():
        sys.exit(f"FEHLER: kein _runtime/-Ordner unter {bundle}")

    helper = Path(__file__).resolve()
    cmd_hook = runtime / "build.cmd"
    sh_hook = runtime / "build.sh"

    cmd_lines = [
        "@echo off",
        f'python "{helper}" --bundle "%~dp0.."',
        "if errorlevel 1 echo (Lint-Verstöße, Build läuft trotzdem weiter)",
        "REM --- Original-Build folgt unter dieser Zeile ---",
    ]
    sh_lines = [
        "#!/usr/bin/env bash",
        f'python "{helper.as_posix()}" --bundle "$(dirname "$0")/.."',
        '# (Lint läuft non-strict, Build läuft trotzdem weiter)',
        "# --- Original-Build folgt unter dieser Zeile ---",
    ]

    cmd_hook.write_text("\r\n".join(cmd_lines), encoding="utf-8")
    sh_hook.write_text("\n".join(sh_lines), encoding="utf-8")
    return cmd_hook


# ---------- CLI ----------


def main() -> None:
    ap = argparse.ArgumentParser(description="Lint-Wrapper für Stefans Präsentations-Bundles.")
    ap.add_argument("--bundle", required=True, type=Path, help="Pfad zum Bundle-Ordner (mit _runtime/)")
    ap.add_argument("--strict", action="store_true", help="Exit-Code 1 bei Verstößen (Default: 0)")
    ap.add_argument("--install-hook", action="store_true", help="Pre-Build-Hook in build.cmd/build.sh schreiben")
    args = ap.parse_args()

    bundle = args.bundle.resolve()
    if not bundle.is_dir():
        sys.exit(f"FEHLER: Bundle-Ordner nicht gefunden: {bundle}")

    if args.install_hook:
        path = install_hook(bundle)
        print(f"HOOK_OK {path}")
        return

    violations = lint_bundle(bundle)
    if not violations:
        print("LINT_OK keine Verstöße")
        return

    print(f"LINT_VIOLATIONS {len(violations)}")
    for v in violations:
        print(v.format())

    if args.strict:
        sys.exit(1)


if __name__ == "__main__":
    main()
