module Handler.Exam.Edit
  ( getEEditR, postEEditR
  ) where

import Import
import Handler.Exam.Form
import Handler.Exam.CorrectorInvite

import Utils.Lens

import qualified Data.Set as Set

import Handler.Utils
import Handler.Utils.Exam
import Handler.Utils.Invitations

import Jobs.Queue


getEEditR, postEEditR :: TermId -> SchoolId -> CourseShorthand -> ExamName -> Handler Html
getEEditR = postEEditR
postEEditR tid ssh csh examn = do
  (cid, eId, template) <- runDB $ do
    (cid, exam@(Entity eId _)) <- fetchCourseIdExam tid ssh csh examn

    template <- examFormTemplate exam

    return (cid, eId, template)

  ((editExamResult, editExamWidget), editExamEnctype) <- runFormPost . validateForm validateExam . examForm $ Just template

  formResult editExamResult $ \ExamForm{..} -> do
    insertRes <- runDBJobs $ do
      insertRes <- myReplaceUnique eId Exam
        { examCourse           = cid
        , examName             = efName
        , examGradingRule      = efGradingRule
        , examBonusRule        = efBonusRule
        , examOccurrenceRule   = efOccurrenceRule
        , examVisibleFrom      = efVisibleFrom
        , examRegisterFrom     = efRegisterFrom
        , examRegisterTo       = efRegisterTo
        , examDeregisterUntil  = efDeregisterUntil
        , examPublishOccurrenceAssignments = efPublishOccurrenceAssignments
        , examStart            = efStart
        , examEnd              = efEnd
        , examFinished         = efFinished
        , examClosed           = efClosed
        , examPublicStatistics = efPublicStatistics
        , examShowGrades       = efShowGrades
        , examDescription      = efDescription
        }

      when (is _Nothing insertRes) $ do
        occIds <- fmap catMaybes . forM (Set.toList efOccurrences) $ traverse decrypt . eofId
        deleteWhere [ ExamOccurrenceExam ==. eId, ExamOccurrenceId /<-. occIds ]
        forM_ (Set.toList efOccurrences) $ \case
          ExamOccurrenceForm{ eofId = Nothing, .. } -> insert_
            ExamOccurrence
              { examOccurrenceExam = eId
              , examOccurrenceName = eofName
              , examOccurrenceRoom = eofRoom
              , examOccurrenceCapacity = eofCapacity
              , examOccurrenceStart = eofStart
              , examOccurrenceEnd = eofEnd
              , examOccurrenceDescription = eofDescription
              }
          ExamOccurrenceForm{ .. } -> void . runMaybeT $ do
            cID <- hoistMaybe eofId
            eofId' <- decrypt cID
            oldOcc <- MaybeT $ get eofId'
            guard $ examOccurrenceExam oldOcc == eId
            lift $ replace eofId' ExamOccurrence
              { examOccurrenceExam = eId
              , examOccurrenceName = eofName
              , examOccurrenceRoom = eofRoom
              , examOccurrenceCapacity = eofCapacity
              , examOccurrenceStart = eofStart
              , examOccurrenceEnd = eofEnd
              , examOccurrenceDescription = eofDescription
              }


        pIds <- fmap catMaybes . forM (Set.toList efExamParts) $ traverse decrypt . epfId
        deleteWhere [ ExamPartExam ==. eId, ExamPartId /<-. pIds ]
        forM_ (Set.toList efExamParts) $ \case
          ExamPartForm{ epfId = Nothing, .. } -> insert_
            ExamPart
              { examPartExam = eId
              , examPartName = epfName
              , examPartMaxPoints = epfMaxPoints
              , examPartWeight = epfWeight
              }
          ExamPartForm{ .. } -> void . runMaybeT $ do
            cID <- hoistMaybe epfId
            epfId' <- decrypt cID
            oldPart <- MaybeT $ get epfId'
            guard $ examPartExam oldPart == eId
            lift $ replace epfId' ExamPart
              { examPartExam = eId
              , examPartName = epfName
              , examPartMaxPoints = epfMaxPoints
              , examPartWeight = epfWeight
              }


        let (invites, adds) = partitionEithers $ Set.toList efCorrectors

        deleteWhere [ ExamCorrectorExam ==. eId ]
        insertMany_ $ map (ExamCorrector eId) adds

        deleteWhere [ InvitationFor ==. invRef @ExamCorrector eId, InvitationEmail /<-. invites ]
        sinkInvitationsF examCorrectorInvitationConfig $ map (, eId, (InvDBDataExamCorrector, InvTokenDataExamCorrector)) invites

      return insertRes

    case insertRes of
      Just _  -> addMessageI Error $ MsgExamNameTaken efName
      Nothing -> do
        addMessageI Success $ MsgExamEdited efName
        redirect $ CExamR tid ssh csh efName EShowR

  let heading = prependCourseTitle tid ssh csh . MsgExamEditHeading $ efName template

  siteLayoutMsg heading $ do
    setTitleI heading
    let
      editExamForm = wrapForm editExamWidget def
        { formMethod = POST
        , formAction = Just . SomeRoute $ CExamR tid ssh csh examn EEditR
        , formEncoding = editExamEnctype
        }
    $(widgetFile "exam-edit")
