#  holidays
#  --------
#  A fast, efficient Python library for generating country, province and state
#  specific sets of holidays on the fly. It aims to make determining whether a
#  specific date is a holiday as fast and flexible as possible.
#
#  Authors: Vacanza Team and individual contributors (see CONTRIBUTORS file)
#           dr-prodigy <dr.prodigy.github@gmail.com> (c) 2017-2023
#           ryanss <ryanssdev@icloud.com> (c) 2014-2017
#  Website: https://github.com/vacanza/holidays
#  License: MIT (see LICENSE file)

from collections.abc import Iterable
from datetime import date
from typing import Optional

from holidays.calendars.custom import _CustomCalendar
from holidays.calendars.gregorian import JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC
from holidays.helpers import _normalize_tuple

BAK_POYA = "BAK_POYA"
BINARA_POYA = "BINARA_POYA"
DURUTHU_POYA = "DURUTHU_POYA"
ESALA_POYA = "ESALA_POYA"
IL_POYA = "IL_POYA"
MEDIN_POYA = "MEDIN_POYA"
NAWAM_POYA = "NAWAM_POYA"
NIKINI_POYA = "NIKINI_POYA"
POSON_POYA = "POSON_POYA"
UNDUVAP_POYA = "UNDUVAP_POYA"
VAP_POYA = "VAP_POYA"
VESAK_POYA = "VESAK_POYA"


class _SinhalaLunar:
    """
    Sinhala Lunar calendar for 2003-2025 years.

    Their Buddhist Uposatha day calculation method is different from Thai LuniSolar
    and Buddhist (Mahayana) used in East Asia.

    Due to the fact that Poya (Uposatha) days are calculated astronomically
    based on how close a particular day is closest to full moon at noon, and that
    an extra month is added every 33 months interval, this is hardcoded for now.

    Adhi month dates are instead hardcoded in Sri Lanka country implementation.
    """

    START_YEAR = 2003
    END_YEAR = 2025

    BAK_POYA_DATES = {
        2003: (APR, 16),
        2004: (APR, 5),
        2005: (APR, 23),
        2006: (APR, 13),
        2007: (APR, 2),
        2008: (APR, 19),
        2009: (APR, 9),
        2010: (MAR, 29),
        2011: (APR, 17),
        2012: (APR, 6),
        2013: (APR, 25),
        2014: (APR, 14),
        2015: (APR, 3),
        2016: (APR, 21),
        2017: (APR, 10),
        2018: (MAR, 31),
        2019: (APR, 19),
        2020: (APR, 7),
        2021: (APR, 26),
        2022: (APR, 16),
        2023: (APR, 5),
        2024: (APR, 23),
        2025: (APR, 12),
    }

    BINARA_POYA_DATES = {
        2003: (SEP, 10),
        2004: (SEP, 28),
        2005: (SEP, 17),
        2006: (SEP, 7),
        2007: (SEP, 26),
        2008: (SEP, 14),
        2009: (SEP, 4),
        2010: (SEP, 22),
        2011: (SEP, 11),
        2012: (SEP, 29),
        2013: (SEP, 19),
        2014: (SEP, 8),
        2015: (SEP, 27),
        2016: (SEP, 16),
        2017: (SEP, 5),
        2018: (SEP, 24),
        2019: (SEP, 13),
        2020: (SEP, 1),
        2021: (SEP, 20),
        2022: (SEP, 10),
        2023: (SEP, 29),
        2024: (SEP, 17),
        2025: (SEP, 7),
    }

    DURUTHU_POYA_DATES = {
        2003: (JAN, 17),
        2004: (JAN, 7),
        2005: (JAN, 24),
        2006: (JAN, 13),
        2007: (JAN, 3),
        2008: (JAN, 22),
        2009: ((JAN, 10), (DEC, 31)),
        2011: (JAN, 19),
        2012: (JAN, 8),
        2013: (JAN, 26),
        2014: (JAN, 15),
        2015: (JAN, 4),
        2016: (JAN, 23),
        2017: (JAN, 12),
        2018: (JAN, 1),
        2019: (JAN, 20),
        2020: (JAN, 10),
        2021: (JAN, 28),
        2022: (JAN, 17),
        2023: (JAN, 6),
        2024: (JAN, 25),
        2025: (JAN, 13),
    }

    ESALA_POYA_DATES = {
        2003: (JUL, 13),
        2004: (JUL, 2),
        2005: (JUL, 21),
        2006: (JUL, 10),
        2007: (JUL, 29),
        2008: (JUL, 17),
        2009: (JUL, 6),
        2010: (JUL, 25),
        2011: (JUL, 14),
        2012: (JUL, 3),
        2013: (JUL, 22),
        2014: (JUL, 12),
        2015: (JUL, 31),
        2016: (JUL, 19),
        2017: (JUL, 8),
        2018: (JUL, 27),
        2019: (JUL, 16),
        2020: (JUL, 4),
        2021: (JUL, 23),
        2022: (JUL, 13),
        2023: (AUG, 1),
        2024: (JUL, 20),
        2025: (JUL, 10),
    }

    IL_POYA_DATES = {
        2003: (NOV, 8),
        2004: (NOV, 26),
        2005: (NOV, 15),
        2006: (NOV, 5),
        2007: (NOV, 24),
        2008: (NOV, 12),
        2009: (NOV, 2),
        2010: (NOV, 21),
        2011: (NOV, 10),
        2012: (NOV, 27),
        2013: (NOV, 17),
        2014: (NOV, 6),
        2015: (NOV, 25),
        2016: (NOV, 14),
        2017: (NOV, 3),
        2018: (NOV, 22),
        2019: (NOV, 12),
        2020: (NOV, 29),
        2021: (NOV, 18),
        2022: (NOV, 7),
        2023: (NOV, 26),
        2024: (NOV, 15),
        2025: (NOV, 5),
    }

    MEDIN_POYA_DATES = {
        2003: (MAR, 18),
        2004: (MAR, 6),
        2005: (MAR, 25),
        2006: (MAR, 14),
        2007: (MAR, 3),
        2008: (MAR, 21),
        2009: (MAR, 10),
        2010: (FEB, 28),
        2011: (MAR, 19),
        2012: (MAR, 7),
        2013: (MAR, 26),
        2014: (MAR, 16),
        2015: (MAR, 5),
        2016: (MAR, 22),
        2017: (MAR, 12),
        2018: (MAR, 1),
        2019: (MAR, 20),
        2020: (MAR, 9),
        2021: (MAR, 28),
        2022: (MAR, 17),
        2023: (MAR, 6),
        2024: (MAR, 24),
        2025: (MAR, 13),
    }

    NAWAM_POYA_DATES = {
        2003: (FEB, 16),
        2004: (FEB, 5),
        2005: (FEB, 23),
        2006: (FEB, 12),
        2007: (FEB, 1),
        2008: (FEB, 20),
        2009: (FEB, 9),
        2010: (JAN, 29),
        2011: (FEB, 17),
        2012: (FEB, 7),
        2013: (FEB, 25),
        2014: (FEB, 14),
        2015: (FEB, 3),
        2016: (FEB, 22),
        2017: (FEB, 10),
        2018: (JAN, 31),
        2019: (FEB, 19),
        2020: (FEB, 8),
        2021: (FEB, 26),
        2022: (FEB, 16),
        2023: (FEB, 5),
        2024: (FEB, 23),
        2025: (FEB, 12),
    }

    NIKINI_POYA_DATES = {
        2003: (AUG, 11),
        2004: (AUG, 29),
        2005: (AUG, 19),
        2006: (AUG, 9),
        2007: (AUG, 28),
        2008: (AUG, 16),
        2009: (AUG, 5),
        2010: (AUG, 24),
        2011: (AUG, 13),
        2012: (AUG, 1),
        2013: (AUG, 20),
        2014: (AUG, 10),
        2015: (AUG, 29),
        2016: (AUG, 17),
        2017: (AUG, 7),
        2018: (AUG, 25),
        2019: (AUG, 14),
        2020: (AUG, 3),
        2021: (AUG, 22),
        2022: (AUG, 11),
        2023: (AUG, 30),
        2024: (AUG, 19),
        2025: (AUG, 8),
    }

    POSON_POYA_DATES = {
        2003: (JUN, 14),
        2004: (JUN, 2),
        2005: (JUN, 21),
        2006: (JUN, 11),
        2007: (JUN, 30),
        2008: (JUN, 18),
        2009: (JUN, 7),
        2010: (JUN, 25),
        2011: (JUN, 15),
        2012: (JUN, 4),
        2013: (JUN, 23),
        2014: (JUN, 12),
        2015: (JUN, 2),
        2016: (JUN, 19),
        2017: (JUN, 8),
        2018: (JUN, 27),
        2019: (JUN, 16),
        2020: (JUN, 5),
        2021: (JUN, 24),
        2022: (JUN, 14),
        2023: (JUN, 3),
        2024: (JUN, 21),
        2025: (JUN, 10),
    }

    UNDUVAP_POYA_DATES = {
        2003: (DEC, 8),
        2004: (DEC, 26),
        2005: (DEC, 15),
        2006: (DEC, 4),
        2007: (DEC, 23),
        2008: (DEC, 12),
        2009: (DEC, 1),
        2010: (DEC, 20),
        2011: (DEC, 10),
        2012: (DEC, 27),
        2013: (DEC, 16),
        2014: (DEC, 6),
        2015: (DEC, 24),
        2016: (DEC, 13),
        2017: (DEC, 3),
        2018: (DEC, 22),
        2019: (DEC, 11),
        2020: (DEC, 29),
        2021: (DEC, 18),
        2022: (DEC, 7),
        2023: (DEC, 26),
        2024: (DEC, 14),
        2025: (DEC, 4),
    }

    VAP_POYA_DATES = {
        2003: (OCT, 9),
        2004: (OCT, 27),
        2005: (OCT, 17),
        2006: (OCT, 6),
        2007: (OCT, 25),
        2008: (OCT, 14),
        2009: (OCT, 3),
        2010: (OCT, 22),
        2011: (OCT, 11),
        2012: (OCT, 29),
        2013: (OCT, 18),
        2014: (OCT, 8),
        2015: (OCT, 27),
        2016: (OCT, 15),
        2017: (OCT, 5),
        2018: (OCT, 24),
        2019: (OCT, 13),
        2020: (OCT, 30),
        2021: (OCT, 20),
        2022: (OCT, 9),
        2023: (OCT, 28),
        2024: (OCT, 17),
        2025: (OCT, 6),
    }

    VESAK_POYA_DATES = {
        2003: (MAY, 15),
        2004: (MAY, 4),
        2005: (MAY, 23),
        2006: (MAY, 12),
        2007: (MAY, 1),
        2008: (MAY, 19),
        2009: (MAY, 8),
        2010: (MAY, 27),
        2011: (MAY, 17),
        2012: (MAY, 5),
        2013: (MAY, 24),
        2014: (MAY, 14),
        2015: (MAY, 3),
        2016: (MAY, 21),
        2017: (MAY, 10),
        2018: (APR, 29),
        2019: (MAY, 18),
        2020: (MAY, 7),
        2021: (MAY, 26),
        2022: (MAY, 15),
        2023: (MAY, 5),
        2024: (MAY, 23),
        2025: (MAY, 12),
    }

    def _get_holiday(self, holiday: str, year: int) -> tuple[Optional[date], bool]:
        estimated_dates = getattr(self, f"{holiday}_DATES", {})
        exact_dates = getattr(self, f"{holiday}_DATES_{_CustomCalendar.CUSTOM_ATTR_POSTFIX}", {})
        dt = exact_dates.get(year, estimated_dates.get(year, ()))
        return date(year, *dt) if dt else None, year not in exact_dates

    def _get_holiday_set(self, holiday: str, year: int) -> Iterable[tuple[date, bool]]:
        estimated_dates = getattr(self, f"{holiday}_DATES", {})
        exact_dates = getattr(self, f"{holiday}_DATES_{_CustomCalendar.CUSTOM_ATTR_POSTFIX}", {})
        for year in (year - 1, year):
            for dt in _normalize_tuple(exact_dates.get(year, estimated_dates.get(year, ()))):
                yield date(year, *dt), year not in exact_dates

    def bak_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(BAK_POYA, year)

    def binara_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(BINARA_POYA, year)

    def duruthu_poya_date(self, year: int) -> Iterable[tuple[date, bool]]:
        return self._get_holiday_set(DURUTHU_POYA, year)

    def esala_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(ESALA_POYA, year)

    def il_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(IL_POYA, year)

    def medin_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(MEDIN_POYA, year)

    def nawam_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(NAWAM_POYA, year)

    def nikini_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(NIKINI_POYA, year)

    def poson_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(POSON_POYA, year)

    def unduvap_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(UNDUVAP_POYA, year)

    def vap_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(VAP_POYA, year)

    def vesak_poya_date(self, year: int) -> tuple[Optional[date], bool]:
        return self._get_holiday(VESAK_POYA, year)


class _CustomSinhalaHolidays(_CustomCalendar, _SinhalaLunar):
    pass
