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 BaseContent(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)


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


class Article(BaseContent):
    """The article content type.

    Attributes:
        title: The title, placed at the top of the article.
        authors: A list of authors. Example:
            ```py
            [
                {"name": "Ola Nordmann", "email": "ola@email.com"},
                {"name": "Kari Nordmann", "email": "kari@email.com"},
            ]

        ingress: The ingress, placed below the title.
    """

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


class Factbox(BaseContent):
    """The factbox content type.

    Attributes:
        title: Title, shown when collapsed and expanded.
        display_type: How much to show when collapsed. Alternatives:

            * "default": Only the title (standard)
            * "sneakPeek": Title and some of the content
            * "aiIcon": Title and some of the content + AI-icon
    """

    title: str = Field(min_length=1)
    display_type: Literal["default", "sneakPeek", "aiIcon"] = "default"


class Highchart(BaseContent):
    """The highchart content type.

    Attributes:
        title: Title, placed above the highchart.
        graph_type: Graph type. Alternatives:

            * "line"
            * "pie"
            * "column"
            * "bar"
            * "area"
            * "barNegative"

        dataframe: A pandas, polars or pyarrow dataframe.
        tbml: URL or tbml-id.
        xlabel: The x-axis title.
        ylabel: The y-axis title.
    """

    title: str = Field(min_length=1)
    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
