from enum import Enum
from typing import List, Optional
from pydantic import BaseModel, Field, model_validator

from graph_processor.utils import clip_text


class PointModel(BaseModel):
    x: float
    y: float

    def to_list(self) -> List[float]:
        return [self.x, self.y]


class PointWithRadius(PointModel):
    radius: Optional[float] = None


class BarSeriesModel(BaseModel):
    name: str
    data: List[float] = Field(
        description="Only numbers should be given out in data. Don't include text/string in data."
    )

    def get_name(self) -> str:
        return clip_text(self.name)


class ScatterSeriesModel(BaseModel):
    name: str
    points: List[PointModel]

    def get_name(self) -> str:
        return clip_text(self.name)


class BubbleSeriesModel(BaseModel):
    name: str
    points: List[PointWithRadius]

    def get_name(self) -> str:
        return clip_text(self.name)


class LineSeriesModel(BaseModel):
    name: str
    data: List[float] = Field(
        description="Only numbers should be given out in data. Don't include text/string in data."
    )

    def get_name(self) -> str:
        return clip_text(self.name)


class PieChartSeriesModel(BaseModel):
    data: List[float]


class BarGraphDataModel(BaseModel):
    categories: List[str]
    series: List[BarSeriesModel] = Field(
        description="There should be no more than 3 series"
    )

    def get_categories(self) -> List[str]:
        return [clip_text(category) for category in self.categories]


class ScatterChartDataModel(BaseModel):
    series: List[ScatterSeriesModel]


class BubbleChartDataModel(BaseModel):
    series: List[BubbleSeriesModel]


class LineChartDataModel(BaseModel):
    categories: List[str]
    series: List[LineSeriesModel] = Field(
        description="There should be no more than 3 series"
    )

    def get_categories(self) -> List[str]:
        return [clip_text(category) for category in self.categories]


class PieChartDataModel(BaseModel):
    categories: List[str]
    series: List[PieChartSeriesModel] = Field(
        description="One series model with list of data",
        min_length=1,
    )

    @model_validator(mode="after")
    def limit_series(self):
        self.series = self.series[:1]
        return self

    def get_categories(self) -> List[str]:
        return [clip_text(category) for category in self.categories]


class TableDataModel(BaseModel):
    categories: List[str]
    series: List[BarSeriesModel]

    def get_categories(self) -> List[str]:
        return [clip_text(category) for category in self.categories]


class GraphTypeEnum(Enum):
    pie = "pie"
    bar = "bar"
    scatter = "scatter"
    bubble = "bubble"
    line = "line"
    table = "table"


class GraphModel(BaseModel):
    id: Optional[str] = None
    style: Optional[dict] = {}
    name: str
    type: GraphTypeEnum
    presentation: Optional[str] = None
    unit: Optional[str] = Field(
        default="Unit of the data in the graph. Example: %, kg, million USD, tonnes, etc."
    )
    data: (
        PieChartDataModel
        | LineChartDataModel
        | BubbleChartDataModel
        | BarGraphDataModel
        | TableDataModel
    )


GRAPH_TYPE_MAPPING = {
    GraphTypeEnum.pie: PieChartDataModel,
    GraphTypeEnum.bar: BarGraphDataModel,
    GraphTypeEnum.line: LineChartDataModel,
    GraphTypeEnum.bubble: BubbleChartDataModel,
}
