"""
Validierung & Konstanten für persönliche Referenzwerte.
"""
from __future__ import annotations

from typing import Any, Optional

from fastapi import HTTPException

REF_VALUE_SOURCES = frozenset(
    {
        "manual_user",
        "manual_admin",
        "import_device",
        "import_app",
        "derived_system",
        "estimated_system",
        "test_entry",
    }
)

REF_VALUE_METHODS = frozenset(
    {
        "direct_measurement",
        "lab_test",
        "field_test",
        "questionnaire",
        "formula_estimation",
        "trend_analysis",
        "device_algorithm",
        "manual_assessment",
        "imported_external",
        "unknown",
    }
)


REF_VALUE_CONFIDENCE = frozenset({"high", "medium", "low", "unknown"})

# Anzeigereihenfolge (nicht alphabetisch)
REF_VALUE_CONFIDENCE_ORDER = ("high", "medium", "low", "unknown")

VALUE_DATA_TYPES = frozenset({"integer", "decimal", "percentage", "text", "enum"})


def _rules_dict(raw: Any) -> dict:
    if not raw:
        return {}
    if isinstance(raw, dict):
        return raw
    return {}


def validate_meta_source(source: Optional[str]) -> str:
    if not source or not str(source).strip():
        raise HTTPException(400, "Quelle (source) ist erforderlich.")
    s = str(source).strip()
    if s not in REF_VALUE_SOURCES:
        raise HTTPException(400, f"Ungültige Quelle: {s}")
    return s


def validate_meta_method(method: Optional[str]) -> str:
    if not method or not str(method).strip():
        raise HTTPException(400, "Methode (method) ist erforderlich.")
    m = str(method).strip()
    if m not in REF_VALUE_METHODS:
        raise HTTPException(400, f"Ungültige Methode: {m}")
    return m


def validate_meta_confidence(confidence: Optional[str]) -> str:
    if not confidence or not str(confidence).strip():
        raise HTTPException(400, "Vertrauensgrad (confidence) ist erforderlich.")
    c = str(confidence).strip().lower()
    if c not in REF_VALUE_CONFIDENCE:
        raise HTTPException(400, f"Ungültiger Vertrauensgrad: {c}")
    return c


def resolve_unit_from_type(default_unit: Optional[str]) -> str:
    u = (default_unit or "").strip()
    if not u:
        raise HTTPException(
            400,
            "Für diesen Kennwert-Typ ist keine Einheit hinterlegt. Bitte im Admin einen Standard unter „Standard-Einheit“ setzen.",
        )
    return u


def validate_value_for_data_type(
    value_data_type: str,
    validation_rules_raw: Any,
    value_numeric: Optional[float],
    value_text: Optional[str],
) -> tuple[Optional[float], Optional[str]]:
    """
    Je nach value_data_type den Wert prüfen und (value_numeric, value_text) für die DB liefern.
    """
    vdt = (value_data_type or "decimal").strip().lower()
    if vdt not in VALUE_DATA_TYPES:
        raise HTTPException(400, f"Ungültiger interner Datentyp: {vdt}")

    rules = _rules_dict(validation_rules_raw)

    if vdt in ("integer", "decimal", "percentage"):
        if value_numeric is None:
            raise HTTPException(400, "Bitte einen numerischen Wert eingeben.")
        v = float(value_numeric)
        if vdt == "integer":
            if abs(v - round(v)) > 1e-9:
                raise HTTPException(400, "Der Wert muss eine ganze Zahl sein.")
            v = float(int(round(v)))
        pos = bool(rules.get("positive_only"))
        mn = rules.get("min")
        mx = rules.get("max")
        if mn is not None:
            mn = float(mn)
        if mx is not None:
            mx = float(mx)
        if vdt == "percentage":
            gmn = float(mn) if mn is not None else 0.0
            gmx = float(mx) if mx is not None else 100.0
            gmn = max(gmn, 0.0)
            gmx = min(gmx, 100.0)
            if gmn > gmx:
                raise HTTPException(500, "Ungültige Plausibilisierung: min > max (Prozent).")
            if v < gmn or v > gmx:
                raise HTTPException(
                    400,
                    f"Prozentwert muss zwischen {gmn} und {gmx} liegen.",
                )
            if pos and v <= 0:
                raise HTTPException(400, "Prozentwert muss positiv sein (laut Konfiguration).")
        else:
            if pos and v <= 0:
                raise HTTPException(400, "Der Wert muss positiv sein (laut Konfiguration).")
            if mn is not None and v < mn:
                raise HTTPException(400, f"Der Wert muss mindestens {mn} sein.")
            if mx is not None and v > mx:
                raise HTTPException(400, f"Der Wert darf höchstens {mx} sein.")
        return v, None

    # text / enum
    s = (value_text or "").strip() if value_text is not None else ""
    if vdt == "text" and not s and not rules.get("not_empty"):
        return None, ""
    if rules.get("not_empty") and not s:
        raise HTTPException(400, "Der Text darf nicht leer sein.")
    ml = rules.get("max_length")
    if ml is not None:
        try:
            ml_int = int(ml)
        except (TypeError, ValueError):
            ml_int = None
        if ml_int is not None and len(s) > ml_int:
            raise HTTPException(400, f"Text zu lang (max. {ml_int} Zeichen).")
    if vdt == "enum":
        allowed = rules.get("allowed_values") or []
        if not isinstance(allowed, list):
            allowed = []
        allowed_str = [str(x).strip() for x in allowed if str(x).strip()]
        if not allowed_str:
            raise HTTPException(500, "ENUM-Typ ohne erlaubte Werte (Admin-Konfiguration).")
        if s not in allowed_str:
            raise HTTPException(
                400,
                f"Ungültiger Wert. Erlaubt: {', '.join(allowed_str)}",
            )
    return None, s
