import re
from datetime import datetime
from enum import Enum
from functools import lru_cache
from inspect import Parameter, signature
from typing import (
    TYPE_CHECKING,
    Any,
    Callable,
    Generator,
    Generic,
    List,
    Optional,
    Tuple,
    Type,
    TypeVar,
    Union,
)
from uuid import UUID

from fastapi import Depends, File, Form, Request, UploadFile, params
from fastapi_utils.api_model import APIModel
from fastapi_utils.camelcase import snake2camel
from makefun import wraps
from pydantic import (
    BaseModel as PydanticBaseModel,
    ConstrainedInt,
    ConstrainedStr,
    create_model,
)
from pydantic.datetime_parse import parse_datetime
from pydantic.fields import Undefined
from pydantic.main import ModelMetaclass
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.functions import FunctionElement
from sqlalchemy.sql.schema import Column
from sqlalchemy.types import DateTime
from sqlmodel import Field, SQLModel
from starlette.datastructures import MultiDict

from joj.horse.utils.base import is_uuid
from joj.horse.utils.errors import ErrorCode

if TYPE_CHECKING:
    Model = TypeVar("Model", bound="BaseModel")


class BaseModel(APIModel):
    """"""

    class Config:
        validate_all = True


class Operation(Enum):
    Create = "Create"
    Read = "Read"
    Update = "Update"
    Delete = "Delete"


class NoneNegativeInt(ConstrainedInt):
    ge = 0


# class PositiveInt(ConstrainedInt):
#     gt = 0


class PaginationLimit(NoneNegativeInt):
    le = 500


class LongStr(ConstrainedStr):
    max_length = 256


class NoneEmptyStr(ConstrainedStr):
    min_length = 1


class SearchQueryStr(ConstrainedStr):
    min_length = 2


class NoneEmptyLongStr(LongStr, NoneEmptyStr):
    pass


class UserInputURL(str):
    URL_RE = re.compile(r"[\w-]+", flags=re.ASCII)

    @classmethod
    def __get_validators__(
        cls,
    ) -> Generator[Callable[[Union[str, Any]], str], None, None]:
        yield cls.validate

    @classmethod
    def validate(cls, v: Optional[str]) -> LongStr:
        if not v:
            return LongStr("")
        if is_uuid(v):
            raise ValueError("url can not be uuid")
        if not UserInputURL.URL_RE.fullmatch(v):
            raise ValueError("url can only contains [a-zA-Z0-9_-]")
        return LongStr(v)


class LongText(ConstrainedStr):
    max_length = 65536


class utcnow(FunctionElement):
    type = DateTime()


@compiles(utcnow, "postgresql")
def pg_utcnow(element: Any, compiler: Any, **kwargs: Any) -> str:
    return "TIMEZONE('utc', CURRENT_TIMESTAMP)"


@compiles(utcnow, "mssql")
def ms_utcnow(element: Any, compiler: Any, **kwargs: Any) -> str:
    return "GETUTCDATE()"


def get_datetime_column(**kwargs: Any) -> Column:
    if "index" not in kwargs:
        kwargs["index"] = False
    if "nullable" not in kwargs:
        kwargs["nullable"] = False
    return Column(DateTime(timezone=True), **kwargs)


class UTCDatetime(datetime):
    """parse a datetime and convert in into UTC format"""

    @classmethod
    def __get_validators__(cls) -> Any:
        yield cls.validate

    @classmethod
    def validate(cls, v: Any) -> datetime:
        return datetime.fromtimestamp(parse_datetime(v).timestamp())


class BaseORMSchema(SQLModel, BaseModel):
    pass


class URLORMSchema(BaseORMSchema):
    url: str = Field("", description="(unique) url of the domain")


class URLCreateMixin(BaseModel):
    if not TYPE_CHECKING:
        url: UserInputURL = 