{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoFieldSelectors #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

-- | The federal income tax forms

module Tax.Canada.Federal (Forms(..), fixFederalForms, formFieldsForProvince) where

import Control.Applicative ((<|>))
import Data.CAProvinceCodes qualified as Province
import Data.Fixed (Centi)
import Data.Text (Text)
import Data.Time (Day)
import Rank2 qualified
import Rank2.TH qualified
import Transformation.Shallow.TH qualified

import Tax.Canada.Federal.Schedule6 qualified as Schedule6
import Tax.Canada.Federal.Schedule6 (Schedule6, fixSchedule6, schedule6Fields)
import Tax.Canada.Federal.Schedule7 qualified
import Tax.Canada.Federal.Schedule7 (Schedule7, fixSchedule7, schedule7Fields)
import Tax.Canada.Federal.Schedule9 (Schedule9(line23_sum), fixSchedule9, schedule9Fields)
import Tax.Canada.Federal.Schedule11 (Schedule11(page1), Page1(line5_trainingClaim, line17_sum), fixSchedule11, schedule11Fields)
import Tax.Canada.T1 (fixT1, t1FieldsForProvince)
import Tax.Canada.T1.Types (T1(page4, page6, page8),
                            Page4(line_20800_RRSPDeduction), Page6(line32300, line34900), Page8(step6_RefundOrBalanceOwing),
                            Page8Step6(line_45300_CWB, line_45350_CTC), LanguageOfCorrespondence, MaritalStatus)
import Tax.FDF (Entry (Amount), FieldConst (Field), within)
import Tax.Util (fixEq)

-- | All supported federal forms
data Forms line = Forms{
   forall (line :: * -> *). Forms line -> T1 line
t1 :: T1 line,
   forall (line :: * -> *). Forms line -> Schedule6 line
schedule6 :: Schedule6 line,
   forall (line :: * -> *). Forms line -> Schedule7 line
schedule7 :: Schedule7 line,
   forall (line :: * -> *). Forms line -> Schedule9 line
schedule9 :: Schedule9 line,
   forall (line :: * -> *). Forms line -> Schedule11 line
schedule11 :: Schedule11 line}

deriving instance (Show (line Bool), Show (line Centi), Show (line Word), Show (line Text),
                   Show (line Rational), Show (line Province.Code), Show (line Day),
                   Show (line LanguageOfCorrespondence), Show (line MaritalStatus))
               => Show (Forms line)
deriving instance (Eq (line Bool), Eq (line Centi), Eq (line Word), Eq (line Text),
                   Eq (line Rational), Eq (line Province.Code), Eq (line Day),
                   Eq (line LanguageOfCorrespondence), Eq (line MaritalStatus))
               => Eq (Forms line)

Rank2.TH.deriveAll ''Forms
Transformation.Shallow.TH.deriveAll ''Forms

-- | Complete all the federal forms, also handling the inter-form field references.
fixFederalForms :: Forms Maybe -> Forms Maybe
fixFederalForms :: Forms Maybe -> Forms Maybe
fixFederalForms = (Forms Maybe -> Forms Maybe) -> Forms Maybe -> Forms Maybe
forall a. Eq a => (a -> a) -> a -> a
fixEq ((Forms Maybe -> Forms Maybe) -> Forms Maybe -> Forms Maybe)
-> (Forms Maybe -> Forms Maybe) -> Forms Maybe -> Forms Maybe
forall a b. (a -> b) -> a -> b
$ \Forms{T1 Maybe
$sel:t1:Forms :: forall (line :: * -> *). Forms line -> T1 line
t1 :: T1 Maybe
t1, Schedule6 Maybe
$sel:schedule6:Forms :: forall (line :: * -> *). Forms line -> Schedule6 line
schedule6 :: Schedule6 Maybe
schedule6, Schedule7 Maybe
$sel:schedule7:Forms :: forall (line :: * -> *). Forms line -> Schedule7 line
schedule7 :: Schedule7 Maybe
schedule7, Schedule9 Maybe
$sel:schedule9:Forms :: forall (line :: * -> *). Forms line -> Schedule9 line
schedule9 :: Schedule9 Maybe
schedule9, Schedule11 Maybe
$sel:schedule11:Forms :: forall (line :: * -> *). Forms line -> Schedule11 line
schedule11 :: Schedule11 Maybe
schedule11}-> Forms{
   $sel:t1:Forms :: T1 Maybe
t1 = HasCallStack => T1 Maybe -> T1 Maybe
T1 Maybe -> T1 Maybe
fixT1 T1 Maybe
t1{page4 = t1.page4{line_20800_RRSPDeduction = schedule7.page3.partC.line20_deduction},
                 page6 = t1.page6{line32300 = schedule11.page1.line17_sum, line34900 = schedule9.line23_sum},
                 page8 = t1.page8{step6_RefundOrBalanceOwing =
                                  t1.page8.step6_RefundOrBalanceOwing{line_45300_CWB = schedule6.page4.step3.line42_sum <|>
                                                                                       schedule6.page4.step2.line28_difference,
                                                                      line_45350_CTC = schedule11.page1.line5_trainingClaim}}},
   $sel:schedule6:Forms :: Schedule6 Maybe
schedule6 = Maybe (T1 Maybe) -> T1 Maybe -> Schedule6 Maybe -> Schedule6 Maybe
fixSchedule6 Maybe (T1 Maybe)
forall a. Maybe a
Nothing T1 Maybe
t1 Schedule6 Maybe
schedule6,
   $sel:schedule7:Forms :: Schedule7 Maybe
schedule7 = T1 Maybe -> Schedule7 Maybe -> Schedule7 Maybe
fixSchedule7 T1 Maybe
t1 Schedule7 Maybe
schedule7,
   $sel:schedule9:Forms :: Schedule9 Maybe
schedule9 = T1 Maybe -> Schedule9 Maybe -> Schedule9 Maybe
fixSchedule9 T1 Maybe
t1 Schedule9 Maybe
schedule9,
   $sel:schedule11:Forms :: Schedule11 Maybe
schedule11 = T1 Maybe -> Schedule11 Maybe -> Schedule11 Maybe
fixSchedule11 T1 Maybe
t1 Schedule11 Maybe
schedule11}

-- | The paths of all the fields in all federal forms, with the form key added as the head of every field path.
formFieldsForProvince :: Province.Code -> Forms FieldConst
formFieldsForProvince :: Code -> Forms FieldConst
formFieldsForProvince Code
p = Forms{
  $sel:t1:Forms :: T1 FieldConst
t1 = Text -> FieldConst a -> FieldConst a
forall x. Text -> FieldConst x -> FieldConst x
within Text
"T1" (forall {a}. FieldConst a -> FieldConst a)
-> T1 FieldConst -> T1 FieldConst
forall {k} (g :: (k -> *) -> *) (p :: k -> *) (q :: k -> *).
Functor g =>
(forall (a :: k). p a -> q a) -> g p -> g q
forall (p :: * -> *) (q :: * -> *).
(forall a. p a -> q a) -> T1 p -> T1 q
Rank2.<$> Code -> T1 FieldConst
t1FieldsForProvince Code
p,
  $sel:schedule6:Forms :: Schedule6 FieldConst
schedule6 = Text -> FieldConst a -> FieldConst a
forall x. Text -> FieldConst x -> FieldConst x
within Text
"Schedule6" (forall {a}. FieldConst a -> FieldConst a)
-> Schedule6 FieldConst -> Schedule6 FieldConst
forall {k} (g :: (k -> *) -> *) (p :: k -> *) (q :: k -> *).
Functor g =>
(forall (a :: k). p a -> q a) -> g p -> g q
forall (p :: * -> *) (q :: * -> *).
(forall a. p a -> q a) -> Schedule6 p -> Schedule6 q
Rank2.<$> Schedule6 FieldConst
schedule6Fields,
  $sel:schedule7:Forms :: Schedule7 FieldConst
schedule7 = Text -> FieldConst a -> FieldConst a
forall x. Text -> FieldConst x -> FieldConst x
within Text
"Schedule7" (forall {a}. FieldConst a -> FieldConst a)
-> Schedule7 FieldConst -> Schedule7 FieldConst
forall {k} (g :: (k -> *) -> *) (p :: k -> *) (q :: k -> *).
Functor g =>
(forall (a :: k). p a -> q a) -> g p -> g q
forall (p :: * -> *) (q :: * -> *).
(forall a. p a -> q a) -> Schedule7 p -> Schedule7 q
Rank2.<$> Schedule7 FieldConst
schedule7Fields,
  $sel:schedule9:Forms :: Schedule9 FieldConst
schedule9 = Text -> FieldConst a -> FieldConst a
forall x. Text -> FieldConst x -> FieldConst x
within Text
"Schedule9" (forall {a}. FieldConst a -> FieldConst a)
-> Schedule9 FieldConst -> Schedule9 FieldConst
forall {k} (g :: (k -> *) -> *) (p :: k -> *) (q :: k -> *).
Functor g =>
(forall (a :: k). p a -> q a) -> g p -> g q
forall (p :: * -> *) (q :: * -> *).
(forall a. p a -> q a) -> Schedule9 p -> Schedule9 q
Rank2.<$> Schedule9 FieldConst
schedule9Fields,
  $sel:schedule11:Forms :: Schedule11 FieldConst
schedule11 = Text -> FieldConst a -> FieldConst a
forall x. Text -> FieldConst x -> FieldConst x
within Text
"Schedule11" (forall {a}. FieldConst a -> FieldConst a)
-> Schedule11 FieldConst -> Schedule11 FieldConst
forall {k} (g :: (k -> *) -> *) (p :: k -> *) (q :: k -> *).
Functor g =>
(forall (a :: k). p a -> q a) -> g p -> g q
forall (p :: * -> *) (q :: * -> *).
(forall a. p a -> q a) -> Schedule11 p -> Schedule11 q
Rank2.<$> Schedule11 FieldConst
schedule11Fields}