import sys
from _typeshed import Unused
from datetime import datetime, timedelta, tzinfo
from typing import Any, ClassVar, Literal, Protocol, TypeVar, type_check_only
from typing_extensions import Self

from ..relativedelta import relativedelta
from ._common import _tzinfo, enfold as enfold, tzrangebase

if sys.platform == "win32":
    from .win import tzwin as tzwin, tzwinlocal as tzwinlocal
else:
    tzwin: None
    tzwinlocal: None

_DateTimeT = TypeVar("_DateTimeT", bound=datetime)

ZERO: timedelta
EPOCH: datetime
EPOCHORDINAL: int

class tzutc(tzinfo):
    def utcoffset(self, dt: datetime | None) -> timedelta | None: ...
    def dst(self, dt: datetime | None) -> timedelta | None: ...
    def tzname(self, dt: datetime | None) -> str: ...
    def is_ambiguous(self, dt: datetime | None) -> bool: ...
    def fromutc(self, dt: _DateTimeT) -> _DateTimeT: ...
    def __eq__(self, other: object) -> bool: ...
    __hash__: ClassVar[None]  # type: ignore[assignment]
    def __ne__(self, other: object) -> bool: ...
    __reduce__ = object.__reduce__

UTC: tzutc

class tzoffset(tzinfo):
    def __init__(self, name: str | None, offset: float | timedelta) -> None: ...
    def utcoffset(self, dt: datetime | None) -> timedelta | None: ...
    def dst(self, dt: datetime | None) -> timedelta | None: ...
    def is_ambiguous(self, dt: datetime | None) -> bool: ...
    def tzname(self, dt: datetime | None) -> str: ...
    def fromutc(self, dt: _DateTimeT) -> _DateTimeT: ...
    def __eq__(self, other: object) -> bool: ...
    __hash__: ClassVar[None]  # type: ignore[assignment]
    def __ne__(self, other: object) -> bool: ...
    __reduce__ = object.__reduce__
    @classmethod
    def instance(cls, name: str | None, offset: float | timedelta) -> tzoffset: ...

class tzlocal(_tzinfo):
    def __init__(self) -> None: ...
    def utcoffset(self, dt: datetime | None) -> timedelta | None: ...
    def dst(self, dt: datetime | None) -> timedelta | None: ...
    def tzname(self, dt: datetime | None) -> str: ...
    def is_ambiguous(self, dt: datetime | None) -> bool: ...
    def __eq__(self, other: object) -> bool: ...
    __hash__: ClassVar[None]  # type: ignore[assignment]
    def __ne__(self, other: object) -> bool: ...
    __reduce__ = object.__reduce__

class _ttinfo:
    __slots__ = ["offset", "delta", "isdst", "abbr", "isstd", "isgmt", "dstoffset"]
    offset: float
    delta: timedelta
    isdst: bool
    abbr: str
    isstd: bool
    isgmt: bool
    dstoffset: timedelta
    def __init__(self) -> None: ...
    def __eq__(self, other: object) -> bool: ...
    __hash__: ClassVar[None]  # type: ignore[assignment]
    def __ne__(self, other: object) -> bool: ...

@type_check_only
class _TZFileReader(Protocol):
    # optional attribute:
    # name: str
    def read(self, size: int, /) -> bytes: ...
    def seek(self, target: int, whence: Literal[1], /) -> object: ...

class tzfile(_tzinfo):
    def __init__(self, fileobj: str | _TZFileReader, filename: str | None = None) -> None: ...
    def is_ambiguous(self, dt: datetime | None, idx: int | None = None) -> bool: ...
    def utcoffset(self, dt: datetime | None) -> timedelta | None: ...
    def dst(self, dt: datetime | None) -> timedelta | None: ...
    def tzname(self, dt: datetime | None) -> str: ...
    def __eq__(self, other: object) -> bool: ...
    __hash__: ClassVar[None]  # type: ignore[assignment]
    def __ne__(self, other: object) -> bool: ...
    def __reduce__(self) -> tuple[type[Self], tuple[None, str], dict[str, Any]]: ...
    def __reduce_ex__(self, protocol: Unused) -> tuple[type[Self], tuple[None, str], dict[str, Any]]: ...

class tzrange(tzrangebase):
    hasdst: bool
    def __init__(
        self,
        stdabbr: str,
        stdoffset: int | timedelta | None = None,
        dstabbr: str | None = None,
        dstoffset: int | timedelta | None = None,
        start: relativedelta | None = None,
        end: relativedelta | None = None,
    ) -> None: ...
    def transitions(self, year: int) -> tuple[datetime, datetime]: ...
    def __eq__(self, other: object) -> bool: ...

class tzstr(tzrange):
    hasdst: bool
    def __init__(self, s: str, posix_offset: bool = False) -> None: ...
    @classmethod
    def instance(cls, name: str | None, offset: float | timedelta) -> tzoffset: ...

@type_check_only
class _ICalReader(Protocol):
    # optional attribute:
    # name: str
    def read(self) -> str: ...

class tzical:
    def __init__(self, fileobj: str | _ICalReader) -> None: ...
    def keys(self) -> list[str]: ...
    def get(self, tzid: str | None = None) -> tzinfo | None: ...

TZFILES: list[str]
TZPATHS: list[str]

def datetime_exists(dt: datetime, tz: tzinfo | None = None) -> bool: ...
def datetime_ambiguous(dt: datetime, tz: tzinfo | None = None) -> bool: ...
def resolve_imaginary(dt: _DateTimeT) -> _DateTimeT: ...

# Singleton type defined locally in a function. Calls itself "GettzFunc".
@type_check_only
class _GetTZ:
    def __call__(self, name: str | None = None) -> tzinfo | None: ...
    def set_cache_size(self, size: int) -> None: ...
    def cache_clear(self) -> None: ...
    def nocache(self, name: str | None) -> tzinfo | None: ...

gettz: _GetTZ
