"""
Dashboard-Layout v1: Validierung, Produkt-Standard (Übersicht) und Lab-Standard.

Erlaubte Widget-IDs und Reihenfolge: widget_catalog.WIDGET_CATALOG.
"""
from __future__ import annotations

from typing import Any, Literal

from pydantic import BaseModel, Field, field_validator, model_validator

from dashboard_widget_config import validate_widget_entry_config
from widget_catalog import (
    ALLOWED_WIDGET_IDS,
    DEFAULT_LAB_WIDGET_IDS,
    DEFAULT_PRODUCT_DASHBOARD_WIDGET_IDS,
    WIDGET_CATALOG,
)

# Abwärtskompatibel (Tests importieren weiterhin aus diesem Modul)
__all__ = [
    "ALLOWED_WIDGET_IDS",
    "DashboardLayoutPayload",
    "DashboardWidgetEntry",
    "coalesce_effective_layout",
    "default_layout_dict",
    "lab_default_layout_dict",
    "product_default_layout_dict",
]


def lab_default_layout_dict() -> dict[str, Any]:
    """Standard für Dashboard-Lab (Experimentier-Widgets)."""
    on = DEFAULT_LAB_WIDGET_IDS
    return {
        "version": 1,
        "widgets": [{"id": e["id"], "enabled": e["id"] in on} for e in WIDGET_CATALOG],
    }


def product_default_layout_dict() -> dict[str, Any]:
    """Code-Fallback für die Produkt-Übersicht; live-Standard ggf. system_config (siehe get_product_default_base_dict)."""
    on = DEFAULT_PRODUCT_DASHBOARD_WIDGET_IDS
    return {
        "version": 1,
        "widgets": [{"id": e["id"], "enabled": e["id"] in on} for e in WIDGET_CATALOG],
    }


def default_layout_dict() -> dict[str, Any]:
    """Alias: Produkt-Standard (coalesce, Reset). Lab nutzt lab_default_layout_dict()."""
    return product_default_layout_dict()


class DashboardWidgetEntry(BaseModel):
    id: str = Field(min_length=1, max_length=64)
    enabled: bool = True
    config: dict[str, Any] = Field(default_factory=dict)

    @field_validator("config", mode="before")
    @classmethod
    def _config_coerce(cls, v: Any) -> dict[str, Any]:
        if v is None:
            return {}
        if not isinstance(v, dict):
            raise ValueError("config muss Objekt sein")
        return v

    @model_validator(mode="after")
    def _normalize_widget_config(self) -> DashboardWidgetEntry:
        normalized = validate_widget_entry_config(self.id, self.config)
        return self.model_copy(update={"config": normalized})


class DashboardLayoutPayload(BaseModel):
    version: Literal[1] = 1
    widgets: list[DashboardWidgetEntry] = Field(min_length=1, max_length=32)

    @model_validator(mode="after")
    def _validate_widgets(self) -> DashboardLayoutPayload:
        ids = [w.id for w in self.widgets]
        if len(ids) != len(set(ids)):
            raise ValueError("Doppelte widget id")
        bad = [i for i in ids if i not in ALLOWED_WIDGET_IDS]
        if bad:
            raise ValueError(f"Unbekannte Widget-IDs: {bad}")
        if not any(w.enabled for w in self.widgets):
            raise ValueError("Mindestens ein Widget muss aktiv sein")
        return self

    def to_stored_dict(self) -> dict[str, Any]:
        out_widgets: list[dict[str, Any]] = []
        for w in self.widgets:
            d: dict[str, Any] = {"id": w.id, "enabled": w.enabled}
            if w.config:
                d["config"] = dict(w.config)
            out_widgets.append(d)
        return {"version": self.version, "widgets": out_widgets}


def coalesce_effective_layout(raw: Any) -> tuple[bool, dict[str, Any]]:
    """
    Returns (has_custom, effective_layout).
    has_custom=True nur wenn DB-Wert vorhanden und gültig (v1).
    """
    if raw is None:
        return False, default_layout_dict()
    parsed_obj: Any = raw
    if isinstance(raw, str):
        import json

        try:
            parsed_obj = json.loads(raw)
        except json.JSONDecodeError:
            return False, default_layout_dict()
    if not isinstance(parsed_obj, dict):
        return False, default_layout_dict()
    try:
        parsed = DashboardLayoutPayload.model_validate(
            {
                "version": parsed_obj.get("version", 1),
                "widgets": parsed_obj.get("widgets", []),
            }
        )
        return True, parsed.to_stored_dict()
    except Exception:
        return False, default_layout_dict()
