from _typeshed import ConvertibleToFloat, ConvertibleToInt, Incomplete, ReadableBuffer, Unused
from collections.abc import Iterable, Sized
from datetime import datetime
from re import Pattern
from typing import Any, Generic, Literal, TypeVar, overload
from typing_extensions import TypeAlias

from openpyxl.descriptors import Strict
from openpyxl.descriptors.serialisable import Serialisable
from openpyxl.drawing.fill import Blip
from openpyxl.worksheet.cell_range import CellRange, MultiCellRange

_T = TypeVar("_T")
_P = TypeVar("_P", str, ReadableBuffer)
_N = TypeVar("_N", bound=bool)
_L = TypeVar("_L", bound=Sized)
_M = TypeVar("_M", int, float)

_ExpectedTypeParam: TypeAlias = type[_T] | tuple[type[_T], ...]
_ConvertibleToMultiCellRange: TypeAlias = MultiCellRange | str | Iterable[CellRange]
# Since everything is convertible to a bool, this restricts to only intended expected types of intended literals
_ConvertibleToBool: TypeAlias = bool | str | int | None  # True | False | "true" | "t" | "false" | "f" | 1 | 0 | None

class Descriptor(Generic[_T]):
    name: str | None
    def __init__(self, name: str | None = None, **kw: object) -> None: ...
    def __get__(self, instance: Serialisable | Strict, cls: type | None) -> _T: ...
    def __set__(self, instance: Serialisable | Strict, value: _T) -> None: ...

class Typed(Descriptor[_T], Generic[_T, _N]):
    __doc__: str
    # Members optional in __init__
    expected_type: type[_T]
    allow_none: _N
    nested: bool

    @overload
    def __init__(
        self: Typed[_T, Literal[True]],
        name: str | None = None,
        *,
        expected_type: _ExpectedTypeParam[_T],
        allow_none: Literal[True],
        nested: bool = False,
    ) -> None: ...
    @overload
    def __init__(
        self: Typed[_T, Literal[False]],
        name: str | None = None,
        *,
        expected_type: _ExpectedTypeParam[_T],
        allow_none: Literal[False] = False,
        nested: bool = False,
    ) -> None: ...
    @overload
    def __get__(self: Typed[_T, Literal[True]], instance: Serialisable | Strict, cls: type | None = None) -> _T | None: ...
    @overload
    def __get__(self: Typed[_T, Literal[False]], instance: Serialisable | Strict, cls: type | None = None) -> _T: ...
    @overload
    def __set__(self: Typed[_T, Literal[True]], instance: Serialisable | Strict, value: _T | None) -> None: ...
    @overload
    def __set__(self: Typed[_T, Literal[False]], instance: Serialisable | Strict, value: _T) -> None: ...

class Convertible(Typed[_T, _N]):
    @overload
    def __init__(
        self: Convertible[_T, Literal[True]],
        name: str | None = None,
        *,
        expected_type: _ExpectedTypeParam[_T],
        allow_none: Literal[True],
    ) -> None: ...
    @overload
    def __init__(
        self: Convertible[_T, Literal[False]],
        name: str | None = None,
        *,
        expected_type: _ExpectedTypeParam[_T],
        allow_none: Literal[False] = False,
    ) -> None: ...
    # NOTE: It is currently impossible to make a generic based on the parameter type of another generic
    # So we implement explicitly the types used internally
    # MultiCellRange
    @overload
    def __set__(
        self: Convertible[MultiCellRange, Literal[True]],
        instance: Serialisable | Strict,
        value: _ConvertibleToMultiCellRange | None,
    ) -> None: ...
    @overload
    def __set__(
        self: Convertible[MultiCellRange, Literal[False]], instance: Serialisable | Strict, value: _ConvertibleToMultiCellRange
    ) -> None: ...
    # str | Blip
    @overload
    def __set__(
        self: Convertible[str, _N] | Convertible[Blip, _N],
        instance: Serialisable | Strict,
        value: object,  # Not[None] when _N = False
    ) -> None: ...
    # bool
    @overload
    def __set__(self: Convertible[bool, _N], instance: Serialisable | Strict, value: _ConvertibleToBool) -> None: ...
    # int
    @overload
    def __set__(
        self: Convertible[int, Literal[True]], instance: Serialisable | Strict, value: ConvertibleToInt | None
    ) -> None: ...
    @overload
    def __set__(self: Convertible[int, Literal[False]], instance: Serialisable | Strict, value: ConvertibleToInt) -> None: ...
    # float
    @overload
    def __set__(
        self: Convertible[float, Literal[True]], instance: Serialisable | Strict, value: ConvertibleToFloat | None
    ) -> None: ...
    @overload
    def __set__(self: Convertible[float, Literal[False]], instance: Serialisable | Strict, value: ConvertibleToFloat) -> None: ...
    # Anything else
    @overload
    def __set__(self: Convertible[_T, Literal[True]], instance: Serialisable | Strict, value: _T | int | Any | None) -> None: ...

class Max(Convertible[_M, _N]):
    expected_type: type[_M]
    allow_none: _N
    max: float
    @overload
    def __init__(
        self: Max[int, Literal[True]], *, expected_type: _ExpectedTypeParam[int], allow_none: Literal[True], max: float
    ) -> None: ...
    @overload
    def __init__(
        self: Max[int, Literal[False]], *, expected_type: _ExpectedTypeParam[int], allow_none: Literal[False] = False, max: float
    ) -> None: ...
    # mypy can't infer type from `expected_type = float` (pyright can), so we have to add extra overloads
    @overload
    def __init__(
        self: Max[float, Literal[True]], *, expected_type: _ExpectedTypeParam[float] = ..., allow_none: Literal[True], max: float
    ) -> None: ...
    @overload
    def __init__(
        self: Max[float, Literal[False]],
        *,
        expected_type: _ExpectedTypeParam[float] = ...,
        allow_none: Literal[False] = False,
        max: float,
    ) -> None: ...
    @overload  # type: ignore[override]  # Different restrictions
    def __set__(self: Max[int, Literal[True]], instance: Serialisable | Strict, value: ConvertibleToInt | None) -> None: ...
    @overload
    def __set__(self: Max[int, Literal[False]], instance: Serialisable | Strict, value: ConvertibleToInt) -> None: ...
    @overload
    def __set__(self: Max[float, Literal[True]], instance: Serialisable | Strict, value: ConvertibleToFloat | None) -> None: ...
    @overload
    def __set__(self: Max[float, Literal[False]], instance: Serialisable | Strict, value: ConvertibleToFloat) -> None: ...

class Min(Convertible[_M, _N]):
    expected_type: type[_M]
    allow_none: _N
    min: float
    @overload
    def __init__(
        self: Min[int, Literal[True]], *, expected_type: _ExpectedTypeParam[int], allow_none: Literal[True], min: float
    ) -> None: ...
    @overload
    def __init__(
        self: Min[int, Literal[False]], *, expected_type: _ExpectedTypeParam[int], allow_none: Literal[False] = False, min: float
    ) -> None: ...
    # mypy can't infer type from `expected_type = float` (pyright can), so we have to add extra overloads
    @overload
    def __init__(
        self: Min[float, Literal[True]], *, expected_type: _ExpectedTypeParam[float] = ..., allow_none: Literal[True], min: float
    ) -> None: ...
    @overload
    def __init__(
        self: Min[float, Literal[False]],
        *,
        expected_type: _ExpectedTypeParam[float] = ...,
        allow_none: Literal[False] = False,
        min: float,
    ) -> None: ...
    @overload  # type: ignore[override]  # Different restrictions
    def __set__(self: Min[int, Literal[True]], instance: Serialisable | Strict, value: ConvertibleToInt | None) -> None: ...
    @overload
    def __set__(self: Min[int, Literal[False]], instance: Serialisable | Strict, value: ConvertibleToInt) -> None: ...
    @overload
    def __set__(self: Min[float, Literal[True]], instance: Serialisable | Strict, value: ConvertibleToFloat | None) -> None: ...
    @overload
    def __set__(self: Min[float, Literal[False]], instance: Serialisable | Strict, value: ConvertibleToFloat) -> None: ...

class MinMax(Min[_M, _N], Max[_M, _N]):
    expected_type: type[_M]
    allow_none: _N
    @overload
    def __init__(
        self: MinMax[int, Literal[True]],
        *,
        expected_type: _ExpectedTypeParam[int],
        allow_none: Literal[True],
        min: float,
        max: float,
    ) -> None: ...
    @overload
    def __init__(
        self: MinMax[int, Literal[False]],
        *,
        expected_type: _ExpectedTypeParam[int],
        allow_none: Literal[False] = False,
        min: float,
        max: float,
    ) -> None: ...
    # mypy can't infer type from `expected_type = float` (pyright can), so we have to add extra overloads
    @overload
    def __init__(
        self: MinMax[float, Literal[True]],
        *,
        expected_type: _ExpectedTypeParam[float] = ...,
        allow_none: Literal[True],
        min: float,
        max: float,
    ) -> None: ...
    @overload
    def __init__(
        self: MinMax[float, Literal[False]],
        *,
        expected_type: _ExpectedTypeParam[float] = ...,
        allow_none: Literal[False] = False,
        min: float,
        max: float,
    ) -> None: ...

class Set(Descriptor[_T]):
    __doc__: str
    values: Iterable[_T]
    def __init__(self, name: str | None = None, *, values: Iterable[_T]) -> None: ...
    def __set__(self, instance: Serialisable | Strict, value: _T) -> None: ...

class NoneSet(Set[_T | None]):
    def __init__(self, name: str | None = None, *, values: Iterable[_T | None]) -> None: ...
    def __set__(self, instance: Serialisable | Strict, value: _T | Literal["none"] | None) -> None: ...

class Integer(Convertible[int, _N]):
    allow_none: _N
    expected_type: type[int]
    @overload
    def __init__(self: Integer[Literal[True]], name: str | None = None, *, allow_none: Literal[True]) -> None: ...
    @overload
    def __init__(self: Integer[Literal[False]], name: str | None = None, *, allow_none: Literal[False] = False) -> None: ...

class Float(Convertible[float, _N]):
    allow_none: _N
    expected_type: type[float]
    @overload
    def __init__(self: Float[Literal[True]], name: str | None = None, *, allow_none: Literal[True]) -> None: ...
    @overload
    def __init__(self: Float[Literal[False]], name: str | None = None, *, allow_none: Literal[False] = False) -> None: ...

class Bool(Convertible[bool, _N]):
    expected_type: type[bool]
    allow_none: _N
    @overload
    def __init__(self: Bool[Literal[True]], name: str | None = None, *, allow_none: Literal[True]) -> None: ...
    @overload
    def __init__(self: Bool[Literal[False]], name: str | None = None, *, allow_none: Literal[False] = False) -> None: ...
    def __set__(self, instance: Serialisable | Strict, value: _ConvertibleToBool) -> None: ...

class String(Typed[str, _N]):
    allow_none: _N
    expected_type: type[str]
    @overload
    def __init__(self: String[Literal[True]], name: str | None = None, *, allow_none: Literal[True]) -> None: ...
    @overload
    def __init__(self: String[Literal[False]], name: str | None = None, *, allow_none: Literal[False] = False) -> None: ...

class Text(String[_N], Convertible[str, _N]): ...  # unused

class ASCII(Typed[bytes, _N]):  # unused
    expected_type: type[bytes]
    def __init__(self, name: str | None = None, *, allow_none: bool = False) -> None: ...

class Tuple(Typed[tuple[Any, ...], _N]):  # unused
    expected_type: type[tuple[Any, ...]]
    def __init__(self, name: str | None = None, *, allow_none: bool = False) -> None: ...

class Length(Descriptor[_L]):
    def __init__(self, name: Unused = None, *, length: int) -> None: ...
    def __set__(self, instance: Serialisable | Strict, value: _L) -> None: ...

class Default(Typed[_T, _N]):  # unused
    def __init__(
        self, name: Unused = None, *, expected_type: _ExpectedTypeParam[_T], allow_none: bool = False, defaults: Unused = {}
    ) -> None: ...
    def __call__(self) -> _T: ...

# Note: Aliases types can't be inferred. Anyway an alias means there's another option.
# Incomplete: Make it generic with explicit getter/setter type arguments?
class Alias(Descriptor[Incomplete]):
    alias: str
    def __init__(self, alias: str) -> None: ...
    def __set__(self, instance: Serialisable | Strict, value: Incomplete) -> None: ...
    def __get__(self, instance: Serialisable | Strict, cls: Unused): ...

class MatchPattern(Descriptor[_P], Generic[_P, _N]):
    allow_none: _N
    test_pattern: Pattern[bytes] | Pattern[str]
    pattern: str | Pattern[str] | bytes | Pattern[bytes]

    @overload  # str
    def __init__(
        self: MatchPattern[str, Literal[True]], name: str | None = None, *, pattern: str | Pattern[str], allow_none: Literal[True]
    ) -> None: ...
    @overload  # str | None
    def __init__(
        self: MatchPattern[str, Literal[False]],
        name: str | None = None,
        *,
        pattern: str | Pattern[str],
        allow_none: Literal[False] = False,
    ) -> None: ...
    @overload  # bytes
    def __init__(
        self: MatchPattern[ReadableBuffer, Literal[True]],
        name: str | None = None,
        *,
        pattern: bytes | Pattern[bytes],
        allow_none: Literal[True],
    ) -> None: ...
    @overload  # bytes | None
    def __init__(
        self: MatchPattern[ReadableBuffer, Literal[False]],
        name: str | None = None,
        *,
        pattern: bytes | Pattern[bytes],
        allow_none: Literal[False] = False,
    ) -> None: ...
    @overload
    def __get__(self: MatchPattern[_P, Literal[True]], instance: Serialisable | Strict, cls: type | None = None) -> _P | None: ...
    @overload
    def __get__(self: MatchPattern[_P, Literal[False]], instance: Serialisable | Strict, cls: type | None = None) -> _P: ...
    @overload
    def __set__(self: MatchPattern[_P, Literal[True]], instance: Serialisable | Strict, value: _P | None) -> None: ...
    @overload
    def __set__(self: MatchPattern[_P, Literal[False]], instance: Serialisable | Strict, value: _P) -> None: ...

class DateTime(Typed[datetime, _N]):
    allow_none: _N
    expected_type: type[datetime]
    @overload
    def __init__(self: DateTime[Literal[True]], name: str | None = None, *, allow_none: Literal[True]) -> None: ...
    @overload
    def __init__(self: DateTime[Literal[False]], name: str | None = None, *, allow_none: Literal[False] = False) -> None: ...
    @overload
    def __set__(self: DateTime[Literal[True]], instance: Serialisable | Strict, value: datetime | str | None) -> None: ...
    @overload
    def __set__(self: DateTime[Literal[False]], instance: Serialisable | Strict, value: datetime | str) -> None: ...
