"""Datenmodelle, Konstanten und Validierungslogik für das EEG-Abrechnungssystem."""

import pandas as pd
from dataclasses import dataclass, field
from datetime import datetime
from typing import List

# ── Geschlecht-Konstanten ──────────────────────────────────────────────────────
SEX_MALE   = "m"
SEX_FEMALE = "w"

# Standard-Datum für uninitialisierte Zeitstempel
DEFAULT_DATE = datetime(1900, 1, 1)

# Spaltennamen für Kassabuch
RECHNUNG_COLUMNS = [
    "", "Rechnungsdatum", "Buchungsdatum", "Buchungstext",
    "Beschreibung", "Name", "Steuertarif", "Rechnungsnummer", "Betrag", "Kassastand"
]

# Unveränderliche Stammdaten des Abrechnungsvereins
_AV_KONSTANTEN = {
    "name":        "Verein zur Abrechnung von Energiegemeinschaften",
    "zvr":         "1530088359",
    "iban":        "AT04 2020 2015 2002 7564",
    "bic":         "301928160",
    "creditor_id": "AT64ZZZ00000086084",
    "adresse":     "Sergius-Pauser-Straße 19, 3340 Waidhofen/Ybbs",
}


@dataclass
class Mitglied:
    """Datensatz eines EEG-Mitglieds mit Zählpunkt-, Verbrauchs- und Rechnungsdaten."""
    ID: str
    Name: str
    Sex: str
    Mail: str
    Strasse: str
    PLZ: str
    Ort: str
    IBAN: str
    BIC: str
    ZP_Consumption: str
    ZP_Generation: str
    Name4Rechnung: str
    Consumption: float = 0.0
    Generation: float = 0.0
    Start_C: datetime = field(default_factory=lambda: DEFAULT_DATE)
    Ende_C:  datetime = field(default_factory=lambda: DEFAULT_DATE)
    Start_G: datetime = field(default_factory=lambda: DEFAULT_DATE)
    Ende_G:  datetime = field(default_factory=lambda: DEFAULT_DATE)
    Kosten: float = 0.0
    Rechnungsnummer: str = ""
    DataCheck: bool = False
    # 0 = Privatperson/leer, 13 = pausch. Landwirt, 20 = Unternehmer
    Steuersatz: int = 0
    # UID-Nummer des Mitglieds (optional; ATU + 8 Ziffern; relevant für RC-Rechnungen)
    UID: str = ""
    # Eintrittsdatum = Datum der Vertragsunterzeichnung = SEPA-Mandatsdatum
    Eintrittsdatum: datetime = field(default_factory=lambda: DEFAULT_DATE)


def validate_member_row(
    member_id=None, vorname=None, nachname=None, mail=None,
    iban=None, bic=None, plz=None, ort=None, strasse=None, sex=None,
    steuersatz=None, uid=None,
):
    """Prüft die Pflichtfelder eines Mitglieds-Datensatzes.

    Gibt einen Fehlertext zurück oder None wenn alles gültig ist.
    Wird sowohl von Abrechnungssystem.validate_member_data als auch vom
    Upload-Validator in app.py verwendet – Regeln bleiben an einer Stelle.
    """
    def _str(v):
        if v is None:
            return ""
        try:
            if pd.isna(v):
                return ""
        except (TypeError, ValueError):
            pass
        return str(v).strip()

    member_id   = _str(member_id)
    vorname     = _str(vorname)
    nachname    = _str(nachname)
    mail        = _str(mail)
    iban_clean  = _str(iban).replace(" ", "")
    bic_clean   = _str(bic).replace(" ", "")
    plz_str     = _str(plz)
    ort_str     = _str(ort)
    strasse_str = _str(strasse)
    sex_str     = _str(sex)

    name = f"{vorname} {nachname}".strip() or "(ohne Namen)"

    required_fields = [
        ("ID",       member_id),
        ("Vorname",  vorname),
        ("Nachname", nachname),
        ("E-Mail",   mail),
        ("IBAN",     iban_clean),
        ("BIC",      bic_clean),
        ("PLZ",      plz_str),
        ("Ort",      ort_str),
        ("Adresse",  strasse_str),
    ]
    for field_label, field_value in required_fields:
        if not field_value:
            return f"{field_label} fehlt für Mitglied {name}"

    # ID muss positive Ganzzahl sein
    try:
        _id_int = int(float(member_id))
        if _id_int <= 0 or abs(float(member_id) - _id_int) > 1e-9:
            raise ValueError
    except (ValueError, TypeError):
        return (f"ID muss eine positive Ganzzahl sein (z.B. 1, 2, 3) "
                f"für Mitglied {name}: '{member_id}'")

    if len(iban_clean) < 15 or not iban_clean.isalnum():
        return f"Ungültige IBAN für Mitglied {name} (Format stimmt nicht)"

    if len(bic_clean) not in (8, 11) or not bic_clean.isalnum() or not bic_clean.isupper():
        return f"Ungültiger BIC für Mitglied {name} (muss 8 oder 11 Zeichen, Großbuchstaben)"

    if "@" not in mail:
        return f"Ungültige E-Mail für Mitglied {name} (kein '@' enthalten)"

    if len(plz_str) != 4 or not plz_str.isdigit():
        return f"Ungültige PLZ für Mitglied {name} (4 Ziffern erwartet)"

    if sex_str and sex_str.lower() not in ("m", "w"):
        return (f"Ungültiges Geschlecht für Mitglied {name}: "
                f"'{sex_str}' (erwartet: 'm' oder 'w' oder leer)")

    steuer_str = _str(steuersatz)
    if steuer_str:
        try:
            s_val = int(float(steuer_str.replace(",", ".")))
        except (ValueError, TypeError):
            return (f"Ungültiger Steuersatz für Mitglied {name}: "
                    f"'{steuer_str}' (erlaubt: 0, 13, 20 oder leer)")
        if s_val not in (0, 13, 20):
            return (f"Ungültiger Steuersatz für Mitglied {name}: "
                    f"{s_val} (erlaubt: 0, 13, 20 oder leer)")

    uid_str = _str(uid)
    if uid_str:
        uid_clean = uid_str.upper().replace(" ", "")
        if not (uid_clean.startswith("ATU") and len(uid_clean) == 11 and uid_clean[3:].isdigit()):
            return (f"Ungültige UID-Nummer für Mitglied {name}: "
                    f"'{uid_str}' (Format: ATU + 8 Ziffern, z. B. ATU12345678)")

    return None


def build_ust_hints(
    kleinunternehmer: bool,
    verein_uid: str,
    mitglied_steuersatz: int,
    has_bezug: bool = True,
    has_einspeisung: bool = True,
) -> tuple:
    """Gibt (bezug_hint, einspeisung_hint) als Strings zurück – leer = Zeile wird ausgeblendet.

    BEZUG: KU → § 6 Abs. 1 Z 27 UStG; Regelbesteuerung → 20% USt enthalten.
    EINSPEISUNG: Steuersatz 0 = kein/KU-Hinweis; 20 = Reverse Charge; 13 = § 22 UStG.
    """
    # ── BEZUG ─────────────────────────────────────────────────────────────
    if not has_bezug:
        bezug_hint = ""
    elif kleinunternehmer:
        bezug_hint = (
            "Umsatzsteuerbefreit gemäß § 6 Abs. 1 Z 27 UStG "
            "(Kleinunternehmerregelung)."
        )
    else:
        uid = (verein_uid or "").strip()
        bezug_hint = (
            f"Im Bezugspreis sind 20% Umsatzsteuer enthalten "
            f"(UID-Nr.: {uid})."
        )

    # ── EINSPEISUNG ───────────────────────────────────────────────────────
    if not has_einspeisung:
        einspeisung_hint = ""
    elif mitglied_steuersatz == 20:
        einspeisung_hint = (
            "Übergang der Steuerschuld (Reverse Charge) gemäß § 19 Abs. 1c "
            "UStG, 20% Umsatzsteuer – zu entrichten durch den Leistungsempfänger."
        )
    elif mitglied_steuersatz == 13:
        einspeisung_hint = (
            "Durchschnittssteuersatz 13% gemäß § 22 UStG (pauschalierter Forst- und "
            "Landwirtschaftsbetrieb)."
        )
    else:
        # Steuersatz 0: Privatperson
        if kleinunternehmer:
            einspeisung_hint = (
                "Umsatzsteuerbefreit – Einspeisung durch Privatperson "
                "(kein Unternehmer i. S. d. UStG)."
            )
        else:
            einspeisung_hint = ""

    return bezug_hint, einspeisung_hint
