from datetime import date, datetime, time, timedelta
from decimal import Decimal
from typing import Any, Literal

import pydantic
from typing_extensions import Self


class Imports(pydantic.BaseModel):
    decimal: bool
    datetime: bool
    date: bool
    time: bool
    timedelta: bool
    optional: bool
    union: bool
    literal: bool
    field: bool
    config_dict: bool
    uses_json: bool
    self_type: bool

    @staticmethod
    def empty() -> "Imports":
        return Imports(
            decimal=False,
            datetime=False,
            date=False,
            time=False,
            timedelta=False,
            optional=False,
            union=False,
            literal=False,
            field=False,
            config_dict=False,
            uses_json=False,
            self_type=False,
        )

    def touch(self, field_type: type[Any]):
        if field_type in _DO_NOT_IMPORT:
            pass
        elif field_type is Decimal:
            self.decimal = True
        elif field_type is datetime:
            self.datetime = True
        elif field_type is date:
            self.date = True
        elif field_type is time:
            self.time = True
        elif field_type is timedelta:
            self.timedelta = True
        elif field_type is Literal:
            self.literal = True
        elif field_type is Self:
            self.self_type = True
        else:
            raise ValueError(f"Unsupported base type {field_type}")

    def imports(self) -> str:
        def filter(items: list[tuple[str, bool]]) -> list[str]:
            return [item[0] for item in items if item[1]]

        # import sys
        # import json
        # from datetime import date, datetime, time, timedelta
        # from decimal import Decimal
        # from typing import Optional, Union

        import_datetime = filter(
            [
                ("date", self.date),
                ("datetime", self.datetime),
                ("time", self.time),
                ("timedelta", self.timedelta),
            ]
        )
        import_decimal = filter(
            [
                ("Decimal", self.decimal),
            ]
        )
        import_typing = filter(
            [
                ("Literal", self.literal),
                ("Optional", self.optional),
                ("Union", self.union),
            ]
        )
        pydantic_imports = filter(
            [
                ("Field", self.field),
                ("ConfigDict", self.config_dict),
            ]
        )

        imports = ""

        if self.self_type:
            imports += "import sys\n"

        if self.uses_json:
            imports += "import json\n"
        if import_datetime:
            imports += f"from datetime import {', '.join(import_datetime)}\n"
        if import_decimal:
            imports += f"from decimal import {', '.join(import_decimal)}\n"
        if import_typing:
            imports += f"from typing import {', '.join(import_typing)}\n"
        if self.self_type:
            imports += (
                "if sys.version_info < (3, 11):\n"
                + "    from typing_extensions import Self\n"
                + "else:\n"
                + "    from typing import Self\n"
            )

        if self.uses_json or import_datetime or import_decimal or import_typing:
            imports += "\n"

        imports += "import pydantic"

        if pydantic_imports:
            imports += f"\nfrom pydantic import {', '.join(pydantic_imports)}"

        return imports

    def combine(self, value: Self) -> "Imports":
        return Imports(
            decimal=self.decimal or value.decimal,
            datetime=self.datetime or value.datetime,
            date=self.date or value.date,
            time=self.time or value.time,
            timedelta=self.timedelta or value.timedelta,
            optional=self.optional or value.optional,
            union=self.union or value.union,
            literal=self.literal or value.literal,
            field=self.field or value.field,
            config_dict=self.config_dict or value.config_dict,
            uses_json=self.uses_json or value.uses_json,
            self_type=self.self_type or value.self_type,
        )


_DO_NOT_IMPORT = {
    type(None),
    bool,
    int,
    float,
    complex,
    str,
    bytes,
    bytearray,
    list,
    set,
    frozenset,
    tuple,
    dict,
}
