"""Volvo Cars API models."""

from dataclasses import KW_ONLY, dataclass, field, is_dataclass
from datetime import datetime
import inspect
import re
from typing import Any, Self, TypeVar

T = TypeVar("T", bound="VolvoCarsApiBaseModel")
_TO_SNAKE_CASE_REGEX = re.compile(r"(?<=[a-z0-9])([A-Z])")


def _sanitize_json_key(key: str) -> str:
    key = "description" if key == "descriptions" else key
    key = _TO_SNAKE_CASE_REGEX.sub(r"_\1", key)
    return key.lower()


@dataclass
class VolvoCarsApiBaseModel:
    """Base API model."""

    _: KW_ONLY
    extra_data: dict[str, Any] = field(default_factory=dict[str, Any])

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> Self | None:
        """Create instance from json dict."""
        parameters = inspect.signature(cls).parameters
        class_data: dict[str, Any] = {}
        extra_data: dict[str, Any] = {}

        for key, value in data.items():
            key = _sanitize_json_key(key)
            if key in parameters:
                # Check if the field is a dataclass and the value is a dict
                param_type = parameters[key].annotation
                if (
                    is_dataclass(param_type)
                    and isinstance(param_type, type)
                    and issubclass(param_type, VolvoCarsApiBaseModel)
                    and isinstance(value, dict)
                ):
                    # Recursive call for nested dataclasses
                    class_data[key] = param_type.from_dict(value)
                elif key in ("timestamp", "updatedAt") and isinstance(value, str):
                    if value:
                        class_data["timestamp"] = datetime.fromisoformat(value)
                else:
                    class_data[key] = value
            else:
                extra_data[key] = value

        if len(class_data) == 0:
            return None

        class_data["extra_data"] = extra_data
        return cls(**class_data)

    def get(self, key: str) -> Any:
        """Get a specific key from the API field."""
        return self.extra_data.get(key)


@dataclass
class VolvoCarsModel(VolvoCarsApiBaseModel):
    """Representation of a Volvo Cars model."""

    model: str
    steering: str
    upholstery: str | None = None


@dataclass
class VolvoCarsImages(VolvoCarsApiBaseModel):
    """Representation of Volvo Cars images."""

    exterior_image_url: str
    internal_image_url: str


@dataclass
class VolvoCarsVehicle(VolvoCarsApiBaseModel):
    """Representation of a Volvo Cars vehicle."""

    vin: str
    model_year: int
    gearbox: str
    fuel_type: str
    images: VolvoCarsImages
    description: VolvoCarsModel
    external_colour: str | None = None
    battery_capacity_kwh: float | None = None

    def has_battery_engine(self) -> bool:
        """Determine if vehicle has a battery engine."""
        return self.fuel_type in ("ELECTRIC", "PETROL/ELECTRIC", "NONE")

    def has_combustion_engine(self) -> bool:
        """Determine if vehicle has a combustion engine."""
        return self.fuel_type in ("DIESEL", "PETROL", "PETROL/ELECTRIC")


@dataclass
class VolvoCarsValue(VolvoCarsApiBaseModel):
    """API value model."""

    value: Any


@dataclass
class VolvoCarsValueField(VolvoCarsValue):
    """API value field model."""

    timestamp: datetime | None = None
    unit: str | None = None


@dataclass
class VolvoCarsValueStatusField(VolvoCarsValueField):
    """API value and status field model."""

    value: Any = None
    status: str | None = None


@dataclass
class VolvoCarsGeometry(VolvoCarsApiBaseModel):
    """API geometry model."""

    coordinates: list[float] = field(default_factory=list[float])


@dataclass
class VolvoCarsLocationProperties(VolvoCarsApiBaseModel):
    """API location properties model."""

    heading: str
    timestamp: datetime | None = None


@dataclass
class VolvoCarsLocation(VolvoCarsApiBaseModel):
    """API location model."""

    type: str
    properties: VolvoCarsLocationProperties
    geometry: VolvoCarsGeometry


@dataclass
class VolvoCarsAvailableCommand(VolvoCarsApiBaseModel):
    """Available command model."""

    command: str
    href: str


@dataclass
class VolvoCarsCommandResult(VolvoCarsApiBaseModel):
    """Command result model."""

    vin: str
    invoke_status: str
    message: str


@dataclass
class TokenResponse(VolvoCarsApiBaseModel):
    """Authorization response model."""

    access_token: str
    refresh_token: str
    token_type: str
    expires_in: int
    expires_at: float
    id_token: str | None = None


@dataclass
class VolvoCarsErrorResult(VolvoCarsApiBaseModel):
    """Error result model."""

    message: str | None = None
    description: str | None = None


class VolvoApiException(Exception):
    """Thrown when an API request fails."""

    def __init__(self, message: str = "", operation: str = "") -> None:
        """Initialize exception."""
        self.message = message
        self.operation = operation


class VolvoAuthException(VolvoApiException):
    """Thrown when the authentication fails."""
