module Handler.ExternalExam.Edit
  ( getEEEditR, postEEEditR
  ) where

import Import

import Handler.Utils
import Handler.Utils.Invitations

import Handler.ExternalExam.Form
import Handler.ExternalExam.StaffInvite

import qualified Data.Set as Set
import qualified Data.Map as Map

import Jobs.Queue


getEEEditR, postEEEditR :: TermId -> SchoolId -> CourseName -> ExamName -> Handler Html
getEEEditR = postEEEditR
postEEEditR tid ssh coursen examn = do
  (Entity eeId ExternalExam{..}, schools, staff) <- runDB $ do
    eExam@(Entity eeId _) <- getBy404 $ UniqueExternalExam tid ssh coursen examn
    schools <- setOf (folded . _entityVal . _externalExamOfficeSchoolSchool) <$> selectList [ ExternalExamOfficeSchoolExam ==. eeId ] []
    actualStaff <- selectList [ ExternalExamStaffExam ==. eeId ] []
    invitedStaff <- sourceInvitationsF @ExternalExamStaff eeId
    let staff = setOf (folded . _entityVal . _externalExamStaffUser . re _Right) actualStaff
             <> Set.mapMonotonic Left (Map.keysSet invitedStaff)
    return (eExam, schools, staff)

  let
    template = ExternalExamForm
      { eefTerm          = tid
      , eefSchool        = ssh
      , eefCourseName    = coursen
      , eefExamName      = examn
      , eefDefaultTime   = externalExamDefaultTime
      , eefGradingMode   = externalExamGradingMode
      , eefOfficeSchools = schools
      , eefStaff         = staff
      }

  ((examResult, examWidget'), examEnctype) <- runFormPost . externalExamForm $ Just template

  formResult examResult $ \ExternalExamForm{..} -> do
    replaceRes <- runDBJobs $ do
      replaceRes <- replaceUnique eeId ExternalExam
        { externalExamTerm        = eefTerm
        , externalExamSchool      = eefSchool
        , externalExamCourseName  = eefCourseName
        , externalExamExamName    = eefExamName
        , externalExamDefaultTime = eefDefaultTime
        , externalExamGradingMode = eefGradingMode
        }
      when (is _Nothing replaceRes) $ do
        audit $ TransactionExternalExamEdit eeId

        memcachedByInvalidate AuthCacheExternalExamStaffList $ Proxy @(Set UserId)
        forM_ (eefStaff `setSymmDiff` staff) $ \change -> if
          | change `Set.member` eefStaff -> case change of
              Left invEmail -> do
                audit $ TransactionExternalExamStaffInviteEdit eeId invEmail
                sinkInvitationsF externalExamStaffInvitationConfig
                  [(invEmail, eeId, (InvDBDataExternalExamStaff, InvTokenDataExternalExamStaff))]
              Right staffUid -> do
                audit $ TransactionExternalExamStaffEdit eeId staffUid
                insert_ $ ExternalExamStaff staffUid eeId
          | otherwise -> case change of
              Left invEmail -> do
                audit $ TransactionExternalExamStaffInviteDelete eeId invEmail
                deleteInvitation @ExternalExamStaff eeId invEmail
              Right staffUid -> do
                audit $ TransactionExternalExamStaffDelete eeId staffUid
                deleteBy $ UniqueExternalExamStaff eeId staffUid

        forM_ (eefOfficeSchools `setSymmDiff` schools) $ \change -> if
          | change `Set.member` eefOfficeSchools -> do
              audit $ TransactionExternalExamOfficeSchoolEdit eeId change
              insert_ $ ExternalExamOfficeSchool change eeId
          | otherwise -> do
              audit $ TransactionExternalExamOfficeSchoolDelete eeId change
              deleteBy $ UniqueExternalExamOfficeSchool eeId change
      return replaceRes

    case replaceRes of
      Nothing -> do
        addMessageI Success $ MsgExternalExamEdited eefCourseName eefExamName
        redirect $ EExamR eefTerm eefSchool eefCourseName eefExamName EEShowR
      Just _ ->
        addMessageI Error $ MsgExternalExamExists eefCourseName eefExamName

  let heading = MsgExternalExamEdit coursen examn

  siteLayoutMsg heading $ do
    setTitleI heading
    wrapForm examWidget' def
      { formAction = Just . SomeRoute $ EExamR tid ssh coursen examn EEEditR
      , formEncoding = examEnctype
      }
