"""Connector-related models."""

from __future__ import annotations

from typing import Any, Dict, List, Optional, Sequence, Union

from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator

from filare.models.colors import MultiColor, SingleColor
from filare.models.dataclasses import (
    Connector,
    GraphicalComponent,
    Loop,
    PinClass,
)  # noqa: F401
from filare.models.hypertext import MultilineHypertext
from filare.models.image import Image
from filare.models.types import AUTOGENERATED_PREFIX, BomCategory, Side  # noqa: F401
from filare.models.utils import remove_links


class GraphicalComponentModel(BaseModel):
    """Pydantic representation mirroring GraphicalComponent fields."""

    designator: str
    category: Optional[Union[BomCategory, str]] = None
    type: Optional[Union[str, MultilineHypertext]] = None
    subtype: Optional[Union[str, MultilineHypertext]] = None
    color: Optional[Union[str, Sequence[str], MultiColor]] = None
    image: Optional[Union[Image, Dict[str, Any]]] = None
    notes: Optional[Union[str, Sequence[str], MultilineHypertext]] = None
    additional_components: List[Any] = Field(default_factory=list)
    bgcolor: Optional[Union[str, SingleColor]] = None
    bgcolor_title: Optional[Union[str, SingleColor]] = None
    show_name: Optional[bool] = None
    show_pincount: Optional[bool] = None

    model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)

    @field_validator("type", "subtype", "notes", mode="before")
    def _to_multiline(cls, value):
        if value is None:
            return value
        return MultilineHypertext.to(value)

    @field_validator("color", mode="before")
    def _to_multicolor(cls, value):
        if value is None:
            return value
        return MultiColor(value)

    @field_validator("bgcolor", "bgcolor_title", mode="before")
    def _to_single_color(cls, value):
        if value is None:
            return value
        return SingleColor(value)

    @field_validator("image", mode="before")
    def _to_image(cls, value):
        if value is None:
            return None
        if isinstance(value, Image):
            return value
        if isinstance(value, dict):
            return Image(**value)
        return value

    @field_validator("category", mode="before")
    def _coerce_category(cls, value: Any):
        if value is None:
            return None
        if isinstance(value, BomCategory):
            return value
        try:
            return BomCategory(value)
        except Exception:
            return value

    def to_graphical_component(self) -> GraphicalComponent:
        return GraphicalComponent(
            designator=remove_links(self.designator),
            type=self.type,
            subtype=self.subtype,
            color=self.color,
            image=self.image,
            notes=self.notes,
            additional_components=self.additional_components,
            bgcolor=self.bgcolor,
            bgcolor_title=self.bgcolor_title,
            show_name=self.show_name,
            category=self.category or BomCategory.ADDITIONAL,
        )


class ConnectorModel(GraphicalComponentModel):
    """Pydantic representation of Connector with conversion helpers."""

    pincount: Optional[int] = None
    pins: List[Any] = Field(default_factory=list)
    pinlabels: List[Any] = Field(default_factory=list)
    pincolors: List[Any] = Field(default_factory=list)
    loops: List[Any] = Field(default_factory=list)
    style: Optional[str] = None
    images: List[str] = Field(default_factory=list)
    hide_disconnected_pins: bool = False

    @model_validator(mode="before")
    def _normalize_pincount(cls, values: Dict[str, Any]) -> Dict[str, Any]:
        data = dict(values or {})
        if data.get("pincount") is None:
            pins = data.get("pins") or []
            pinlabels = data.get("pinlabels") or []
            data["pincount"] = len(pins) or len(pinlabels) or None
        return data

    @field_validator("loops", mode="before")
    def _coerce_loops(cls, value: Any):
        if value is None:
            return []
        if isinstance(value, dict):
            return [value]
        return list(value)

    def to_connector(self) -> Connector:
        normalized_pins = []
        for pin in self.pins:
            if isinstance(pin, dict):
                normalized_pins.append(pin.get("id") or pin.get("label"))
            else:
                normalized_pins.append(pin)
        kwargs = dict(
            designator=self.designator,
            type=self.type,
            subtype=self.subtype,
            pincount=self.pincount,
            pins=normalized_pins,
            pinlabels=self.pinlabels,
            pincolors=self.pincolors,
            loops=self.loops,
            style=self.style,
            notes=self.notes,
            color=self.color,
            bgcolor=self.bgcolor,
            bgcolor_title=self.bgcolor_title,
            show_pincount=self.show_pincount,
            show_name=self.show_name,
            hide_disconnected_pins=self.hide_disconnected_pins,
            additional_components=self.additional_components,
        )
        return Connector(**kwargs)


__all__ = [
    "Connector",
    "Loop",
    "PinClass",
    "AUTOGENERATED_PREFIX",
    "Side",
    "ConnectorModel",
    "GraphicalComponentModel",
]
