"""
Sorter Modul
Regel-basierte Erkennung und Benennung von Dokumenten
"""
import re
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Optional, Any
import logging
import shutil

from .extraktoren import extrahiere_alle_felder, baue_dateiname

logger = logging.getLogger(__name__)


class Sorter:
    """Sortiert und benennt Dokumente basierend auf Regeln"""

    def __init__(self, regeln: List[Dict]):
        """
        Args:
            regeln: Liste von Regel-Dicts, sortiert nach Priorität
        """
        # Nach Priorität sortieren (niedrig = wichtig)
        self.regeln = sorted(regeln, key=lambda r: r.get("prioritaet", 100))

    def finde_passende_regel(self, dokument_info: Dict) -> Optional[Dict]:
        """
        Findet die erste passende Regel für ein Dokument

        Args:
            dokument_info: Dict mit text, original_name, absender, etc.

        Returns:
            Passende Regel oder None
        """
        for regel in self.regeln:
            if not regel.get("aktiv", True):
                continue

            muster = regel.get("muster", {})
            if self._pruefe_muster(muster, dokument_info):
                logger.info(f"Regel '{regel.get('name')}' matched für {dokument_info.get('original_name')}")
                return regel

        return None

    def _pruefe_muster(self, muster: Dict, dokument_info: Dict) -> bool:
        """Prüft ob alle Muster auf das Dokument zutreffen"""
        text = dokument_info.get("text", "").lower()
        original_name = dokument_info.get("original_name", "").lower()
        absender = dokument_info.get("absender", "").lower()

        # keywords (einfache Komma-getrennte Liste - für UI)
        if "keywords" in muster:
            keywords = muster["keywords"]
            if isinstance(keywords, str):
                keywords = [k.strip() for k in keywords.split(",")]
            # Alle Keywords müssen vorkommen
            for keyword in keywords:
                keyword = keyword.lower().strip()
                if keyword and keyword not in text and keyword not in original_name:
                    return False

        # absender_contains
        if "absender_contains" in muster:
            if muster["absender_contains"].lower() not in absender:
                return False

        # dateiname_match
        if "dateiname_match" in muster:
            pattern = muster["dateiname_match"]
            if isinstance(pattern, str):
                if pattern.lower() not in original_name:
                    return False
            elif isinstance(pattern, list):
                if not any(p.lower() in original_name for p in pattern):
                    return False

        # text_match (alle müssen enthalten sein)
        if "text_match" in muster:
            patterns = muster["text_match"]
            if isinstance(patterns, str):
                patterns = [patterns]
            for pattern in patterns:
                if pattern.lower() not in text:
                    return False

        # text_match_any (mindestens einer muss enthalten sein)
        if "text_match_any" in muster:
            patterns = muster["text_match_any"]
            if isinstance(patterns, str):
                patterns = [patterns]
            if not any(p.lower() in text for p in patterns):
                return False

        # text_regex
        if "text_regex" in muster:
            pattern = muster["text_regex"]
            if not re.search(pattern, text, re.IGNORECASE):
                return False

        return True

    def extrahiere_felder(self, regel: Dict, dokument_info: Dict) -> Dict[str, Any]:
        """
        Extrahiert Felder aus dem Dokument - nutzt globale Extraktoren mit Fallbacks

        Returns:
            Dict mit extrahierten Werten
        """
        text = dokument_info.get("text", "")
        regel_extraktion = regel.get("extraktion", {})

        # Globale Extraktoren nutzen (mit Regel-spezifischen Überschreibungen)
        felder = extrahiere_alle_felder(text, dokument_info, regel_extraktion)

        # Regel-spezifische statische Werte überschreiben
        for feld_name, feld_config in regel_extraktion.items():
            if isinstance(feld_config, dict):
                if "wert" in feld_config:
                    felder[feld_name] = feld_config["wert"]
                elif "regex" in feld_config:
                    # Einzelnes Regex aus Regel
                    wert = self._extrahiere_mit_regex(feld_config, text)
                    if wert:
                        felder[feld_name] = wert
            elif isinstance(feld_config, str):
                # Direkter statischer Wert
                felder[feld_name] = feld_config

        return felder

    def _extrahiere_mit_regex(self, config: Dict, text: str) -> Optional[str]:
        """Extrahiert ein Feld mit einem einzelnen Regex"""
        try:
            match = re.search(config["regex"], text, re.IGNORECASE | re.MULTILINE)
            if match:
                wert = match.group(1) if match.groups() else match.group(0)

                # Datum formatieren
                if "format" in config:
                    try:
                        datum = datetime.strptime(wert.strip(), config["format"])
                        return datum.strftime("%Y-%m-%d")
                    except:
                        pass

                # Betrag formatieren
                if config.get("typ") == "betrag":
                    wert = self._formatiere_betrag(wert)

                return wert.strip()
        except Exception as e:
            logger.debug(f"Regex-Extraktion fehlgeschlagen: {e}")

        return None

    def _formatiere_betrag(self, betrag: str) -> str:
        """Formatiert Betrag einheitlich"""
        betrag = betrag.replace(" ", "").replace(".", "").replace(",", ".")

        try:
            wert = float(betrag)
            if wert == int(wert):
                return str(int(wert))
            return f"{wert:.2f}".replace(".", ",")
        except:
            return betrag

    def generiere_dateinamen(self, regel: Dict, extrahierte_felder: Dict) -> str:
        """
        Generiert den neuen Dateinamen basierend auf Schema
        Nutzt den intelligenten Schema-Builder der fehlende Felder entfernt
        """
        schema = regel.get("schema", "{datum} - Dokument.pdf")
        return baue_dateiname(schema, extrahierte_felder, ".pdf")

    def verschiebe_datei(self, quell_pfad: str, ziel_ordner: str, neuer_name: str) -> str:
        """
        Verschiebt und benennt Datei um

        Returns:
            Neuer Pfad der Datei
        """
        ziel_dir = Path(ziel_ordner)
        ziel_dir.mkdir(parents=True, exist_ok=True)

        ziel_pfad = ziel_dir / neuer_name

        # Eindeutigen Namen sicherstellen
        counter = 1
        original_name = ziel_pfad.stem
        suffix = ziel_pfad.suffix
        while ziel_pfad.exists():
            ziel_pfad = ziel_dir / f"{original_name} ({counter}){suffix}"
            counter += 1

        # Verschieben
        shutil.move(quell_pfad, ziel_pfad)
        logger.info(f"Verschoben: {quell_pfad} -> {ziel_pfad}")

        return str(ziel_pfad)


# ============ STANDARD-DOKUMENTTYPEN ============
# Diese werden für das einfache UI verwendet

DOKUMENTTYPEN = {
    "rechnung": {
        "name": "Rechnung",
        "keywords": ["rechnung", "invoice"],
        "schema": "{datum} - Rechnung - {firma} - {nummer} - {betrag} EUR.pdf",
        "unterordner": "rechnungen"
    },
    "angebot": {
        "name": "Angebot",
        "keywords": ["angebot", "quotation", "offerte"],
        "schema": "{datum} - Angebot - {firma} - {nummer} - {betrag} EUR.pdf",
        "unterordner": "angebote"
    },
    "gutschrift": {
        "name": "Gutschrift",
        "keywords": ["gutschrift", "credit"],
        "schema": "{datum} - Gutschrift - {firma} - {nummer} - {betrag} EUR.pdf",
        "unterordner": "gutschriften"
    },
    "lieferschein": {
        "name": "Lieferschein",
        "keywords": ["lieferschein", "delivery"],
        "schema": "{datum} - Lieferschein - {firma} - {nummer}.pdf",
        "unterordner": "lieferscheine"
    },
    "auftragsbestaetigung": {
        "name": "Auftragsbestätigung",
        "keywords": ["auftragsbestätigung", "bestellbestätigung"],
        "schema": "{datum} - Auftragsbestätigung - {firma} - {nummer}.pdf",
        "unterordner": "auftraege"
    },
    "vertrag": {
        "name": "Vertrag",
        "keywords": ["vertrag", "contract"],
        "schema": "Vertrag - {firma} - {nummer} - {datum}.pdf",
        "unterordner": "vertraege"
    },
    "versicherung": {
        "name": "Versicherung",
        "keywords": ["versicherung", "police", "beitrag"],
        "schema": "Versicherung - {firma} - {nummer} - {datum}.pdf",
        "unterordner": "versicherungen"
    },
    "zeugnis": {
        "name": "Zeugnis",
        "keywords": ["zeugnis", "zertifikat"],
        "schema": "Zeugnis - {firma} - {nummer} - {datum}.pdf",
        "unterordner": "zeugnisse"
    },
    "bescheinigung": {
        "name": "Bescheinigung",
        "keywords": ["bescheinigung", "nachweis", "bestätigung"],
        "schema": "Bescheinigung - {firma} - {nummer} - {datum}.pdf",
        "unterordner": "bescheinigungen"
    },
    "kontoauszug": {
        "name": "Kontoauszug",
        "keywords": ["kontoauszug", "account statement"],
        "schema": "{datum} - Kontoauszug - {firma} - {nummer}.pdf",
        "unterordner": "kontoauszuege"
    },
    "sonstiges": {
        "name": "Sonstiges",
        "keywords": [],
        "schema": "{datum} - {typ} - {firma}.pdf",
        "unterordner": "sonstiges"
    }
}


def erstelle_einfache_regel(name: str, dokumenttyp: str, keywords: str,
                            firma_wert: str = None, unterordner: str = None,
                            prioritaet: int = 50) -> Dict:
    """
    Erstellt eine einfache Regel basierend auf Dokumenttyp

    Args:
        name: Name der Regel (z.B. "Sonepar Rechnung")
        dokumenttyp: Typ aus DOKUMENTTYPEN
        keywords: Komma-getrennte Keywords zur Erkennung
        firma_wert: Optionaler fester Firmenwert
        unterordner: Optionaler Unterordner (überschreibt Standard)
        prioritaet: Priorität (niedriger = wichtiger)

    Returns:
        Regel-Dict für die Datenbank
    """
    typ_config = DOKUMENTTYPEN.get(dokumenttyp, DOKUMENTTYPEN["sonstiges"])

    regel = {
        "name": name,
        "prioritaet": prioritaet,
        "aktiv": True,
        "muster": {
            "keywords": keywords
        },
        "extraktion": {},
        "schema": typ_config["schema"],
        "unterordner": unterordner or typ_config["unterordner"]
    }

    # Feste Firma wenn angegeben
    if firma_wert:
        regel["extraktion"]["firma"] = {"wert": firma_wert}

    return regel


def liste_dokumenttypen() -> List[Dict]:
    """Gibt Liste aller Dokumenttypen für UI zurück"""
    return [
        {"id": key, "name": config["name"], "schema": config["schema"]}
        for key, config in DOKUMENTTYPEN.items()
    ]
