from __future__ import annotations

from collections.abc import Iterable
from typing import Any, Iterator, List, TypedDict, Union

VectorSource = Iterable[float]


class VectorValuesInput(TypedDict):
    values: VectorSource


VectorInput = Union["Vector", VectorSource, VectorValuesInput]


def _coerce_vector_values(value: VectorInput | VectorSource | None = None) -> List[float]:
    if value is None:
        return []
    if isinstance(value, Vector):
        return value.to_list()
    if isinstance(value, dict) and "values" in value:
        return _coerce_vector_values(value["values"])
    return [float(item) for item in value]


class Vector:
    def __init__(self, value: VectorInput | VectorSource) -> None:
        self.value: List[float] = _coerce_vector_values(value)

    @classmethod
    def empty(cls) -> "Vector":
        return cls(())

    @classmethod
    def of(cls, *values: float) -> "Vector":
        return cls(values)

    @classmethod
    def from_wire(cls, value: Any) -> "Vector":
        return value if isinstance(value, cls) else cls(value)

    @classmethod
    def from_input(cls, value: VectorInput) -> "Vector":
        return value if isinstance(value, cls) else cls(value)

    @classmethod
    def to_wire_input(cls, value: VectorInput) -> List[float]:
        return _coerce_vector_values(value)

    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, value: Any, *args: Any, **kwargs: Any) -> "Vector":
        return cls.from_wire(value)

    def to_list(self) -> List[float]:
        return list(self.value)

    def to_wire(self) -> List[float]:
        return self.to_list()

    def at(self, index: int) -> float | None:
        if index < 0 or index >= len(self.value):
            return None
        return self.value[index]

    def is_empty(self) -> bool:
        return not self.value

    def concat(self, other: VectorSource) -> "Vector":
        return Vector([*self.value, *_coerce_vector_values(other)])

    def equals(self, other: Any) -> bool:
        other_value = other.value if isinstance(other, Vector) else list(other)
        return self.value == [float(item) for item in other_value]

    def __iter__(self) -> Iterator[float]:
        return iter(self.value)

    def __len__(self) -> int:
        return len(self.value)

    def __getitem__(self, index: int) -> float:
        return self.value[index]

    def __eq__(self, other: object) -> bool:
        if isinstance(other, Vector):
            return self.value == other.value
        if isinstance(other, Iterable):
            return self.equals(other)
        return False

    def __repr__(self) -> str:
        return f"Vector({self.value!r})"


__all__ = ["Vector", "VectorSource", "VectorValuesInput", "VectorInput"]
