"""
Globale Feld-Extraktoren mit Kaskaden-Regex
Werden automatisch als Fallback verwendet wenn regel-spezifische Muster nicht greifen
"""
import re
from datetime import datetime
from typing import Optional, List, Dict, Any
import logging

logger = logging.getLogger(__name__)


# ============ DATUM ============
DATUM_MUSTER = [
    # Mit Kontext (zuverlässiger)
    {"regex": r"Rechnungsdatum[:\s]*(\d{2})[./](\d{2})[./](\d{4})", "order": "dmy"},
    {"regex": r"Belegdatum[:\s]*(\d{2})[./](\d{2})[./](\d{4})", "order": "dmy"},
    {"regex": r"Datum[:\s]*(\d{2})[./](\d{2})[./](\d{4})", "order": "dmy"},
    {"regex": r"Date[:\s]*(\d{2})[./](\d{2})[./](\d{4})", "order": "dmy"},
    {"regex": r"vom[:\s]*(\d{2})[./](\d{2})[./](\d{4})", "order": "dmy"},

    # ISO Format
    {"regex": r"(\d{4})-(\d{2})-(\d{2})", "order": "ymd"},

    # Deutsches Format ohne Kontext
    {"regex": r"(\d{2})\.(\d{2})\.(\d{4})", "order": "dmy"},
    {"regex": r"(\d{2})/(\d{2})/(\d{4})", "order": "dmy"},

    # Amerikanisches Format
    {"regex": r"(\d{2})/(\d{2})/(\d{4})", "order": "mdy"},

    # Ausgeschriebene Monate
    {"regex": r"(\d{1,2})\.\s*(Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)\s*(\d{4})", "order": "dMy"},
    {"regex": r"(\d{1,2})\s+(Jan|Feb|Mär|Apr|Mai|Jun|Jul|Aug|Sep|Okt|Nov|Dez)[a-z]*\.?\s+(\d{4})", "order": "dMy"},
]

MONATE_DE = {
    "januar": 1, "februar": 2, "märz": 3, "april": 4, "mai": 5, "juni": 6,
    "juli": 7, "august": 8, "september": 9, "oktober": 10, "november": 11, "dezember": 12,
    "jan": 1, "feb": 2, "mär": 3, "apr": 4, "jun": 6, "jul": 7, "aug": 8, "sep": 9, "okt": 10, "nov": 11, "dez": 12
}


def extrahiere_datum(text: str, spezifische_muster: List[Dict] = None) -> Optional[str]:
    """
    Extrahiert Datum aus Text mit Kaskaden-Ansatz
    Returns: ISO Format YYYY-MM-DD oder None
    """
    muster_liste = (spezifische_muster or []) + DATUM_MUSTER

    for muster in muster_liste:
        try:
            match = re.search(muster["regex"], text, re.IGNORECASE)
            if match:
                groups = match.groups()
                order = muster.get("order", "dmy")

                if order == "dmy":
                    tag, monat, jahr = int(groups[0]), int(groups[1]), int(groups[2])
                elif order == "ymd":
                    jahr, monat, tag = int(groups[0]), int(groups[1]), int(groups[2])
                elif order == "mdy":
                    monat, tag, jahr = int(groups[0]), int(groups[1]), int(groups[2])
                elif order == "dMy":
                    tag = int(groups[0])
                    monat = MONATE_DE.get(groups[1].lower(), 1)
                    jahr = int(groups[2])
                else:
                    continue

                # Validierung
                if 1 <= tag <= 31 and 1 <= monat <= 12 and 1900 <= jahr <= 2100:
                    return f"{jahr:04d}-{monat:02d}-{tag:02d}"
        except Exception as e:
            logger.debug(f"Datum-Extraktion fehlgeschlagen: {e}")
            continue

    return None


# ============ BETRAG ============
BETRAG_MUSTER = [
    # Mit Kontext (zuverlässiger)
    {"regex": r"Gesamtbetrag[:\s]*([\d.,]+)\s*(?:EUR|€)?", "context": True},
    {"regex": r"Rechnungsbetrag[:\s]*([\d.,]+)\s*(?:EUR|€)?", "context": True},
    {"regex": r"Endbetrag[:\s]*([\d.,]+)\s*(?:EUR|€)?", "context": True},
    {"regex": r"Summe[:\s]*([\d.,]+)\s*(?:EUR|€)?", "context": True},
    {"regex": r"Total[:\s]*([\d.,]+)\s*(?:EUR|€)?", "context": True},
    {"regex": r"Brutto[:\s]*([\d.,]+)\s*(?:EUR|€)?", "context": True},
    {"regex": r"zu zahlen[:\s]*([\d.,]+)\s*(?:EUR|€)?", "context": True},
    {"regex": r"Zahlbetrag[:\s]*([\d.,]+)\s*(?:EUR|€)?", "context": True},

    # Mit Währung (weniger zuverlässig)
    {"regex": r"([\d.,]+)\s*(?:EUR|€)", "context": False},
    {"regex": r"€\s*([\d.,]+)", "context": False},
]


def extrahiere_betrag(text: str, spezifische_muster: List[Dict] = None) -> Optional[str]:
    """
    Extrahiert Betrag aus Text mit Kaskaden-Ansatz
    Returns: Formatierter Betrag (z.B. "1234,56") oder None
    """
    muster_liste = (spezifische_muster or []) + BETRAG_MUSTER

    for muster in muster_liste:
        try:
            match = re.search(muster["regex"], text, re.IGNORECASE)
            if match:
                betrag_str = match.group(1)
                betrag = _parse_betrag(betrag_str)
                if betrag is not None and betrag > 0:
                    # Formatierung: Ganzzahl wenn möglich, sonst 2 Dezimalstellen
                    if betrag == int(betrag):
                        return str(int(betrag))
                    return f"{betrag:.2f}".replace(".", ",")
        except Exception as e:
            logger.debug(f"Betrag-Extraktion fehlgeschlagen: {e}")
            continue

    return None


def _parse_betrag(betrag_str: str) -> Optional[float]:
    """Parst Betrag-String zu Float"""
    betrag_str = betrag_str.strip()

    # Leerzeichen entfernen
    betrag_str = betrag_str.replace(" ", "")

    # Deutsches Format: 1.234,56 -> 1234.56
    if "," in betrag_str and "." in betrag_str:
        if betrag_str.rfind(",") > betrag_str.rfind("."):
            # Deutsches Format
            betrag_str = betrag_str.replace(".", "").replace(",", ".")
        else:
            # Englisches Format
            betrag_str = betrag_str.replace(",", "")
    elif "," in betrag_str:
        # Nur Komma: deutsches Dezimaltrennzeichen
        betrag_str = betrag_str.replace(",", ".")

    try:
        return float(betrag_str)
    except:
        return None


# ============ RECHNUNGSNUMMER ============
NUMMER_MUSTER = [
    # Mit Kontext
    {"regex": r"Rechnungsnummer[:\s#]*([A-Z0-9][\w\-/]+)", "context": True},
    {"regex": r"Rechnung\s*Nr\.?[:\s#]*([A-Z0-9][\w\-/]+)", "context": True},
    {"regex": r"Rechnungs-Nr\.?[:\s#]*([A-Z0-9][\w\-/]+)", "context": True},
    {"regex": r"Invoice\s*(?:No\.?|Number)?[:\s#]*([A-Z0-9][\w\-/]+)", "context": True},
    {"regex": r"Beleg-?Nr\.?[:\s#]*([A-Z0-9][\w\-/]+)", "context": True},
    {"regex": r"Dokumentnummer[:\s#]*([A-Z0-9][\w\-/]+)", "context": True},
    {"regex": r"Bestell-?Nr\.?[:\s#]*([A-Z0-9][\w\-/]+)", "context": True},
    {"regex": r"Auftrags-?Nr\.?[:\s#]*([A-Z0-9][\w\-/]+)", "context": True},

    # Typische Formate ohne Kontext
    {"regex": r"RE-?(\d{4,})", "context": False},
    {"regex": r"INV-?(\d{4,})", "context": False},
]


def extrahiere_nummer(text: str, spezifische_muster: List[Dict] = None) -> Optional[str]:
    """
    Extrahiert Rechnungs-/Belegnummer aus Text
    """
    muster_liste = (spezifische_muster or []) + NUMMER_MUSTER

    for muster in muster_liste:
        try:
            match = re.search(muster["regex"], text, re.IGNORECASE)
            if match:
                nummer = match.group(1).strip()
                if len(nummer) >= 3:  # Mindestens 3 Zeichen
                    return nummer
        except Exception as e:
            logger.debug(f"Nummer-Extraktion fehlgeschlagen: {e}")
            continue

    return None


# ============ FIRMA/ABSENDER ============
FIRMA_MUSTER = [
    # Absender-Zeile
    {"regex": r"^([A-ZÄÖÜ][A-Za-zäöüÄÖÜß\s&\-\.]+(?:GmbH|AG|KG|e\.K\.|Inc|Ltd|SE|UG))", "context": True},
    {"regex": r"Absender[:\s]*([A-Za-zäöüÄÖÜß\s&\-\.]+)", "context": True},
    {"regex": r"Von[:\s]*([A-Za-zäöüÄÖÜß\s&\-\.]+)", "context": True},
]

# Bekannte Firmen (werden im Text gesucht)
BEKANNTE_FIRMEN = [
    "Sonepar", "Amazon", "Ebay", "MediaMarkt", "Saturn", "Conrad", "Reichelt",
    "Hornbach", "Bauhaus", "OBI", "Hagebau", "Toom", "Hellweg",
    "Telekom", "Vodafone", "O2", "1&1",
    "Allianz", "HUK", "Provinzial", "DEVK", "Gothaer",
    "IKEA", "Poco", "XXXLutz", "Roller",
    "Alternate", "Mindfactory", "Caseking", "Notebooksbilliger",
    "DHL", "DPD", "Hermes", "UPS", "GLS",
]


def extrahiere_firma(text: str, absender_email: str = "", spezifische_muster: List[Dict] = None) -> Optional[str]:
    """
    Extrahiert Firmennamen aus Text oder E-Mail-Absender
    """
    text_lower = text.lower()

    # 1. Bekannte Firmen im Text suchen
    for firma in BEKANNTE_FIRMEN:
        if firma.lower() in text_lower:
            return firma

    # 2. Aus E-Mail-Domain extrahieren
    if absender_email:
        match = re.search(r"@([\w\-]+)\.", absender_email)
        if match:
            domain = match.group(1)
            # Bekannte Domain-Namen kapitalisieren
            for firma in BEKANNTE_FIRMEN:
                if firma.lower() == domain.lower():
                    return firma
            return domain.capitalize()

    # 3. Regex-Muster
    muster_liste = (spezifische_muster or []) + FIRMA_MUSTER
    for muster in muster_liste:
        try:
            match = re.search(muster["regex"], text, re.MULTILINE)
            if match:
                firma = match.group(1).strip()
                if len(firma) >= 2:
                    return firma
        except:
            continue

    return None


# ============ DOKUMENTTYP ============
DOKUMENTTYP_KEYWORDS = {
    "Rechnung": ["rechnung", "invoice", "faktura", "bill"],
    "Angebot": ["angebot", "quotation", "quote", "offerte"],
    "Gutschrift": ["gutschrift", "credit note", "erstattung"],
    "Mahnung": ["mahnung", "zahlungserinnerung", "payment reminder"],
    "Lieferschein": ["lieferschein", "delivery note", "packing slip"],
    "Auftragsbestätigung": ["auftragsbestätigung", "order confirmation", "bestellbestätigung"],
    "Vertrag": ["vertrag", "contract", "vereinbarung"],
    "Versicherungsschein": ["versicherungsschein", "police", "versicherungspolice"],
    "Zeugnis": ["zeugnis", "certificate", "zertifikat"],
    "Bescheinigung": ["bescheinigung", "nachweis", "bestätigung"],
    "Kontoauszug": ["kontoauszug", "account statement", "bankbeleg"],
    "Beitragsrechnung": ["beitragsrechnung", "beitragsberechnung", "mitgliedsbeitrag"],
}


def extrahiere_dokumenttyp(text: str, dateiname: str = "") -> Optional[str]:
    """
    Erkennt den Dokumenttyp anhand von Keywords
    """
    text_lower = text.lower() + " " + dateiname.lower()

    for typ, keywords in DOKUMENTTYP_KEYWORDS.items():
        for keyword in keywords:
            if keyword in text_lower:
                return typ

    return None


# ============ HAUPTFUNKTION ============
def extrahiere_alle_felder(text: str, dokument_info: Dict = None,
                           regel_extraktion: Dict = None) -> Dict[str, Any]:
    """
    Extrahiert alle verfügbaren Felder aus einem Dokument

    Args:
        text: Der extrahierte Text aus dem PDF
        dokument_info: Zusätzliche Infos (absender, original_name, etc.)
        regel_extraktion: Spezifische Extraktionsregeln aus der Regel

    Returns:
        Dict mit allen extrahierten Feldern
    """
    dokument_info = dokument_info or {}
    regel_extraktion = regel_extraktion or {}

    felder = {}

    # Datum
    datum_muster = regel_extraktion.get("datum", {}).get("muster", [])
    datum = extrahiere_datum(text, datum_muster if isinstance(datum_muster, list) else None)
    if datum:
        felder["datum"] = datum

    # Betrag
    betrag_muster = regel_extraktion.get("betrag", {}).get("muster", [])
    betrag = extrahiere_betrag(text, betrag_muster if isinstance(betrag_muster, list) else None)
    if betrag:
        felder["betrag"] = betrag

    # Nummer
    nummer_muster = regel_extraktion.get("nummer", {}).get("muster", [])
    nummer = extrahiere_nummer(text, nummer_muster if isinstance(nummer_muster, list) else None)
    if nummer:
        felder["nummer"] = nummer

    # Firma
    absender = dokument_info.get("absender", "")
    firma = extrahiere_firma(text, absender)
    if firma:
        felder["firma"] = firma

    # Dokumenttyp
    dateiname = dokument_info.get("original_name", "")
    typ = extrahiere_dokumenttyp(text, dateiname)
    if typ:
        felder["typ"] = typ

    # Statische Werte aus Regel übernehmen
    for feld_name, feld_config in regel_extraktion.items():
        if isinstance(feld_config, dict) and "wert" in feld_config:
            felder[feld_name] = feld_config["wert"]

    return felder


# ============ SCHEMA-BUILDER ============
def baue_dateiname(schema: str, felder: Dict[str, Any], endung: str = ".pdf") -> str:
    """
    Baut Dateinamen aus Schema und Feldern.
    Entfernt automatisch Platzhalter und deren Trennzeichen wenn Feld fehlt.

    Schema-Beispiel: "{datum} - {typ} - {firma} - {nummer} - {betrag} EUR"
    Mit felder = {datum: "2026-10-01", typ: "Rechnung", firma: "Sonepar"}
    Ergebnis: "2026-10-01 - Rechnung - Sonepar.pdf"
    """
    # Schema ohne Endung verarbeiten
    if schema.lower().endswith(".pdf"):
        schema = schema[:-4]

    # Platzhalter ersetzen
    result = schema
    for key, value in felder.items():
        placeholder = "{" + key + "}"
        if placeholder in result and value:
            result = result.replace(placeholder, str(value))

    # Nicht ersetzte Platzhalter und ihre Trennzeichen entfernen
    # Muster: " - {feld}" oder "{feld} - " oder "{feld}"
    result = re.sub(r'\s*-\s*\{[^}]+\}', '', result)
    result = re.sub(r'\{[^}]+\}\s*-\s*', '', result)
    result = re.sub(r'\{[^}]+\}', '', result)

    # Aufräumen: Doppelte Trennzeichen, Leerzeichen
    result = re.sub(r'\s*-\s*-\s*', ' - ', result)
    result = re.sub(r'\s+', ' ', result)
    result = result.strip(' -')

    # Ungültige Zeichen entfernen
    invalid_chars = '<>:"/\\|?*'
    for char in invalid_chars:
        result = result.replace(char, "_")

    # Endung anhängen
    if not result:
        result = "Dokument"

    return result + endung
