from typing import Any
from typing import Literal

import narwhals as nw
from pydantic import BaseModel
from pydantic import ConfigDict
from pydantic import Field
from pydantic import field_serializer
from pydantic import field_validator


class BaseUserData(BaseModel):
    """Base model for content data that is set directly by the user.

    The common field for all content types is a title.
    """

    title: str = Field(min_length=1)


class Author(BaseModel):
    name: str
    email: str


class Article(BaseUserData):
    """
    :param title: Hovedtittel for artikkelen.
    :param authors: Liste av forfattere.

        Eksempel:

            ```py
            [
                {"name": "Ola Nordmann", "email": "ola@email.com"},
                {"name": "Kari Nordmann", "email": "kari@email.com"},
            ]
    :param ingress: Ingressen til artikkelen.
    """

    authors: list[Author] = Field(default_factory=list)
    ingress: str = ""


class Factbox(BaseUserData):
    """
    :param title: Tittel for faktaboksen.
    :param display_type: Visning av faktaboksen.

        Alternativer:

        * "default": Bare tittel (standard)
        * "sneakPeek": Tittel og litt av forklaringsteksten
        * "aiIcon": Tittel og litt av forklaringsteksten + KI-ikon
    """

    display_type: Literal["default", "sneakPeek", "aiIcon"] = "default"


class Highchart(BaseUserData):
    """
    :param title: Tittel for highchart-figuren.
    :param graph_type: Graftype.

        Alternativer:

        * "line": Linje (standard)
        * "pie": Kake
        * "column": Stolpe
        * "bar": Liggende stolpe
        * "area": Areal
        * "barNegative": Pyramide

    :param dataframe: En pandas, Polars eller PyArrow dataframe.
    :param tbml: URL eller TBML-id.
    :param xlabel: X-akse, tittel.
    :param ylabel: Y-akse, tittel.
    """

    model_config = ConfigDict(arbitrary_types_allowed=True)

    graph_type: Literal["line", "pie", "column", "bar", "area", "barNegative"] = "line"
    dataframe: Any | None = None
    tbml: str | None = None
    xlabel: str = Field(default="x", min_length=1)
    ylabel: str = Field(default="y", min_length=1)

    @field_validator("dataframe", mode="before")
    @classmethod
    def validate_dataframe(cls, v: Any) -> Any:
        if isinstance(v, dict):
            return nw.from_dict(v["df"], backend=v["backend"])
        return nw.from_native(v)

    @field_serializer("dataframe")
    def serialize_dataframe(self, v: nw.DataFrame[Any]) -> dict[str, Any]:
        return {"backend": v.implementation.value, "df": v.to_dict(as_series=False)}


UserData = Article | Factbox | Highchart
