#  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 typing import Union

from holidays.calendars.gregorian import MON, TUE, WED, THU, FRI, SAT, SUN
from holidays.constants import GOVERNMENT, PUBLIC, UNOFFICIAL
from holidays.groups import ChristianHolidays, InternationalHolidays
from holidays.observed_holiday_base import (
    ObservedHolidayBase,
    ObservedRule,
    MON_TO_NEXT_TUE,
    FRI_TO_PREV_THU,
    SAT_TO_PREV_FRI,
    SUN_TO_NEXT_MON,
    SAT_SUN_TO_PREV_FRI,
    SAT_SUN_TO_NEXT_MON,
)

GA_IN_WASHINGTON_BIRTHDAY = ObservedRule(
    {MON: +1, TUE: -1, WED: -1, THU: +1, FRI: -1, SAT: -2, SUN: -2}
)


class UnitedStates(ObservedHolidayBase, ChristianHolidays, InternationalHolidays):
    """
    https://en.wikipedia.org/wiki/Public_holidays_in_the_United_States

    Federal holidays:
        - https://www.opm.gov/policy-data-oversight/pay-leave/federal-holidays/

    For Northern Mariana Islands (subdivision MP):
    - https://governor.gov.mp/archived-news/executive-actions-archive/memorandum-2022-legal-holidays/
    - https://webcache.googleusercontent.com/search?q=cache:C17_7FBgPtQJ:https://governor.gov.mp/archived-news/executive-actions-archive/memorandum-2022-legal-holidays/&hl=en&gl=sg&strip=1&vwsrc=0

    Columbus Day / Indigenous Peoples' Day history:
    - https://www.pewresearch.org/short-reads/2023/10/05/working-on-columbus-day-or-indigenous-peoples-day-it-depends-on-where-your-job-is/
    - https://www.officeholidays.com/holidays/usa/columbus-day-state-guide
    - https://en.wikipedia.org/wiki/Indigenous_Peoples%27_Day_(United_States)
    - https://www.sos.ri.gov/divisions/civics-and-education/reference-desk/ri-state-holidays
    - https://web.archive.org/web/20080831103521/http://www.dpa.ca.gov/personnel-policies/holidays.htm

    Frances Xavier Cabrini Day:
        - https://leg.colorado.gov/sites/default/files/2020a_1031_signed.pdf

    Washington's Birthday:
        - AK: https://doa.alaska.gov/calendar/
        - AL: https://admincode.legislature.state.al.us/administrative-code/670-X-12-.01
        - AR: https://arkleg.state.ar.us/Home/FTPDocument?path=/ACTS/2001/Public/ACT304.pdf
        - AZ: https://www.azleg.gov/ars/1/00301.htm
        - CA: https://www.sos.ca.gov/state-holidays
        - CO: https://leg.colorado.gov/sites/default/files/images/olls/crs2023-title-24.pdf
        - GA: https://www.gasupreme.us/court-information/holidays-2/
        - HI: https://www.capitol.hawaii.gov/hrscurrent/Vol01_Ch0001-0042F/HRS0008/HRS_0008-0001.htm
        - ID: https://idaho.gov/government/state-holidays/
        - IN: https://digital.statelib.lib.in.us/infoexpress/holidays.aspx
              https://www.in.gov/spd/benefits/state-holidays/
        - MD: https://msa.maryland.gov/msa/mdmanual/01glance/html/holidayl.html
        - MI: https://www.michigan.gov/som/government/state-holidays
        - MN: https://www.revisor.mn.gov/statutes/cite/645.44
        - MT: https://archive.legmt.gov/bills/mca/title_0010/chapter_0010/part_0020/section_0160/0010-0010-0020-0160.html
        - NJ: https://nj.gov/nj/about/facts/holidays/
        - OH: https://codes.ohio.gov/ohio-revised-code/section-1.14
        - OK: https://oklahoma.gov/omes/divisions/human-capital-management/employee-benefits/leave-holidays/holidays.html
        - OR: https://www.oregonlegislature.gov/bills_laws/ors/ors187.html
        - PA: https://www.legis.state.pa.us/WU01/LI/LI/US/PDF/1893/0/0138..PDF
        - PR: https://en.wikipedia.org/wiki/Public_holidays_in_Puerto_Rico
        - SC: https://www.scstatehouse.gov/code/t53c005.php
        - TN: https://www.tn.gov/about-tn/state-holidays.html
        - TX: https://www.tsl.texas.gov/ref/abouttx/holidays
        - UT: https://le.utah.gov/xcode/Title63G/Chapter1/63G-1-S301.html
        - VA: https://law.lis.virginia.gov/vacode/title2.2/chapter33/section2.2-3300/
        - VT: https://legislature.vermont.gov/statutes/section/01/007/00371
        - WA: https://app.leg.wa.gov/rcw/default.aspx?cite=1.16.050
        - WV: https://code.wvlegislature.gov/2-2-1/
        - WY: https://ai.wyo.gov/about-us/state-holidays-and-office-closures

    American Samoa holidays:
        - https://asbar.org/code-annotated/1-0501-public-holidays/
    """

    country = "US"
    supported_categories = (GOVERNMENT, PUBLIC, UNOFFICIAL)
    observed_label = "%s (observed)"
    subdivisions: Union[tuple[()], tuple[str, ...]] = (
        "AK",  # Alaska.
        "AL",  # Alabama.
        "AR",  # Arkansas.
        "AS",  # American Samoa.
        "AZ",  # Arizona.
        "CA",  # California.
        "CO",  # Colorado.
        "CT",  # Connecticut.
        "DC",  # District of Columbia.
        "DE",  # Delaware.
        "FL",  # Florida.
        "GA",  # Georgia.
        "GU",  # Guam.
        "HI",  # Hawaii.
        "IA",  # Iowa.
        "ID",  # Idaho.
        "IL",  # Illinois.
        "IN",  # Indiana.
        "KS",  # Kansas.
        "KY",  # Kentucky.
        "LA",  # Louisiana.
        "MA",  # Massachusetts.
        "MD",  # Maryland.
        "ME",  # Maine.
        "MI",  # Michigan.
        "MN",  # Minnesota.
        "MO",  # Missouri.
        "MP",  # Northern Mariana Islands.
        "MS",  # Mississippi.
        "MT",  # Montana.
        "NC",  # North Carolina.
        "ND",  # North Dakota.
        "NE",  # Nebraska.
        "NH",  # New Hampshire.
        "NJ",  # New Jersey.
        "NM",  # New Mexico.
        "NV",  # Nevada.
        "NY",  # New York.
        "OH",  # Ohio.
        "OK",  # Oklahoma.
        "OR",  # Oregon.
        "PA",  # Pennsylvania.
        "PR",  # Puerto Rico.
        "RI",  # Rhode Island.
        "SC",  # South Carolina.
        "SD",  # South Dakota.
        "TN",  # Tennessee.
        "TX",  # Texas.
        "UM",  # United States Minor Outlying Islands.
        "UT",  # Utah.
        "VA",  # Virginia.
        "VI",  # Virgin Islands, U.S..
        "VT",  # Vermont.
        "WA",  # Washington.
        "WI",  # Wisconsin.
        "WV",  # West Virginia.
        "WY",  # Wyoming.
    )

    _deprecated_subdivisions = (
        "FM",
        "MH",
        "PW",
    )
    start_year = 1777

    def __init__(self, *args, **kwargs):
        ChristianHolidays.__init__(self)
        InternationalHolidays.__init__(self)
        kwargs.setdefault("observed_rule", SAT_TO_PREV_FRI + SUN_TO_NEXT_MON)
        super().__init__(*args, **kwargs)

    def _populate_common(self, include_federal: bool = False):
        """
        Populates common US holidays.

        :param include_federal:
            Whether to include federal-specific holidays.
        """
        # New Year's Day
        if self._year >= 1871:
            name = "New Year's Day"
            self._add_observed(self._add_new_years_day(name))
            self._add_observed(self._next_year_new_years_day, name=name)

        if include_federal:
            # Birthday of Martin Luther King, Jr.
            if self._year >= 1986:
                self._add_holiday_3rd_mon_of_jan("Birthday of Martin Luther King, Jr.")

            # Washington's Birthday
            if self._year >= 1879:
                name = "Washington's Birthday"
                if self._year >= 1971:
                    self._add_holiday_3rd_mon_of_feb(name)
                else:
                    self._add_holiday_feb_22(name)

        # Memorial Day
        if self._year >= 1971:
            self._add_holiday_last_mon_of_may("Memorial Day")

        # Juneteenth Day
        if self._year >= 2021:
            self._add_observed(self._add_holiday_jun_19("Juneteenth National Independence Day"))

        # Independence Day
        if self._year >= 1871:
            self._add_observed(self._add_holiday_jul_4("Independence Day"))

        # Labor Day
        if self._year >= 1894:
            self._add_holiday_1st_mon_of_sep("Labor Day")

        if include_federal:
            # Columbus Day
            if self._year >= 1971:
                self._add_holiday_2nd_mon_of_oct("Columbus Day")

        # Veterans Day
        if self._year >= 1938:
            name = "Veterans Day" if self._year >= 1954 else "Armistice Day"
            if 1971 <= self._year <= 1977:
                self._add_holiday_4th_mon_of_oct(name)
            else:
                self._add_observed(self._add_remembrance_day(name))

        # Thanksgiving Day
        if self._year >= 1871:
            self._add_holiday_4th_thu_of_nov("Thanksgiving Day")

        # Christmas Day
        if self._year >= 1871:
            self._add_observed(self._add_christmas_day("Christmas Day"))

    def _populate_public_holidays(self):
        self._populate_common()

        # Memorial Day
        if 1888 <= self._year <= 1970:
            self._add_holiday_may_30("Memorial Day")

    def _add_christmas_eve_holiday(self):
        # Christmas Eve
        # If on Friday, observed on Thursday
        # If on Saturday or Sunday, observed on Friday
        name = "Christmas Eve"
        self._add_observed(
            self._add_christmas_eve(name), name=name, rule=FRI_TO_PREV_THU + SAT_SUN_TO_PREV_FRI
        )

    def _populate_subdiv_holidays(self):
        if PUBLIC not in self.categories:
            return None

        # Martin Luther King Jr. Day
        if self._year >= 1986 and self.subdiv not in {"AL", "AR", "AZ", "GA", "ID", "MS", "NH"}:
            self._add_holiday_3rd_mon_of_jan("Martin Luther King Jr. Day")

        # Washington's Birthday
        if self._year >= 1879 and self.subdiv not in {
            "AK",
            "AL",
            "AR",
            "AZ",
            "CA",
            "CO",
            "DE",
            "FL",
            "GA",
            "HI",
            "ID",
            "IN",
            "MD",
            "MN",
            "MT",
            "NJ",
            "NM",
            "OH",
            "OK",
            "OR",
            "PA",
            "PR",
            "SC",
            "TN",
            "TX",
            "UT",
            "VA",
            "VI",
            "VT",
            "WA",
            "WV",
            "WY",
        }:
            name = "Washington's Birthday"
            if self._year >= 1971:
                self._add_holiday_3rd_mon_of_feb(name)
            else:
                self._add_holiday_feb_22(name)

        # Columbus Day
        if self._year >= 1937 and (
            self.subdiv is None
            or self.subdiv
            in {
                "AS",
                "AZ",
                "CT",
                "GA",
                "ID",
                "IL",
                "IN",
                "MA",
                "MD",
                "MO",
                "MT",
                "NJ",
                "NY",
                "OH",
                "PA",
                "UT",
                "WV",
            }
        ):
            name = "Columbus Day"
            if self._year >= 1971:
                self._add_holiday_2nd_mon_of_oct(name)
            else:
                self._add_columbus_day(name)

        super()._populate_subdiv_holidays()

    def _populate_subdiv_ak_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

        # Seward's Day
        # No observance in 1921: https://chroniclingamerica.loc.gov/lccn/sn86072239/1922-03-29/ed-1/seq-8/
        if self._year >= 1918 and self._year != 1921:
            name = "Seward's Day"
            if self._year >= 1955:
                self._add_holiday_last_mon_of_mar(name)
            else:
                self._add_holiday_mar_30(name)

        # Indigenous Peoples' Day
        if self._year >= 1971:
            self._add_holiday_2nd_mon_of_oct(
                "Indigenous Peoples' Day" if self._year >= 2015 else "Columbus Day"
            )

        # Alaska Day
        # https://web.archive.org/web/20120502232826/http://www.alaskadispatch.com/article/happy-alaska-day-great-land
        if self._year >= 1917:
            self._add_observed(self._add_holiday_oct_18("Alaska Day"))

    def _populate_subdiv_al_public_holidays(self):
        # Martin Luther King Jr. Day
        if self._year >= 1986:
            self._add_holiday_3rd_mon_of_jan("Martin Luther King, Jr & Robert E. Lee's Birthday")

        # Washington's Birthday
        if self._year >= 1879:
            name = "George Washington & Thomas Jefferson's Birthday"
            if self._year >= 1971:
                self._add_holiday_3rd_mon_of_feb(name)
            else:
                self._add_holiday_feb_22(name)

        # Confederate Memorial Day
        if self._year >= 1866:
            self._add_holiday_4th_mon_of_apr("Confederate Memorial Day")

        # Jefferson Davis Birthday
        if self._year >= 1890:
            self._add_holiday_1st_mon_of_jun("Jefferson Davis Birthday")

        # Columbus Day / American Indian Heritage Day / Fraternal Day
        if self._year >= 1971:
            self._add_holiday_2nd_mon_of_oct(
                "Columbus Day / American Indian Heritage Day / Fraternal Day"
                if self._year >= 2000
                else "Columbus Day / Fraternal Day"
            )

    def _populate_subdiv_ar_public_holidays(self):
        # Martin Luther King Jr. Day
        if self._year >= 1986:
            self._add_holiday_3rd_mon_of_jan(
                "Martin Luther King Jr. Day"
                if self._year >= 2018
                else "Dr. Martin Luther King Jr. and Robert E. Lee's Birthdays"
            )

        # Washington's Birthday
        if self._year >= 1879:
            name = "George Washington's Birthday and Daisy Gatson Bates Day"
            if self._year >= 1971:
                self._add_holiday_3rd_mon_of_feb(name)
            else:
                self._add_holiday_feb_22(name)

    def _populate_subdiv_as_public_holidays(self):
        # American Samoa Flag Day
        if self._year >= 1901:
            self._add_observed(self._add_holiday_apr_17("American Samoa Flag Day"))

        # Manu'a Islands Cession Day
        if self._year >= 1983:
            self._add_observed(self._add_holiday_jul_16("Manu'a Islands Cession Day"))

        # White Sunday
        self._add_holiday_2nd_sun_of_oct("White Sunday")

    def _populate_subdiv_az_public_holidays(self):
        # Martin Luther King Jr. Day
        if self._year >= 1986:
            self._add_holiday_3rd_mon_of_jan("Dr. Martin Luther King Jr. / Civil Rights Day")

        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Lincoln/Washington Presidents' Day")

    def _populate_subdiv_ca_public_holidays(self):
        # Lincoln's Birthday
        if 1971 <= self._year <= 2009:
            self._add_observed(self._add_holiday_feb_12("Lincoln's Birthday"))

        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

        # Susan B. Anthony Day
        if self._year >= 2014:
            self._add_holiday_feb_15("Susan B. Anthony Day")

        # Cesar Chavez Day
        if self._year >= 1995:
            self._add_observed(self._add_holiday_mar_31("Cesar Chavez Day"), rule=SUN_TO_NEXT_MON)

        # Columbus Day
        if 1971 <= self._year <= 2008:
            self._add_holiday_2nd_mon_of_oct("Columbus Day")

        # Day After Thanksgiving
        if self._year >= 1975:
            self._add_holiday_1_day_past_4th_thu_of_nov("Day After Thanksgiving")

    def _populate_subdiv_co_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Washington-Lincoln Day")

        # Cesar Chavez Day
        if self._year >= 2001:
            self._add_holiday_mar_31("Cesar Chavez Day")

        # Frances Xavier Cabrini Day
        if self._year >= 2020:
            self._add_holiday_1st_mon_of_oct("Frances Xavier Cabrini Day")

    def _populate_subdiv_ct_public_holidays(self):
        # Lincoln's Birthday
        if self._year >= 1971:
            self._add_observed(self._add_holiday_feb_12("Lincoln's Birthday"))

        # Good Friday
        self._add_good_friday("Good Friday")

    def _populate_subdiv_dc_public_holidays(self):
        # Inauguration Day
        if self._year >= 1789 and (self._year - 1789) % 4 == 0:
            name = "Inauguration Day"
            self._add_observed(
                self._add_holiday_jan_20(name)
                if self._year >= 1937
                else self._add_holiday_mar_4(name),
                rule=SUN_TO_NEXT_MON,
            )

        # Emancipation Day
        if self._year >= 2005:
            self._add_observed(self._add_holiday_apr_16("Emancipation Day"))

        # Indigenous Peoples' Day
        if self._year >= 1971:
            self._add_holiday_2nd_mon_of_oct(
                "Indigenous Peoples' Day" if self._year >= 2019 else "Columbus Day"
            )

    def _populate_subdiv_de_public_holidays(self):
        # Good Friday
        self._add_good_friday("Good Friday")

        # Election Day
        if self._year >= 2008 and self._year % 2 == 0:
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

        # Day After Thanksgiving
        if self._year >= 1975:
            self._add_holiday_1_day_past_4th_thu_of_nov("Day After Thanksgiving")

    def _populate_subdiv_fl_public_holidays(self):
        # Susan B. Anthony Day
        if self._year >= 2011:
            self._add_holiday_feb_15("Susan B. Anthony Day")

        # Friday After Thanksgiving
        if self._year >= 1975:
            self._add_holiday_1_day_past_4th_thu_of_nov("Friday After Thanksgiving")

    def _populate_subdiv_ga_public_holidays(self):
        # Martin Luther King Jr. Day
        if self._year >= 1986:
            self._add_holiday_3rd_mon_of_jan(
                "Martin Luther King Jr. Day" if self._year >= 2012 else "Robert E. Lee's Birthday"
            )

        # Confederate Memorial Day
        if self._year >= 1866:
            name = "State Holiday" if self._year >= 2016 else "Confederate Memorial Day"
            if self._year == 2020:
                self._add_holiday_apr_10(name)
            else:
                self._add_holiday_4th_mon_of_apr(name)

        # Robert E. Lee's Birthday
        if self._year >= 1986:
            self._add_holiday_1_day_past_4th_thu_of_nov(
                "State Holiday" if self._year >= 2016 else "Robert E. Lee's Birthday"
            )

        # Washington's Birthday
        if self._year >= 1879:
            self._add_holiday(
                "Washington's Birthday",
                self._get_observed_date(self._christmas_day, rule=GA_IN_WASHINGTON_BIRTHDAY),
            )

    def _populate_subdiv_gu_public_holidays(self):
        # Guam Discovery Day
        if self._year >= 1970:
            self._add_holiday_1st_mon_of_mar("Guam Discovery Day")

        # Good Friday
        self._add_good_friday("Good Friday")

        # Liberation Day (Guam)
        if self._year >= 1945:
            self._add_holiday_jul_21("Liberation Day (Guam)")

        # All Souls' Day
        self._add_all_souls_day("All Souls' Day")

        # Lady of Camarin Day
        self._add_immaculate_conception_day("Lady of Camarin Day")

    def _populate_subdiv_hi_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

        # Prince Jonah Kuhio Kalanianaole Day
        if self._year >= 1949:
            self._add_observed(self._add_holiday_mar_26("Prince Jonah Kuhio Kalanianaole Day"))

        # Kamehameha Day
        if self._year >= 1872:
            jun_11 = self._add_holiday_jun_11("Kamehameha Day")
            if self._year >= 2011:
                self._add_observed(jun_11)

        # Statehood Day
        if self._year >= 1959:
            self._add_holiday_3rd_fri_of_aug("Statehood Day")

        # Election Day
        if self._year >= 2008 and self._year % 2 == 0:
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

    def _populate_subdiv_ia_public_holidays(self):
        # Lincoln's Birthday
        if self._year >= 1971:
            self._add_observed(self._add_holiday_feb_12("Lincoln's Birthday"))

    def _populate_subdiv_id_public_holidays(self):
        # Martin Luther King Jr. Day
        if self._year >= 1986:
            self._add_holiday_3rd_mon_of_jan(
                "Martin Luther King Jr. / Idaho Human Rights Day"
                if self._year >= 2006
                else "Martin Luther King Jr. Day"
            )

        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

    def _populate_subdiv_il_public_holidays(self):
        # Lincoln's Birthday
        if self._year >= 1971:
            self._add_observed(self._add_holiday_feb_12("Lincoln's Birthday"))

        # Casimir Pulaski Day
        if self._year >= 1978:
            self._add_holiday_1st_mon_of_mar("Casimir Pulaski Day")

        # Election Day
        if self._year >= 2008 and self._year % 2 == 0:
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

    def _populate_subdiv_in_public_holidays(self):
        # Good Friday
        self._add_good_friday("Good Friday")

        # Primary Election Day
        if self._year >= 2015 or (self._year >= 2006 and self._year % 2 == 0):
            self._add_holiday_1_day_past_1st_mon_of_may("Primary Election Day")

        # Election Day
        if self._year >= 2015 or (self._year >= 2008 and self._year % 2 == 0):
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

        # Lincoln's Birthday
        if self._year >= 2010:
            self._add_holiday_1_day_past_4th_thu_of_nov("Lincoln's Birthday")

        # Washington's Birthday
        if self._year >= 1879:
            self._add_holiday(
                "Washington's Birthday",
                self._get_observed_date(self._christmas_day, rule=GA_IN_WASHINGTON_BIRTHDAY),
            )

    def _populate_subdiv_ks_public_holidays(self):
        # Christmas Eve
        if self._year >= 2013:
            self._add_christmas_eve_holiday()

    def _populate_subdiv_ky_public_holidays(self):
        # Good Friday
        self._add_good_friday("Good Friday")

        # New Year's Eve
        if self._year >= 2013:
            self._add_observed(self._add_new_years_eve("New Year's Eve"))

    def _populate_subdiv_la_public_holidays(self):
        # Inauguration Day
        if self._year >= 1789 and (self._year - 1789) % 4 == 0:
            name = "Inauguration Day"
            self._add_observed(
                self._add_holiday_jan_20(name)
                if self._year >= 1937
                else self._add_holiday_mar_4(name),
                rule=SUN_TO_NEXT_MON,
            )

        # Mardi Gras
        if self._year >= 1857:
            self._add_carnival_tuesday("Mardi Gras")

        # Good Friday
        self._add_good_friday("Good Friday")

        # Election Day
        if self._year >= 2008 and self._year % 2 == 0:
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

    def _populate_subdiv_ma_public_holidays(self):
        # Evacuation Day
        if self._year >= 1901:
            self._add_observed(
                self._add_holiday_mar_17("Evacuation Day"), rule=SAT_SUN_TO_NEXT_MON
            )

        # Patriots' Day
        if self._year >= 1894:
            name = "Patriots' Day"
            if self._year >= 1969:
                self._add_holiday_3rd_mon_of_apr(name)
            else:
                self._add_holiday_apr_19(name)

    def _populate_subdiv_md_public_holidays(self):
        if self._year >= 1789 and (self._year - 1789) % 4 == 0:
            # Inauguration Day
            name = "Inauguration Day"
            self._add_observed(
                self._add_holiday_jan_20(name)
                if self._year >= 1937
                else self._add_holiday_mar_4(name),
                rule=SUN_TO_NEXT_MON,
            )

        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

        # American Indian Heritage Day
        if self._year >= 2008:
            self._add_holiday_1_day_past_4th_thu_of_nov("American Indian Heritage Day")

    def _populate_subdiv_me_public_holidays(self):
        # Patriots' Day
        if self._year >= 1894:
            name = "Patriots' Day"
            if self._year >= 1969:
                self._add_holiday_3rd_mon_of_apr("Patriots' Day")
            else:
                self._add_holiday_apr_19(name)

        # Indigenous Peoples' Day
        if self._year >= 1971:
            self._add_holiday_2nd_mon_of_oct(
                "Indigenous Peoples' Day" if self._year >= 2019 else "Columbus Day"
            )

    def _populate_subdiv_mi_public_holidays(self):
        if self._year >= 2013:
            # Christmas Eve
            self._add_christmas_eve_holiday()

            # New Year's Eve
            self._add_observed(self._add_new_years_eve("New Year's Eve"))

        # Election Day
        if self._year >= 2008 and self._year % 2 == 0:
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

        # Day after Thanksgiving
        if self._year >= 2017:
            self._add_holiday_1_day_past_4th_thu_of_nov("Day After Thanksgiving")

    def _populate_subdiv_mn_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Washington's and Lincoln's Birthday")

    def _populate_subdiv_mo_public_holidays(self):
        # Truman Day
        if self._year >= 1949:
            self._add_observed(self._add_holiday_may_8("Truman Day"))

    def _populate_subdiv_mp_public_holidays(self):
        # Commonwealth Covenant Day in Northern Mariana Islands
        self._add_observed(self._add_holiday_mar_24("Commonwealth Covenant Day"))

        # Good Friday
        self._add_good_friday("Good Friday")

        # Commonwealth Cultural Day in Northern Mariana Islands
        self._add_holiday_2nd_mon_of_oct("Commonwealth Cultural Day")

        # Election Day
        if self._year >= 2008 and self._year % 2 == 0:
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

        # Citizenship Day in Northern Mariana Islands
        self._add_observed(self._add_holiday_nov_4("Citizenship Day"))

        # Constitution Day in Northern Mariana Islands
        self._add_observed(self._add_holiday_dec_8("Constitution Day"))

    def _populate_subdiv_ms_public_holidays(self):
        # Martin Luther King Jr. Day
        if self._year >= 1986:
            self._add_holiday_3rd_mon_of_jan(
                "Dr. Martin Luther King Jr. and Robert E. Lee's Birthdays"
            )

        # Confederate Memorial Day
        if self._year >= 1866:
            self._add_holiday_last_mon_of_apr("Confederate Memorial Day")

    def _populate_subdiv_mt_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Lincoln's and Washington's Birthdays")

        # Election Day
        if self._year >= 2008 and self._year % 2 == 0:
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

    def _populate_subdiv_nc_public_holidays(self):
        # Good Friday
        self._add_good_friday("Good Friday")

        # Day After Thanksgiving
        if self._year >= 1975:
            self._add_holiday_1_day_past_4th_thu_of_nov("Day After Thanksgiving")

        # Christmas Eve
        if self._year >= 2013:
            self._add_christmas_eve_holiday()

        # Day After Christmas
        if self._year >= 2013:
            # If on Saturday or Sunday, observed on Monday
            # If on Monday, observed on Tuesday
            name = "Day After Christmas"
            self._add_observed(
                self._add_christmas_day_two(name),
                name=name,
                rule=MON_TO_NEXT_TUE + SAT_SUN_TO_NEXT_MON,
            )

    def _populate_subdiv_nd_public_holidays(self):
        pass

    def _populate_subdiv_ne_public_holidays(self):
        # Arbor Day
        if self._year >= 1875:
            name = "Arbor Day"
            if self._year >= 1989:
                self._add_holiday_last_fri_of_apr(name)
            else:
                self._add_holiday_apr_22(name)

        # Indigenous Peoples' Day
        if self._year >= 1971:
            self._add_holiday_2nd_mon_of_oct(
                "Indigenous Peoples' Day" if self._year >= 2020 else "Columbus Day"
            )

    def _populate_subdiv_nh_public_holidays(self):
        # Martin Luther King Jr. Day
        if self._year >= 1986:
            self._add_holiday_3rd_mon_of_jan("Dr. Martin Luther King Jr. / Civil Rights Day")

        # Election Day
        if self._year >= 2008 and self._year % 2 == 0:
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

        # Day After Thanksgiving
        if self._year >= 1975:
            self._add_holiday_1_day_past_4th_thu_of_nov("Day After Thanksgiving")

    def _populate_subdiv_nj_public_holidays(self):
        # Lincoln's Birthday
        if self._year >= 1971:
            self._add_observed(self._add_holiday_feb_12("Lincoln's Birthday"))

        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents Day")

        # Good Friday
        self._add_good_friday("Good Friday")

        # Election Day
        if self._year >= 2008 and self._year % 2 == 0:
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

    def _populate_subdiv_nm_public_holidays(self):
        # Indigenous Peoples' Day
        if self._year >= 1971:
            self._add_holiday_2nd_mon_of_oct(
                "Indigenous Peoples' Day" if self._year >= 2019 else "Columbus Day"
            )

        # Presidents' Day
        self._add_holiday_1_day_past_4th_thu_of_nov("Presidents' Day")

    def _populate_subdiv_nv_public_holidays(self):
        # Nevada Day
        if self._year >= 1933:
            name = "Nevada Day"
            self._add_observed(
                self._add_holiday_last_fri_of_oct(name)
                if self._year >= 2000
                else self._add_holiday_oct_31(name)
            )

        # Family Day
        self._add_holiday_1_day_past_4th_thu_of_nov("Family Day")

    def _populate_subdiv_ny_public_holidays(self):
        # Lincoln's Birthday
        if self._year >= 1971:
            self._add_observed(self._add_holiday_feb_12("Lincoln's Birthday"))

        # Susan B. Anthony Day
        if self._year >= 2004:
            self._add_holiday_feb_15("Susan B. Anthony Day")

        # Election Day
        if self._year >= 2015 or (self._year >= 2008 and self._year % 2 == 0):
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

    def _populate_subdiv_oh_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Washington-Lincoln Day")

    def _populate_subdiv_ok_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

        # Day After Thanksgiving
        if self._year >= 1975:
            self._add_holiday_1_day_past_4th_thu_of_nov("Day After Thanksgiving")

    def _populate_subdiv_or_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents Day")

    def _populate_subdiv_pa_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

        # Day After Thanksgiving
        self._add_holiday_1_day_past_4th_thu_of_nov("Day After Thanksgiving")

    def _populate_subdiv_pr_public_holidays(self):
        # Epiphany
        self._add_epiphany_day("Epiphany")

        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

        # Emancipation Day
        self._add_observed(self._add_holiday_mar_22("Emancipation Day"), rule=SUN_TO_NEXT_MON)

        # Good Friday
        self._add_good_friday("Good Friday")

        # Constitution Day
        self._add_observed(self._add_holiday_jul_25("Constitution Day"), rule=SUN_TO_NEXT_MON)

        # Discovery Day
        self._add_observed(self._add_holiday_nov_19("Discovery Day"), rule=SUN_TO_NEXT_MON)

    def _populate_subdiv_ri_public_holidays(self):
        # Victory Day
        if self._year >= 1948:
            self._add_holiday_2nd_mon_of_aug("Victory Day")

        # Indigenous Peoples' Day
        if self._year >= 1971:
            self._add_holiday_2nd_mon_of_oct(
                "Indigenous Peoples' Day / Columbus Day" if self._year >= 2022 else "Columbus Day"
            )

    def _populate_subdiv_sc_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("President's Day")

        # Confederate Memorial Day
        if self._year >= 1866:
            self._add_holiday_4th_mon_of_apr("Confederate Memorial Day")

    def _populate_subdiv_sd_public_holidays(self):
        # Native Americans' Day / Columbus Day
        if self._year >= 1937:
            name = "Native Americans' Day" if self._year >= 1990 else "Columbus Day"
            if self._year >= 1970:
                self._add_holiday_2nd_mon_of_oct(name)
            else:
                self._add_columbus_day(name)

    def _populate_subdiv_tn_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("President's Day")

        # Good Friday
        self._add_good_friday("Good Friday")

    def _populate_subdiv_tx_public_holidays(self):
        # Confederate Memorial Day
        if self._year >= 1931:
            self._add_holiday_jan_19("Confederate Memorial Day")

        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

        # Texas Independence Day
        if self._year >= 1874:
            self._add_holiday_mar_2("Texas Independence Day")

        # Cesar Chavez Day
        if self._year >= 2000:
            self._add_holiday_mar_31("Cesar Chavez Day")

        # Good Friday
        self._add_good_friday("Good Friday")

        # San Jacinto Day
        if self._year >= 1875:
            self._add_holiday_apr_21("San Jacinto Day")

        # Emancipation Day In Texas
        if self._year >= 1980:
            self._add_holiday_jun_19("Emancipation Day In Texas")

        # Lyndon Baines Johnson Day
        if self._year >= 1973:
            self._add_holiday_aug_27("Lyndon Baines Johnson Day")

        # Friday After Thanksgiving
        if self._year >= 1975:
            self._add_holiday_1_day_past_4th_thu_of_nov("Friday After Thanksgiving")

        # Christmas Eve
        if self._year >= 1981:
            self._add_christmas_eve_holiday()

        # Day After Christmas
        if self._year >= 1981:
            self._add_christmas_day_two("Day After Christmas")

    def _populate_subdiv_um_public_holidays(self):
        pass

    def _populate_subdiv_ut_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Washington and Lincoln Day")

        # Pioneer Day
        if self._year >= 1849:
            self._add_observed(self._add_holiday_jul_24("Pioneer Day"))

    def _populate_subdiv_va_public_holidays(self):
        # Lee Jackson Day
        if 1889 <= self._year <= 2020:
            name = "Lee Jackson Day"
            if self._year >= 2000:
                self._add_holiday_3_days_prior_3rd_mon_of_jan(name)
            elif self._year >= 1983:
                self._add_holiday_3rd_mon_of_jan(name)
            else:
                self._add_holiday_jan_19(name)

        # Inauguration Day
        if self._year >= 1789 and (self._year - 1789) % 4 == 0:
            name = "Inauguration Day"
            self._add_observed(
                self._add_holiday_jan_20(name)
                if self._year >= 1937
                else self._add_holiday_mar_4(name),
                rule=SUN_TO_NEXT_MON,
            )

        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("George Washington Day")

        # Indigenous Peoples' Day
        if self._year >= 1971:
            self._add_holiday_2nd_mon_of_oct(
                "Indigenous Peoples' Day" if self._year >= 2020 else "Columbus Day"
            )

    def _populate_subdiv_vi_public_holidays(self):
        # Three Kings Day
        self._add_epiphany_day("Three Kings Day")

        # Washington's Birthday
        if self._year >= 1879:
            name = "Presidents' Day"
            if self._year >= 1971:
                self._add_holiday_3rd_mon_of_feb(name)
            else:
                self._add_holiday_feb_22(name)

        # Transfer Day
        self._add_holiday_mar_31("Transfer Day")

        # Holy Thursday
        self._add_holy_thursday("Holy Thursday")

        # Good Friday
        self._add_good_friday("Good Friday")

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

        # Emancipation Day in US Virgin Islands
        self._add_holiday_jul_3("Emancipation Day")

        # Columbus Day
        if self._year >= 1937:
            name = "Columbus Day and Puerto Rico Friendship Day"
            if self._year >= 1970:
                self._add_holiday_2nd_mon_of_oct(name)
            else:
                self._add_columbus_day(name)

        # Liberty Day
        self._add_holiday_nov_1("Liberty Day")

        # Christmas Second Day
        self._add_christmas_day_two("Christmas Second Day")

    def _populate_subdiv_vt_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

        # Town Meeting Day
        if self._year >= 1800:
            self._add_holiday_1st_tue_of_mar("Town Meeting Day")

        # Bennington Battle Day
        if self._year >= 1778:
            self._add_observed(self._add_holiday_aug_16("Bennington Battle Day"))

    def _populate_subdiv_wa_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

    def _populate_subdiv_wi_public_holidays(self):
        # Susan B. Anthony Day
        if self._year >= 1976:
            self._add_holiday_feb_15("Susan B. Anthony Day")

        if self._year >= 2012:
            # Christmas Eve
            self._add_christmas_eve_holiday()

            # New Year's Eve
            self._add_observed(self._add_new_years_eve("New Year's Eve"))

    def _populate_subdiv_wv_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("Presidents' Day")

        # West Virginia Day
        if self._year >= 1927:
            self._add_observed(self._add_holiday_jun_20("West Virginia Day"))

        # Election Day
        if self._year >= 2008 and self._year % 2 == 0:
            self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")

        # Day After Thanksgiving
        if self._year >= 1975:
            self._add_holiday_1_day_past_4th_thu_of_nov("Day After Thanksgiving")

    def _populate_subdiv_wy_public_holidays(self):
        # Washington's Birthday
        if self._year >= 1971:
            self._add_holiday_3rd_mon_of_feb("President's Day")

    def _populate_government_holidays(self):
        # Federal holidays in the United States.
        self._populate_common(include_federal=True)

    def _populate_unofficial_holidays(self):
        # Very common celebrated cultural days, but no official observance.
        # Due to its nature, no in-lieus are observed.

        # Valentine's Day
        # While the modern iteration of Valentine's Day has started in the UK in 1797,
        # it wasn't until 1847 in the US that this started to be observed here.

        if self._year >= 1847:
            self._add_holiday_feb_14("Valentine's Day")

        # Saint Patrick's Day
        # Started in Boston in 1737 for the US.

        self._add_holiday_mar_17("Saint Patrick's Day")

        # Halloween
        # Halloween began in the US sometime around the 19th century.

        self._add_holiday_oct_31("Halloween")

        # Continental US non-Public dates

        if self.subdiv not in {"AS", "GU", "MP", "PR", "UM", "VI"}:
            # Groundhog Day
            # First observed on Feb 2 in 1886 in Continental US + Hawaii.

            if self._year >= 1886:
                self._add_holiday_feb_2("Groundhog Day")

            # Election Day
            # May be duplicates for certain states which has this as their actual public holiday.
            # The current US Presidential Election date pattern was codified in 1848 nationwide.

            if self._year >= 1848 and self._year % 4 == 0:
                self._add_holiday_1_day_past_1st_mon_of_nov("Election Day")


class US(UnitedStates):
    pass


class USA(UnitedStates):
    pass
