-- SPDX-FileCopyrightText: 2022 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later

Qualification
  -- INVARIANT: 2*refreshWithin < validDuration
  school              SchoolId                  --TODO: Ansprechpartner der Schule in Briefe erwähnen
  shorthand           (CI Text)
  name                (CI Text)
  description         StoredMarkup Maybe        -- user-defined large Html, ought to contain full description
  validDuration       Int Maybe                 -- > 0, qualification is valid indefinitely or for a specified number of months, use with addMonthsDay
  auditDuration       Int Maybe                 -- > 0, number of months to keep audit log and LmsUserIdents; or indefinitely  (dangerous, since LmsIdents may run out)
  refreshWithin       CalendarDiffDays Maybe    -- notify users about renewal within this number of month/days before expiry; to be used with addGregorianDurationClip
  refreshReminder     CalendarDiffDays Maybe    -- send a second notification about renewal within this number of month/days before expiry
  elearningStart      Bool                      -- automatically schedule e-refresher
  -- elearningOnly       Bool                   -- successful E-learing automatically increases validity. NO!  
  expiryNotification  Bool default=true         -- should expiryNotification be generated for this qualification?
  avsLicence          AvsLicence Maybe          -- if set, valid QualificationUsers are synchronized to AVS as a driving licence
  sapId               Text Maybe                -- if set, valid QualificationUsers with userCompanyPersonalNumber are transmitted via SAP interface under this id
  SchoolQualificationShort school shorthand            -- must be unique per school and shorthand
  SchoolQualificationName  school name                 -- must be unique per school and name
  -- across all schools, only one qualification may be a driving licence:
  UniqueQualificationAvsLicence avsLicence      !force
    -- NOTE: two NULL values are not equal for the purpose of Uniqueness constraints!
  deriving Eq Generic

-- TODOs:
--  - Enstehen Kosten, wenn Teilnehmer für KnowHow eingereiht werden, aber nicht am Kurs teilnehmen?
--    Falls ja, so sollte bei automatischem refresher vorher der Kunde durch FRADrive befragt werden?!
--    A: Der Inhaber per Email informieren!
--    A: Es kann gleich eine LMS Pin generiert und verschickt werden!
--  - Aufteilung Qualification "R" in zwei Teile: "R e-learning" und "R praxis" okay?

-- Besonderheiten:
--  - LmsIdent muss für alle Qualificationen einzigartig sein!
--  - Durchfallen wird mit UserList ständig erneut gesandt, bis Löschantrag gestellt wurde.
--  - Bestehen mit Result wird nur ein einziges mal gesendet! (Ausfallrisiko: keine Bestätigung der Kommunikation!)
--  - Explizites Löschen eines LmsIdent nach Success/Failure ist notwendig (feedback bei Block)
--  - LmsUser soll nur DELTA übermitteln. (GET Request will always return the same; until POST Request was processed!)
--  - PinReset==1 mit bestehendem Passwort kann problemlos erneut gesendet werden
--  - Flag "interner Mitarbeiter" wird von Know-How ignoriert / nicht ausgewertet (legacy)

QualificationPrecondition
  qualification     QualificationId OnDeleteCascade OnUpdateCascade -- AND: not unique, ie. qualification can have multiple required preconditions
  required          [QualificationId]           -- OR : alternatives, any one will suffice
  continuous        Bool                        -- expiring precondition removes qualification
  deriving Generic

-- TODO: connect Qualification with Exams!

QualificationEdit
  user            UserId
  time            UTCTime
  qualification   QualificationId OnDeleteCascade OnUpdateCascade
  deriving Generic

QualificationUser
  user              UserId OnDeleteCascade OnUpdateCascade
  qualification     QualificationId OnDeleteCascade OnUpdateCascade
  validUntil        Day                           -- addGregorianMonthsRollOver (toInteger renewalMonths) qualificationUserValidUntil
  lastRefresh       Day                           -- lastRefresh > validUntil possible, if Qualification^elearningOnly == False
  firstHeld         Day                           -- first time the qualification was earned, should never change
  scheduleRenewal   Bool  default=true            -- if false, no automatic renewal is scheduled and the qualification expires
  lastNotified      UTCTime default=now()         -- last notficiation about being invalid
  -- Reasons and temporary revocations are implemented through QualificationUserBlock
  -- TODO: adjust SAP interface to transmit end dates
  UniqueQualificationUser qualification user
  deriving Generic

QualificationUserBlock
  qualificationUser QualificationUserId OnDeleteCascade OnUpdateCascade
  unblock           Bool
  from              UTCTime  
  reason            Text  
  blocker           UserId Maybe
  deriving Eq Ord Read Show Generic

  -- LMS Interface Tables, need regular processing by background jobs, per QualificationId:
  --
  -- 1. Daily Job: Add to LmsUser daily all qualification holders with
  --          QualificationUserValidUntil >= now
  --       /\ QualificationUserValudUntil <= now + QualificationRefreshWithin    (time to schedule refresher)
  --       /\ not already enlisted
  --
  -- 2. REST GET User.csv:
  --     - where LmsUserReceived == Nothing \/ (LmsUserResetPin /\ LmsUserEnded == Nothing)
  --     - delete-flag: isJust LmsUserStatus
  --   Note: REST means that LmsUserResetPin and LmsUserDelete remain unchanged by this GET request!
  --
  -- 3. REST POST Userlist.csv: just save as is to LmsUserlist
  --
  -- 4. REST POST Ergebnisse.csv: just save as is to LmsResult
  --
  -- 5. When received: Job LmsUserlist:               -- Note: containment needs at-once processing
  --     - For all LmsUser:
  --         + if contained:
  --              set LmsUserReceived to Just now()
  --              if LmsUserlistFailed: set LmsUserStatus to Just LmsBlocked now
  --         + not contained, by LmsUserReceived is set: set LmsUserEnded to Just now()
  --     - move row to LmsAudit
  --
  -- 6. When received: Daily Job LmsResult:
  --     - set LmsUserReceived to Just now()             -- always
  --     - set LmsUserStatus   to Just LmsSuccess now    -- conditional
  --     -   and renew QualificationValidTo
  --     - move row to LmsAudit
  --
  -- 7. Daily Job: dequeue LMS Users
  --     - remove from LmsUser after audit Period has passed

LmsUser
  qualification     QualificationId OnDeleteCascade OnUpdateCascade
  user              UserId          OnDeleteCascade OnUpdateCascade
  ident             LmsIdent                      -- must be unique accross all LMS courses!
  pin               Text
  resetPin          Bool            default=false -- should pin be reset?
  datePin           UTCTime         default=now() -- time pin was created
  status            LmsStatus Maybe               -- open, success or failure; status should never change unless isNothing; isJust indicates lms is finished and user shall be deleted from LMS
  --toDelete encoded by Handler.Utils.LMS.lmsUserToDelete
  statusDay         Day Maybe                     -- date of status change; should be isJust iff isJust status; modelling as a separate table too bothersome, unlike qualification block
  started           UTCTime         default=now()
  received          UTCTime Maybe                 -- last acknowledgement by LMS
  notified          UTCTime Maybe                 -- last notified by FRADrive
  ended             UTCTime Maybe                 -- ident was deleted from LMS
  -- Primary ident -- newtype Key LmsUserId = LmsUserKey { unLmsUser :: Text } -- change LmsIdent -> Text. Do we want this?
  UniqueLmsIdent ident                            -- idents must be unique accross all qualifications, since idents are global within LMS!
  UniqueLmsQualificationUser qualification user   -- each user may be enrolled at most once per course
  deriving Generic

-- LmsUserStatus
--   lmsUser LmsUserId OnDeleteCascade OnUpdateCascade
--   result  LmsStatus -- data LmsStatus = LmsBlocked | LmsExpired | LmsSuccess
--   day     Day   
--   UniqueLmsUserStatus lmsUser -- enforcing uniqueness prohibits history
--   deriving Generic

-- LmsUserlist stores LMS upload for later processing only
LmsUserlist
  qualification     QualificationId OnDeleteCascade OnUpdateCascade
  ident             LmsIdent        
  failed            Bool
  timestamp         UTCTime         default=now()
  UniqueLmsUserlist qualification ident
  deriving Generic Show

-- LmsResult stores LMS upload for later processing only
LmsResult
  qualification     QualificationId OnDeleteCascade OnUpdateCascade
  ident             LmsIdent        
  success           Day             -- BEWARE: timezone is local as submitted by LMS
  timestamp         UTCTime         default=now()
  UniqueLmsResult qualification ident -- required by DBTable
  deriving Generic
