from __future__ import annotations

from typing import TYPE_CHECKING, Literal

from pydantic import BaseModel

from lite_dist2.common import numerize, portablize
from lite_dist2.expections import LD2ModelTypeError
from lite_dist2.type_definitions import PortableValueType

if TYPE_CHECKING:
    from collections.abc import Iterable

    from lite_dist2.type_definitions import PrimitiveValueType


def _get_default_value(value_type: Literal["bool", "int", "float"]) -> PortableValueType:
    match value_type:
        case "bool":
            return False
        case "int":
            return "0x0"
        case "float":
            return "0x0.0p+0"
        case _:
            raise LD2ModelTypeError(value_type)


class ScalarValue(BaseModel):
    type: Literal["scalar"]
    value_type: Literal["bool", "int", "float"]
    value: PortableValueType
    name: str | None = None

    def numerize(self) -> PrimitiveValueType:
        return numerize(self.value_type, self.value)

    def equal_to(self, other: object) -> bool:
        if not isinstance(other, ScalarValue):
            return False
        return self.value_type == other.value_type and self.value == other.value

    def get_value_size(self) -> int:
        return 1

    def get_value_types(self) -> tuple[Literal["bool", "int", "float"], ...]:
        return (self.value_type,)

    def get_value_list(self) -> list[PortableValueType]:
        return [self.value]

    def to_dummy(self) -> ScalarValue:
        return ScalarValue(
            type="scalar",
            value_type=self.value_type,
            value=_get_default_value(self.value_type),
            name=self.name,
        )

    @staticmethod
    def create_from_numeric(
        raw_result_value: PrimitiveValueType,
        value_type: Literal["bool", "int", "float"],
        name: str | None = None,
    ) -> ScalarValue:
        val = portablize(value_type, raw_result_value)
        return ScalarValue(type="scalar", value_type=value_type, value=val, name=name)


class VectorValue(BaseModel):
    type: Literal["vector"]
    value_type: Literal["bool", "int", "float"]
    values: list[PortableValueType]
    name: str | None = None

    def numerize(self) -> list[PrimitiveValueType]:
        return [numerize(self.value_type, v) for v in self.values]

    def equal_to(self, other: object) -> bool:
        if not isinstance(other, VectorValue):
            return False
        return self.value_type == other.value_type and self.values == other.values

    def get_value_size(self) -> int:
        return len(self.values)

    def get_value_types(self) -> tuple[Literal["bool", "int", "float"], ...]:
        return (self.value_type,) * self.get_value_size()

    def get_value_list(self) -> list[PortableValueType]:
        return self.values

    def to_dummy(self) -> VectorValue:
        return VectorValue(
            type="vector",
            value_type=self.value_type,
            values=[_get_default_value(self.value_type) for _ in self.values],
            name=self.name,
        )

    @staticmethod
    def create_from_numeric(
        raw_result_value: Iterable[PrimitiveValueType],
        value_type: Literal["bool", "int", "float"],
        name: str | None = None,
    ) -> VectorValue:
        val = [portablize(value_type, rv) for rv in raw_result_value]
        return VectorValue(type="vector", value_type=value_type, values=val, name=name)


type ParamType = tuple[ScalarValue, ...]
type ResultType = ScalarValue | VectorValue
