#  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 AUTHORS 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 gettext import gettext as tr
from typing import Union

from holidays.calendars.gregorian import APR, MAY, JUN, JUL, SEP, DEC
from holidays.groups import ChristianHolidays, InternationalHolidays, StaticHolidays
from holidays.observed_holiday_base import (
    ObservedHolidayBase,
    MON_TO_NEXT_TUE,
    SAT_SUN_TO_NEXT_MON,
    SAT_SUN_TO_NEXT_MON_TUE,
)


class UnitedKingdom(ObservedHolidayBase, ChristianHolidays, InternationalHolidays, StaticHolidays):
    """
    References:
    - https://en.wikipedia.org/wiki/Public_holidays_in_the_United_Kingdom
    - https://archive.org/details/treatiseonbanki00walk/page/334/mode/2up
    - https://www.gov.uk/bank-holidays
    - https://www.timeanddate.com/holidays/uk/

    The Anniversary of the Battle of the Boyne bank holiday is proclaimed annually by the
    Secretary of State for Northern Ireland.

    In-Lieu observance was first provided in the Bank Holidays Extension Act 1875.
    """

    country = "GB"
    default_language = "en_GB"
    # %s (observed).
    observed_label = tr("%s (observed)")
    subdivisions: Union[tuple[()], tuple[str, ...]] = (
        "ENG",  # England
        "NIR",  # Northern Ireland
        "SCT",  # Scotland
        "WLS",  # Wales
    )
    subdivisions_aliases = {
        "England": "ENG",
        "Northern Ireland": "NIR",
        "Scotland": "SCT",
        "Wales": "WLS",
    }
    supported_languages = ("en_GB", "en_US", "th")
    _deprecated_subdivisions = ("UK",)
    # Bank Holidays Act 1871
    start_year = 1872

    def __init__(self, *args, **kwargs):
        ChristianHolidays.__init__(self)
        InternationalHolidays.__init__(self)
        StaticHolidays.__init__(self, UnitedKingdomStaticHolidays)
        kwargs.setdefault("observed_rule", SAT_SUN_TO_NEXT_MON)
        # Bank Holidays Extension Act 1875
        kwargs.setdefault("observed_since", 1875)
        super().__init__(*args, **kwargs)

    def _populate_public_holidays(self) -> None:
        # Good Friday.
        self._add_good_friday(tr("Good Friday"))

        if self._year >= 1978:
            # May Day.
            name = tr("May Day")
            if self._year in {1995, 2020}:
                self._add_holiday_may_8(name)
            else:
                self._add_holiday_1st_mon_of_may(name)

        if self._year >= 1971:
            spring_bank_dates = {
                2002: (JUN, 4),
                2012: (JUN, 4),
                2022: (JUN, 2),
            }
            # Spring Bank Holiday.
            name = tr("Spring Bank Holiday")
            if self._year in spring_bank_dates:
                self._add_holiday(name, spring_bank_dates[self._year])
            else:
                self._add_holiday_last_mon_of_may(name)

    def _populate_subdiv_holidays(self):
        if self.subdiv != "SCT":
            if self._year >= 1975:
                # New Year's Day.
                self._add_observed(self._add_new_years_day(tr("New Year's Day")))

            self._add_observed(
                # Christmas Day.
                self._add_christmas_day(tr("Christmas Day")),
                rule=SAT_SUN_TO_NEXT_MON_TUE,
            )

            self._add_observed(
                # Boxing Day.
                self._add_christmas_day_two(tr("Boxing Day")),
                rule=SAT_SUN_TO_NEXT_MON_TUE,
            )

        super()._populate_subdiv_holidays()

    def _populate_subdiv_eng_public_holidays(self):
        # Easter Monday.
        self._add_easter_monday(tr("Easter Monday"))

        if self._year <= 1970:
            # Whit Monday.
            self._add_whit_monday(tr("Whit Monday"))

        if self._year >= 1971:
            # Late Summer Bank Holiday.
            self._add_holiday_last_mon_of_aug(tr("Late Summer Bank Holiday"))

    def _populate_subdiv_nir_public_holidays(self):
        if self._year >= 1903:
            # Saint Patrick's Day.
            self._add_observed(self._add_holiday_mar_17(tr("Saint Patrick's Day")))

        # Easter Monday.
        self._add_easter_monday(tr("Easter Monday"))

        if self._year <= 1970:
            # Whit Monday.
            self._add_whit_monday(tr("Whit Monday"))

        # Battle of the Boyne.
        self._add_observed(self._add_holiday_jul_12(tr("Battle of the Boyne")))

        if self._year >= 1971:
            # Late Summer Bank Holiday.
            self._add_holiday_last_mon_of_aug(tr("Late Summer Bank Holiday"))

    def _populate_subdiv_sct_public_holidays(self):
        # New Year's Day.
        jan_1 = self._add_new_years_day(tr("New Year's Day"))

        self._add_observed(
            # New Year Holiday.
            self._add_new_years_day_two(tr("New Year Holiday")),
            rule=SAT_SUN_TO_NEXT_MON_TUE + MON_TO_NEXT_TUE,
        )
        self._add_observed(jan_1)

        # Summer Bank Holiday.
        self._add_holiday_1st_mon_of_aug(tr("Summer Bank Holiday"))

        if self._year >= 2006:
            # Saint Andrew's Day.
            self._add_observed(self._add_holiday_nov_30(tr("Saint Andrew's Day")))

        self._add_observed(
            # Christmas Day.
            self._add_christmas_day(tr("Christmas Day")),
            rule=SAT_SUN_TO_NEXT_MON_TUE if self._year >= 1974 else SAT_SUN_TO_NEXT_MON,
        )

        if self._year >= 1974:
            self._add_observed(
                # Boxing Day.
                self._add_christmas_day_two(tr("Boxing Day")),
                rule=SAT_SUN_TO_NEXT_MON_TUE,
            )

    def _populate_subdiv_wls_public_holidays(self):
        # Easter Monday.
        self._add_easter_monday(tr("Easter Monday"))

        if self._year <= 1970:
            # Whit Monday.
            self._add_whit_monday(tr("Whit Monday"))

        if self._year >= 1971:
            # Late Summer Bank Holiday.
            self._add_holiday_last_mon_of_aug(tr("Late Summer Bank Holiday"))


class UK(UnitedKingdom):
    pass


class GB(UnitedKingdom):
    pass


class GBR(UnitedKingdom):
    pass


class UnitedKingdomStaticHolidays:
    special_public_holidays = {
        # Silver Jubilee of Elizabeth II.
        1977: (JUN, 7, tr("Silver Jubilee of Elizabeth II")),
        # Wedding of Charles and Diana.
        1981: (JUL, 29, tr("Wedding of Charles and Diana")),
        # Millennium Celebrations.
        1999: (DEC, 31, tr("Millennium Celebrations")),
        # Golden Jubilee of Elizabeth II.
        2002: (JUN, 3, tr("Golden Jubilee of Elizabeth II")),
        # Wedding of William and Catherine.
        2011: (APR, 29, tr("Wedding of William and Catherine")),
        # Diamond Jubilee of Elizabeth II.
        2012: (JUN, 5, tr("Diamond Jubilee of Elizabeth II")),
        2022: (
            # Platinum Jubilee of Elizabeth II.
            (JUN, 3, tr("Platinum Jubilee of Elizabeth II")),
            # State Funeral of Queen Elizabeth II.
            (SEP, 19, tr("State Funeral of Queen Elizabeth II")),
        ),
        # Coronation of Charles III.
        2023: (MAY, 8, tr("Coronation of Charles III")),
    }
