"""
Persönliche Referenzwerte (profilorientiert)

Typkatalog system-seeded; Nutzer pflegt historische Einträge pro aktivem Profil.
Einheit immer aus dem Typ; Wert je value_data_type validiert.

Reads (Liste, Summary, Katalog) → data_layer.reference_values (Layer 1).
"""
from __future__ import annotations

from datetime import datetime
from decimal import Decimal
from typing import Any, Optional

from fastapi import APIRouter, Depends, Header, HTTPException, Query
from pydantic import BaseModel, Field
from psycopg2.extras import Json

from auth import require_auth
from data_layer.reference_values import (
    fetch_reference_type_by_key,
    get_profile_reference_values_summary,
    list_active_reference_value_types_data,
    list_profile_reference_values_for_type,
    normalize_reference_row,
)
from db import get_db, get_cursor, r2d
from reference_value_validation import (
    REF_VALUE_CONFIDENCE,
    REF_VALUE_CONFIDENCE_ORDER,
    REF_VALUE_METHODS,
    REF_VALUE_SOURCES,
    validate_meta_confidence,
    validate_meta_method,
    validate_meta_source,
    validate_value_for_data_type,
    resolve_unit_from_type,
)
from routers.profiles import get_pid

router = APIRouter(prefix="/api", tags=["reference-values"])


class ProfileReferenceValueCreate(BaseModel):
    reference_value_type_key: str = Field(..., min_length=1, max_length=64)
    effective_date: str
    value_numeric: Optional[float] = None
    value_text: Optional[str] = None
    source: str = Field(..., min_length=1)
    method: str = Field(..., min_length=1)
    confidence: str = Field(..., min_length=1)
    notes: Optional[str] = None
    extra: Optional[dict] = None


class ProfileReferenceValueUpdate(BaseModel):
    effective_date: Optional[str] = None
    value_numeric: Optional[float] = None
    value_text: Optional[str] = None
    source: Optional[str] = None
    method: Optional[str] = None
    confidence: Optional[str] = None
    notes: Optional[str] = None
    extra: Optional[dict] = None


@router.get("/reference-value-types")
def list_reference_value_types(session: dict = Depends(require_auth)):
    """Alle aktiven Referenztyp-Definitionen (dynamische UI inkl. Validierungsmetadaten)."""
    return list_active_reference_value_types_data()


@router.get("/reference-value-meta/enums")
def list_reference_value_meta_enums(session: dict = Depends(require_auth)):
    """Erlaubte Werte für Quelle, Methode, Vertrauensgrad (Erfassungsdialog)."""
    return {
        "sources": sorted(REF_VALUE_SOURCES),
        "methods": sorted(REF_VALUE_METHODS),
        "confidence_levels": [x for x in REF_VALUE_CONFIDENCE_ORDER if x in REF_VALUE_CONFIDENCE],
    }


@router.get("/profile-reference-values/summary")
def profile_reference_values_summary(
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth),
):
    """
    Für das aktive Profil: je Referenztyp mit mindestens einem Eintrag der jüngste Wert
    plus der unmittelbar vorherige (gleiche Sortierung wie Liste), für Tendenz-Anzeigen.
    """
    pid = get_pid(x_profile_id)
    return get_profile_reference_values_summary(pid)


@router.get("/profile-reference-values")
def list_profile_reference_values(
    type_key: str = Query(..., description="Schlüssel aus reference_value_types.key"),
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth),
):
    """Historische Einträge eines Typs für das aktive Profil (neueste zuerst)."""
    pid = get_pid(x_profile_id)
    rows = list_profile_reference_values_for_type(pid, type_key)
    if rows is None:
        raise HTTPException(404, "Referenztyp nicht gefunden")
    return rows


@router.post("/profile-reference-values")
def create_profile_reference_value(
    body: ProfileReferenceValueCreate,
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth),
):
    pid = get_pid(x_profile_id)
    try:
        datetime.strptime(body.effective_date, "%Y-%m-%d")
    except ValueError:
        raise HTTPException(400, "Ungültiges Datum. Format: YYYY-MM-DD")

    src = validate_meta_source(body.source)
    meth = validate_meta_method(body.method)
    conf = validate_meta_confidence(body.confidence)

    with get_db() as conn:
        cur = get_cursor(conn)
        t = fetch_reference_type_by_key(cur, body.reference_value_type_key.strip(), require_active=True)
        if not t:
            raise HTTPException(404, "Referenztyp nicht gefunden")
        vdt = (t.get("value_data_type") or "decimal").strip().lower()
        rules = t.get("validation_rules") or {}
        vnum, vtxt = validate_value_for_data_type(vdt, rules, body.value_numeric, body.value_text)
        unit = resolve_unit_from_type(t.get("default_unit"))
        extra = body.extra if body.extra is not None else {}

        cur.execute(
            """
            INSERT INTO profile_reference_values (
                profile_id, reference_value_type_id, effective_date,
                value_numeric, value_text, unit, source, confidence, method, notes, extra
            )
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
            RETURNING id
            """,
            (
                pid,
                t["id"],
                body.effective_date,
                vnum,
                vtxt,
                unit,
                src,
                conf,
                meth,
                body.notes,
                Json(extra),
            ),
        )
        new_id = cur.fetchone()["id"]

        cur.execute(
            """
            SELECT
                v.id,
                v.profile_id,
                v.reference_value_type_id,
                v.effective_date,
                v.value_numeric,
                v.value_text,
                v.unit,
                v.source,
                v.confidence,
                v.method,
                v.notes,
                v.extra,
                v.created_at,
                v.updated_at,
                rt.key AS type_key,
                rt.label AS type_label
            FROM profile_reference_values v
            JOIN reference_value_types rt ON rt.id = v.reference_value_type_id
            WHERE v.id = %s AND v.profile_id = %s
            """,
            (new_id, pid),
        )
        return normalize_reference_row(r2d(cur.fetchone()))


@router.put("/profile-reference-values/{entry_id}")
def update_profile_reference_value(
    entry_id: int,
    body: ProfileReferenceValueUpdate,
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth),
):
    pid = get_pid(x_profile_id)
    patch = body.model_dump(exclude_unset=True)
    if not patch:
        raise HTTPException(400, "Keine Felder zum Aktualisieren")

    if patch.get("effective_date"):
        try:
            datetime.strptime(patch["effective_date"], "%Y-%m-%d")
        except ValueError:
            raise HTTPException(400, "Ungültiges Datum. Format: YYYY-MM-DD")

    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute(
            """
            SELECT v.*, rt.default_unit, rt.value_data_type, rt.validation_rules
            FROM profile_reference_values v
            JOIN reference_value_types rt ON rt.id = v.reference_value_type_id
            WHERE v.id = %s AND v.profile_id = %s
            """,
            (entry_id, pid),
        )
        row = cur.fetchone()
        if not row:
            raise HTTPException(404, "Eintrag nicht gefunden")
        cur_row = r2d(row)

        vdt = (cur_row.get("value_data_type") or "decimal").strip().lower()
        rules = cur_row.get("validation_rules") or {}

        new_ed = patch.get("effective_date", cur_row["effective_date"])
        if hasattr(new_ed, "isoformat"):
            new_ed = new_ed.isoformat()

        vn = patch["value_numeric"] if "value_numeric" in patch else cur_row.get("value_numeric")
        vt_raw = patch["value_text"] if "value_text" in patch else cur_row.get("value_text")
        if vn is not None and isinstance(vn, Decimal):
            vn = float(vn)

        vnum, vtxt = validate_value_for_data_type(vdt, rules, vn, vt_raw)

        unit = resolve_unit_from_type(cur_row.get("default_unit"))

        if "source" in patch:
            src = validate_meta_source(patch["source"])
        else:
            src = validate_meta_source(cur_row.get("source"))
        if "method" in patch:
            meth = validate_meta_method(patch["method"])
        else:
            meth = validate_meta_method(cur_row.get("method"))
        if "confidence" in patch:
            conf = validate_meta_confidence(patch["confidence"])
        else:
            conf = validate_meta_confidence(cur_row.get("confidence"))

        updates: dict[str, Any] = {
            "effective_date": new_ed,
            "value_numeric": vnum,
            "value_text": vtxt,
            "unit": unit,
            "source": src,
            "method": meth,
            "confidence": conf,
        }
        if "notes" in patch:
            updates["notes"] = patch["notes"]
        if "extra" in patch:
            updates["extra"] = Json(patch["extra"] if patch["extra"] is not None else {})

        set_parts = [f"{k} = %s" for k in updates]
        vals = list(updates.values()) + [entry_id, pid]
        cur.execute(
            f"""
            UPDATE profile_reference_values SET {", ".join(set_parts)}, updated_at = NOW()
            WHERE id = %s AND profile_id = %s
            """,
            tuple(vals),
        )

        cur.execute(
            """
            SELECT
                v.id,
                v.profile_id,
                v.reference_value_type_id,
                v.effective_date,
                v.value_numeric,
                v.value_text,
                v.unit,
                v.source,
                v.confidence,
                v.method,
                v.notes,
                v.extra,
                v.created_at,
                v.updated_at,
                rt.key AS type_key,
                rt.label AS type_label
            FROM profile_reference_values v
            JOIN reference_value_types rt ON rt.id = v.reference_value_type_id
            WHERE v.id = %s AND v.profile_id = %s
            """,
            (entry_id, pid),
        )
        return normalize_reference_row(r2d(cur.fetchone()))


@router.delete("/profile-reference-values/{entry_id}")
def delete_profile_reference_value(
    entry_id: int,
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth),
):
    pid = get_pid(x_profile_id)
    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute(
            "DELETE FROM profile_reference_values WHERE id = %s AND profile_id = %s RETURNING id",
            (entry_id, pid),
        )
        if not cur.fetchone():
            raise HTTPException(404, "Eintrag nicht gefunden")
    return {"ok": True}
