module Handler.Utils.ExamOffice.Exam
  ( resultIsSynced
  , examOfficeExamResultAuth
  ) where

import Import.NoFoundation

import Handler.Utils.StudyFeatures

import qualified Database.Esqueleto.Legacy as E
import qualified Database.Esqueleto.Utils as E

resultIsSynced :: E.SqlExpr (E.Value UserId) -- ^ office
               -> E.SqlExpr (Entity ExamResult)
               -> E.SqlExpr (E.Value Bool)
resultIsSynced authId examResult = (hasSchool E.&&. allSchools) E.||. (E.not_ hasSchool E.&&. anySync)
  where
    anySync = E.exists . E.from $ \synced ->
      E.where_ $ synced E.^. ExamOfficeResultSyncedResult E.==. examResult E.^. ExamResultId
           E.&&. synced E.^. ExamOfficeResultSyncedTime   E.>=. examResult E.^. ExamResultLastChanged

    hasSchool = E.exists . E.from $ \userFunction ->
      E.where_ $ userFunction E.^. UserFunctionUser E.==. authId
           E.&&. userFunction E.^. UserFunctionFunction E.==. E.val SchoolExamOffice
    allSchools = E.not_ . E.exists . E.from $ \userFunction -> do
      E.where_ $ userFunction E.^. UserFunctionUser E.==. authId
           E.&&. userFunction E.^. UserFunctionFunction E.==. E.val SchoolExamOffice
      E.where_ . E.not_ . E.exists . E.from $ \synced ->
        E.where_ $ synced E.^. ExamOfficeResultSyncedSchool E.==. E.just (userFunction E.^. UserFunctionSchool)
             E.&&. synced E.^. ExamOfficeResultSyncedResult E.==. examResult E.^. ExamResultId
             E.&&. synced E.^. ExamOfficeResultSyncedTime   E.>=. examResult E.^. ExamResultLastChanged


examOfficeExamResultAuth :: E.SqlExpr (E.Value UserId) -- ^ office
                         -> E.SqlExpr (Entity ExamResult)
                         -> E.SqlExpr (E.Value Bool)
examOfficeExamResultAuth authId examResult = ((isOffice E.||. isSystemOffice) E.&&. authByUser) E.||. authByField E.||. authBySchool E.||. authByExtraSchool
  where
    cId = E.subSelectForeign examResult ExamResultExam (\exam -> E.subSelectForeign exam ExamCourse (E.^. CourseId))
    
    authByField = E.exists . E.from $ \(examOfficeField `E.InnerJoin` studyFeatures) -> do
      E.on $ studyFeatures E.^. StudyFeaturesField E.==. examOfficeField E.^. ExamOfficeFieldField
      E.where_ . E.maybe E.false id . E.subSelectMaybe . E.from $ \course -> do
        E.where_ $ course E.^. CourseId E.==. cId
        return . E.just $ isCourseStudyFeature course studyFeatures
      E.where_ $ studyFeatures E.^. StudyFeaturesUser E.==. examResult E.^. ExamResultUser
           E.&&. examOfficeField E.^. ExamOfficeFieldOffice E.==. authId
           E.&&. examOfficeField E.^. ExamOfficeFieldField E.==. studyFeatures E.^. StudyFeaturesField
      E.where_ $ examOfficeField E.^. ExamOfficeFieldForced
           E.||. E.exists (E.from $ \userFunction ->
                             E.where_ $ userFunction E.^. UserFunctionUser E.==. authId
                                  E.&&. userFunction E.^. UserFunctionFunction E.==. E.val SchoolExamOffice
                                  E.&&. E.not_ (E.exists . E.from $ \courseUserExamOfficeOptOut -> do
                                                  E.where_ $ courseUserExamOfficeOptOut E.^. CourseUserExamOfficeOptOutCourse E.==. cId
                                                       E.&&. courseUserExamOfficeOptOut E.^. CourseUserExamOfficeOptOutUser E.==. examResult E.^. ExamResultUser
                                                       E.&&. courseUserExamOfficeOptOut E.^. CourseUserExamOfficeOptOutSchool E.==. userFunction E.^. UserFunctionSchool
                                               )
                          )

    authByUser = E.exists . E.from $ \examOfficeUser ->
      E.where_ $ examOfficeUser E.^. ExamOfficeUserOffice E.==. authId
           E.&&. examOfficeUser E.^. ExamOfficeUserUser E.==. examResult E.^. ExamResultUser

    isOffice = E.exists . E.from $ \userFunction ->
      E.where_ $ userFunction E.^. UserFunctionUser E.==. authId
           E.&&. userFunction E.^. UserFunctionFunction E.==. E.val SchoolExamOffice
    isSystemOffice = E.exists . E.from $ \userSystemFunction -> 
      E.where_ $ userSystemFunction E.^. UserSystemFunctionUser E.==. authId
           E.&&. userSystemFunction E.^. UserSystemFunctionFunction E.==. E.val SystemExamOffice
           E.&&. E.not_ (userSystemFunction E.^. UserSystemFunctionIsOptOut)

    authBySchool = E.exists . E.from $ \(userFunction `E.InnerJoin` course `E.InnerJoin` exam) -> do
      E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse
       E.&&. exam E.^. ExamId E.==. examResult E.^. ExamResultExam
      E.on $ course E.^. CourseSchool E.==. userFunction E.^. UserFunctionSchool
       E.&&. userFunction E.^. UserFunctionFunction E.==. E.val SchoolExamOffice
      E.where_ $ userFunction E.^. UserFunctionUser E.==. authId

    authByExtraSchool = E.exists . E.from $ \(userFunction `E.InnerJoin` examSchool) -> do
      E.on $ userFunction E.^. UserFunctionFunction E.==. E.val SchoolExamOffice
       E.&&. userFunction E.^. UserFunctionSchool E.==. examSchool E.^. ExamOfficeSchoolSchool
      E.where_ $ examSchool E.^. ExamOfficeSchoolExam E.==. examResult E.^. ExamResultExam
      E.where_ $ userFunction E.^. UserFunctionUser E.==. authId
