from typing import Any, cast
from uuid import UUID

from district42 import Props, SchemaVisitor
from district42 import SchemaVisitorReturnType as ReturnType
from district42.errors import (
    DeclarationError,
    make_already_declared_error,
    make_invalid_type_error,
)
from district42.types import Schema
from niltype import Nil, Nilable

__all__ = ("UUIDStrSchema", "UUIDStrProps",)


class UUIDStrProps(Props):
    @property
    def value(self) -> Nilable[str]:
        return self.get("value")


class UUIDStrSchema(Schema[UUIDStrProps]):
    def __accept__(self, visitor: SchemaVisitor[ReturnType], **kwargs: Any) -> ReturnType:
        return cast(ReturnType, visitor.visit_uuid_str(self, **kwargs))

    def __call__(self, /, value: str) -> "UUIDStrSchema":
        if not isinstance(value, str):
            raise make_invalid_type_error(self, value, (str,))

        try:
            UUID(value)
        except ValueError as e:
            raise DeclarationError(str(e)) from None

        if self.props.value is not Nil:
            raise make_already_declared_error(self)

        return self.__class__(self.props.update(value=value))
