import calendar
import datetime
from collections.abc import Generator
from typing import Literal, TypedDict, overload, type_check_only
from typing_extensions import Self

from .dates import BaseDate, HebrewDate

@type_check_only
class _MoladDict(TypedDict):
    weekday: int
    hours: int
    parts: int

@type_check_only
class _MoladAnnouncementDict(TypedDict):
    weekday: int
    hour: int
    minutes: int
    parts: int

class IllegalMonthError(ValueError):
    month: int
    def __init__(self, month: int) -> None: ...

class IllegalWeekdayError(ValueError):
    weekday: int
    def __init__(self, weekday: int) -> None: ...

class Year:
    year: int
    leap: bool
    def __init__(self, year: int) -> None: ...
    def __len__(self) -> int: ...
    def __eq__(self, other: object) -> bool: ...
    def __add__(self, other: int) -> Year: ...

    @overload
    def __sub__(self, other: int) -> Year: ...
    @overload
    def __sub__(self, other: Year) -> int: ...

    def __gt__(self, other: Year) -> bool: ...
    def __ge__(self, other: Year) -> bool: ...
    def __lt__(self, other: Year) -> bool: ...
    def __le__(self, other: Year) -> bool: ...
    def __iter__(self) -> Generator[int]: ...
    def monthscount(self) -> Literal[12, 13]: ...
    def itermonths(self) -> Generator[Month]: ...
    def iterdays(self) -> Generator[int]: ...
    def iterdates(self) -> Generator[HebrewDate]: ...
    @classmethod
    def from_date(cls, date: BaseDate) -> Self: ...
    @classmethod
    def from_pydate(cls, pydate: datetime.date) -> Self: ...
    def year_string(self, thousands: bool = False) -> str: ...

class Month:
    year: int
    month: int
    def __init__(self, year: int, month: int) -> None: ...
    def __len__(self) -> int: ...
    def __iter__(self) -> Generator[int]: ...
    def __eq__(self, other: object) -> bool: ...
    def __add__(self, other: int) -> Month: ...

    @overload
    def __sub__(self, other: int) -> Month: ...
    @overload
    def __sub__(self, other: Month) -> int: ...

    def __gt__(self, other: Month) -> bool: ...
    def __ge__(self, other: Month) -> bool: ...
    def __lt__(self, other: Month) -> bool: ...
    def __le__(self, other: Month) -> bool: ...
    @classmethod
    def from_date(cls, date: BaseDate) -> Month: ...
    @classmethod
    def from_pydate(cls, pydate: datetime.date) -> Month: ...
    def month_name(self, hebrew: bool = False) -> str: ...
    def month_string(self, thousands: bool = False) -> str: ...
    def starting_weekday(self) -> int: ...
    def iterdates(self) -> Generator[HebrewDate]: ...
    def molad(self) -> _MoladDict: ...
    def molad_announcement(self) -> _MoladAnnouncementDict: ...

def to_hebrew_numeral(num: int, thousands: bool = False, withgershayim: bool = True) -> str: ...

class HebrewCalendar(calendar.Calendar):
    hebrewnumerals: bool
    hebrewweekdays: bool
    hebrewmonths: bool
    hebrewyear: bool
    def __init__(
        self,
        firstweekday: int = 1,
        hebrewnumerals: bool = True,
        hebrewweekdays: bool = False,
        hebrewmonths: bool = False,
        hebrewyear: bool = False,
    ) -> None: ...

    @property
    def firstweekday(self) -> int: ...
    @firstweekday.setter
    def firstweekday(self, thefirstweekday: int) -> None: ...

    def iterweekdays(self) -> Generator[int]: ...
    def itermonthdates(self, year: int, month: int) -> Generator[HebrewDate]: ...  # type: ignore[override]
    def itermonthdays(self, year: int, month: int) -> Generator[int]: ...
    def itermonthdays2(self, year: int, month: int) -> Generator[tuple[int, int]]: ...
    def itermonthdays3(self, year: int, month: int) -> Generator[tuple[int, int, int]]: ...
    def itermonthdays4(self, year: int, month: int) -> Generator[tuple[int, int, int, int]]: ...
    def yeardatescalendar(self, year: int, width: int = 3) -> list[list[list[list[HebrewDate]]]]: ...  # type: ignore[override]
    def yeardays2calendar(self, year: int, width: int = 3) -> list[list[list[list[tuple[int, int]]]]]: ...
    def yeardayscalendar(self, year: int, width: int = 3) -> list[list[list[list[int]]]]: ...
    def monthdatescalendar(self, year: int, month: int) -> list[list[HebrewDate]]: ...  # type: ignore[override]

class HebrewHTMLCalendar(HebrewCalendar, calendar.HTMLCalendar):
    rtl: bool
    def __init__(
        self,
        firstweekday: int = 1,
        hebrewnumerals: bool = True,
        hebrewweekdays: bool = False,
        hebrewmonths: bool = False,
        hebrewyear: bool = False,
        rtl: bool = False,
    ) -> None: ...
    def formatday(self, day: int, weekday: int) -> str: ...
    def formatweekday(self, day: int) -> str: ...
    def formatyearnumber(self, theyear: int) -> int | str: ...
    def formatmonthname(self, theyear: int, themonth: int, withyear: bool = True) -> str: ...
    def formatmonth(self, theyear: int, themonth: int, withyear: bool = True) -> str: ...
    def formatyear(self, theyear: int, width: int = 3) -> str: ...

class HebrewTextCalendar(HebrewCalendar, calendar.TextCalendar):
    def formatday(self, day: int, weekday: int, width: int) -> str: ...
    def formatweekday(self, day: int, width: int) -> str: ...
    def formatmonthname(self, theyear: int, themonth: int, width: int = 0, withyear: bool = True) -> str: ...
    def formatyear(self, theyear: int, w: int = 2, l: int = 1, c: int = 6, m: int = 3) -> str: ...

def fast_day(date: BaseDate, hebrew: bool = False) -> str | None: ...
def festival(
    date: BaseDate, israel: bool = False, hebrew: bool = False, include_working_days: bool = True, prefix_day: bool = False
) -> str | None: ...
def holiday(date: BaseDate, israel: bool = False, hebrew: bool = False, prefix_day: bool = False) -> str | None: ...
