"""
Reference values — Layer 1 (read paths + structured rows)

Structured retrieval for profile reference values and the active type catalog.
Mutations (INSERT/UPDATE/DELETE) stay in routers/reference_values.py with validation.

Dates are normalized to ISO strings; Decimals to float — suitable for JSON/API layers.
"""

from __future__ import annotations

from decimal import Decimal
from typing import Any, Optional

from db import get_cursor, get_db, r2d


def normalize_reference_row(d: Optional[dict[str, Any]]) -> dict[str, Any]:
    """Normalize DB row dict for JSON (dates → ISO, Decimal → float)."""
    if not d:
        return d
    out = dict(d)
    for k in ("effective_date", "created_at", "updated_at"):
        v = out.get(k)
        if v is not None and hasattr(v, "isoformat"):
            out[k] = v.isoformat()
    vn = out.get("value_numeric")
    if vn is not None and isinstance(vn, Decimal):
        out["value_numeric"] = float(vn)
    return out


def fetch_reference_type_by_key(cur, key: str, require_active: bool = True) -> Optional[dict[str, Any]]:
    """Single type row by key; for use inside router transactions (shared cursor)."""
    q = (
        "SELECT id, key, label, description, default_unit, active, category, "
        "value_data_type, validation_rules, metadata "
        "FROM reference_value_types WHERE key = %s "
    )
    if require_active:
        q += "AND active = TRUE "
    cur.execute(q, (key,))
    row = cur.fetchone()
    return r2d(row) if row else None


def list_active_reference_value_types_data() -> list[dict[str, Any]]:
    """All active reference_value_types rows, catalog sort order."""
    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute(
            """
            SELECT
                id, key, label, description, default_unit, sort_order, active,
                category, value_data_type, validation_rules, metadata, created_at
            FROM reference_value_types
            WHERE active = TRUE
            ORDER BY sort_order ASC, id ASC
            """
        )
        return [normalize_reference_row(r2d(r)) for r in cur.fetchall()]


def list_profile_reference_values_for_type(
    profile_id: str, type_key: str
) -> Optional[list[dict[str, Any]]]:
    """
    Historical entries for one type, newest first.
    Returns None if type_key does not resolve to an active type.
    """
    with get_db() as conn:
        cur = get_cursor(conn)
        t = fetch_reference_type_by_key(cur, type_key, require_active=True)
        if not t:
            return None
        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.profile_id = %s AND rt.key = %s
            ORDER BY v.effective_date DESC, v.created_at DESC
            """,
            (profile_id, t["key"]),
        )
        return [normalize_reference_row(r2d(r)) for r in cur.fetchall()]


def build_summary_tiles_from_ranked_rows(raw_rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """
    Build /summary.tile payloads from SQL rows (rn 1..2 per type).
    Each row still contains rn, type_sort_order, value_data_type before stripping.
    """
    by_key: dict[str, dict[str, Any]] = {}
    skip_cols = frozenset({"rn", "type_sort_order", "value_data_type"})
    for row in raw_rows:
        rn = int(row.get("rn") or 0)
        key = row["type_key"]
        if key not in by_key:
            by_key[key] = {
                "type_key": key,
                "type_label": row.get("type_label") or key,
                "value_data_type": (row.get("value_data_type") or "decimal").strip().lower(),
                "sort_key": (row.get("type_sort_order") or 0, key),
                "latest": None,
                "previous": None,
            }
        entry = {k: v for k, v in row.items() if k not in skip_cols}
        api_entry = normalize_reference_row(entry)
        if rn == 1:
            by_key[key]["latest"] = api_entry
        elif rn == 2:
            by_key[key]["previous"] = api_entry

    tiles = sorted(by_key.values(), key=lambda t: t["sort_key"])
    for t in tiles:
        t.pop("sort_key", None)
    return tiles


def get_profile_reference_values_summary(profile_id: str) -> dict[str, Any]:
    """latest + previous entry per type (active types only), tiles sorted like catalog."""
    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute(
            """
            WITH ranked AS (
                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,
                    rt.sort_order AS type_sort_order,
                    rt.value_data_type,
                    ROW_NUMBER() OVER (
                        PARTITION BY v.reference_value_type_id
                        ORDER BY v.effective_date DESC, v.created_at DESC
                    ) AS rn
                FROM profile_reference_values v
                JOIN reference_value_types rt ON rt.id = v.reference_value_type_id
                WHERE v.profile_id = %s AND rt.active = TRUE
            )
            SELECT * FROM ranked WHERE rn <= 2
            ORDER BY type_sort_order ASC, type_key ASC, rn ASC
            """,
            (profile_id,),
        )
        raw_rows = [r2d(r) for r in cur.fetchall()]

    tiles = build_summary_tiles_from_ranked_rows(raw_rows)
    return {"tiles": tiles}
