import csv
import re
from collections import Counter
from pathlib import Path

import config

_COLLECTIVE = config.COLLECTIVE_TERMS

_GERMAN_STOP_WORDS = {
    "der", "die", "das", "ein", "eine", "einer", "einen", "einem", "eines",
    "und", "oder", "aber", "an", "in", "auf", "für", "mit", "von", "zu",
    "bei", "nach", "vor", "aus", "ist", "sind", "war", "waren", "hat",
    "haben", "wird", "werden", "ich", "du", "er", "sie", "es", "wir",
    "ihr", "ihn", "ihm", "ihnen", "mich", "mir", "dich", "dir",
    "ihre", "ihren", "seinem", "seinen", "seiner", "seine",
    "auch", "nicht", "noch", "dann", "durch", "dem", "den",
    "des", "als", "wie", "dass", "um", "über", "unter", "zwischen",
    "all", "alle", "was", "wer", "wo", "wann", "welche", "welcher",
    "mehr", "sehr", "nur", "schon", "dabei", "dazu",
    "bis", "seit", "gegen", "ohne", "doch", "wenn", "weil",
    "ob", "so", "da", "dort", "hier", "nun", "ja", "nein",
    "ihrer", "ihrem",
    # Contracted prepositions common in German Inhalt summaries
    "im", "am", "ans", "ins", "zum", "zur", "vom", "beim", "sich",
    "hat", "hatte", "wird", "wurde", "wurden", "worden",
    "kann", "konnte", "soll", "sollte", "will", "wollte",
    "ihm", "dieses", "dieser", "diesem", "diesen",
}


def _is_correspondence(raw: str) -> bool:
    lower = raw.lower()
    return " an " in lower or lower.startswith("an ") or ".an " in lower


def _tokenize(text: str) -> list[str]:
    return [t.lower() for t in re.findall(r"[a-zA-ZäöüÄÖÜß]+", text)]


def _has_collective(tokens: list[str]) -> bool:
    return any(t in _COLLECTIVE for t in tokens)


def classify_schlagwort(raw: str) -> list[str]:
    if not raw or not raw.strip():
        return []
    if not _is_correspondence(raw):
        return [f"Themen/{raw}"]
    if _has_collective(_tokenize(raw)):
        return [f"Briefwechsel/{raw}"]
    return []


def mine_summary_candidates(summaries: list[str]) -> list[tuple[str, int]]:
    counter: Counter = Counter()
    for summary in summaries:
        for token in re.split(r"[,;\s]+", summary.lower()):
            token = re.sub(r"[^a-zA-ZäöüÄÖÜß]", "", token)
            if len(token) >= 2 and token not in _GERMAN_STOP_WORDS:
                counter[token] += 1
    return counter.most_common()


def load_approved_themes(path: Path) -> set[str]:
    if not path.exists():
        return set()
    themes: set[str] = set()
    with open(path, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            if row.get("candidate"):
                themes.add(row["candidate"].strip().lower())
    return themes


def apply_approved_themes(summary: str, themes: set[str]) -> list[str]:
    lower = summary.lower()
    return [
        f"Themen/{theme}"
        for theme in themes
        if re.search(r"\b" + re.escape(theme) + r"\b", lower)
    ]


def generate_tags(schlagwort: str, summary: str, themes: set[str]) -> list[str]:
    result = classify_schlagwort(schlagwort or "")
    if summary and themes:
        result = result + apply_approved_themes(summary, themes)
    return result


def encode_tags(tag_list: list[str]) -> str:
    return "|".join(tag_list)


def build_tag_tree(all_tag_paths: list[str]) -> list[dict]:
    unique_paths = list(dict.fromkeys(all_tag_paths))
    roots: dict[str, None] = {}
    children: dict[str, tuple[str, str]] = {}
    for path in unique_paths:
        if "/" in path:
            parent, child = path.split("/", 1)
            roots[parent] = None
            children[path] = (parent, child)
        else:
            roots[path] = None

    rows: list[dict] = []
    seen: set[str] = set()
    for root in roots:
        if root not in seen:
            rows.append({"tag_path": root, "parent_name": "", "tag_name": root})
            seen.add(root)
    for path, (parent, child) in children.items():
        if path not in seen:
            rows.append({"tag_path": path, "parent_name": parent, "tag_name": child})
            seen.add(path)
    return rows
