{-# LANGUAGE OverloadedStrings #-}

{- |
This module determines public holidays based on country code and year.
-}
module Holidays (
  ISO_3166_1_Alpha_3,
  Region,
  holidays,
  hday,
  Holiday (..),
)
where

import Data.Set qualified as S
import Data.Time
import Holidays.Base
import Holidays.DateTransform
import Holidays.Germany qualified as DEU
import Holidays.Mozambique qualified as MOZ
import Holidays.Namibia qualified as NAM
import Holidays.SouthAfrica qualified as ZAF
import Holidays.UnitedKingdom qualified as GBR
import Holidays.UnitedStates qualified as USA

{- |
Returns a set of public holidays based on the country code (ISO_3166_1_Alpha_3) and a specific year.
If a country is not supported an empty set is returned.
Country regions are also supported.

Examples:

@
holidays \"DEU\" [\"BW\",\"BY\",\"BE\"] 2025 -- Germany and various regions
holidays \"USA\" [] 2025
@
-}
holidays :: ISO_3166_1_Alpha_3 -> [Region] -> Year -> S.Set Holiday
holidays :: ISO_3166_1_Alpha_3 -> [ISO_3166_1_Alpha_3] -> Year -> Set Holiday
holidays ISO_3166_1_Alpha_3
countryCode [ISO_3166_1_Alpha_3]
regions Year
year =
  case ISO_3166_1_Alpha_3
countryCode of
    ISO_3166_1_Alpha_3
"DEU" -> [ISO_3166_1_Alpha_3] -> ([Year -> Holiday], [DateTransform])
DEU.holidays [ISO_3166_1_Alpha_3]
regions ([Year -> Holiday], [DateTransform]) -> Year -> Set Holiday
`apply` Year
year
    ISO_3166_1_Alpha_3
"GBR" -> ([Year -> Holiday], [DateTransform])
GBR.holidays ([Year -> Holiday], [DateTransform]) -> Year -> Set Holiday
`apply` Year
year
    ISO_3166_1_Alpha_3
"MOZ" -> ([Year -> Holiday], [DateTransform])
MOZ.holidays ([Year -> Holiday], [DateTransform]) -> Year -> Set Holiday
`apply` Year
year
    ISO_3166_1_Alpha_3
"NAM" -> ([Year -> Holiday], [DateTransform])
NAM.holidays ([Year -> Holiday], [DateTransform]) -> Year -> Set Holiday
`apply` Year
year
    ISO_3166_1_Alpha_3
"USA" -> ([Year -> Holiday], [DateTransform])
USA.holidays ([Year -> Holiday], [DateTransform]) -> Year -> Set Holiday
`apply` Year
year
    ISO_3166_1_Alpha_3
"ZAF" -> ([Year -> Holiday], [DateTransform])
ZAF.holidays ([Year -> Holiday], [DateTransform]) -> Year -> Set Holiday
`apply` Year
year
    ISO_3166_1_Alpha_3
_ -> Set Holiday
forall a. Set a
S.empty

-- | Applies year and date transformations to holidays
apply :: ([Year -> Holiday], [DateTransform]) -> Year -> S.Set Holiday
apply :: ([Year -> Holiday], [DateTransform]) -> Year -> Set Holiday
apply ([Year -> Holiday]
hs, [DateTransform]
transforms) Year
year =
  let validDays :: [Holiday]
validDays = (Holiday -> Bool) -> [Holiday] -> [Holiday]
forall a. (a -> Bool) -> [a] -> [a]
filter (Day -> Bool
validDay (Day -> Bool) -> (Holiday -> Day) -> Holiday -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Holiday -> Day
holidayValue) ([Holiday] -> [Holiday]) -> [Holiday] -> [Holiday]
forall a b. (a -> b) -> a -> b
$ ((Year -> Holiday) -> Holiday) -> [Year -> Holiday] -> [Holiday]
forall a b. (a -> b) -> [a] -> [b]
map (\Year -> Holiday
d -> Year -> Holiday
d Year
year) [Year -> Holiday]
hs -- apply year and filter out invalid days
  in  (Holiday -> Set Holiday -> Set Holiday)
-> Set Holiday -> [Holiday] -> Set Holiday
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\Holiday
d Set Holiday
ds -> Holiday -> Set Holiday -> Set Holiday
forall a. Ord a => a -> Set a -> Set a
S.insert ((DateTransform -> Holiday -> Holiday)
-> Holiday -> [DateTransform] -> Holiday
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\DateTransform
t Holiday
d' -> DateTransform
t Set Holiday
ds Holiday
d') Holiday
d [DateTransform]
transforms) Set Holiday
ds) Set Holiday
forall a. Set a
S.empty [Holiday]
validDays -- apply transforms to valid days