{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}

module Handler.Submission.Helper.ArchiveTable
  ( mkSubmissionArchiveTable
  ) where

import Import
import Handler.Utils
import qualified Database.Esqueleto.Legacy as E
import qualified Database.Esqueleto.Utils as E
import qualified Database.Esqueleto.Internal.Internal as E (unsafeSqlFunction)


type SubmissionArchiveExpr = E.SqlExpr (Maybe (Entity SubmissionFile)) `E.FullOuterJoin` E.SqlExpr (Maybe (Entity SubmissionFile))

queryOriginal, queryCorrected :: Getter SubmissionArchiveExpr (E.SqlExpr (Maybe (Entity SubmissionFile)))
queryOriginal = to $(E.sqlFOJproj 2 1)
queryCorrected = to $(E.sqlFOJproj 2 2)


type SubmissionArchiveData = DBRow ( Maybe (Entity SubmissionFile)
                                   , Maybe (Entity SubmissionFile)
                                   )

resultOriginal, resultCorrected :: Traversal' SubmissionArchiveData (Entity SubmissionFile)
resultOriginal = _dbrOutput . _1 . _Just
resultCorrected = _dbrOutput . _2 . _Just
  

mkSubmissionArchiveTable :: TermId -> SchoolId -> CourseShorthand -> SheetName
                         -> Bool -- ^ @showCorrection@
                         -> SubmissionId
                         -> DB (Bool, Widget)
mkSubmissionArchiveTable tid ssh csh shn showCorrection smid = do
  cID <- encrypt smid :: DB CryptoFileNameSubmission -- shouldn't be expensive due to caching

  let
    dbtIdent :: Text
    dbtIdent = "files"
    
    dbtSQLQuery = runReaderT $ do
      original <- view queryOriginal
      corrected <- view queryCorrected

      lift . E.on $
              original E.?. SubmissionFileTitle E.==. corrected E.?. SubmissionFileTitle
        E.&&. original E.?. SubmissionFileSubmission E.==. corrected E.?. SubmissionFileSubmission
        E.&&. original E.?. SubmissionFileId E.!=. corrected E.?. SubmissionFileId
        E.&&. corrected E.?. SubmissionFileIsDeletion E.==. E.val (Just False)
        E.&&. E.val showCorrection -- Do not correlate files if we don't show correction; together with `may-access` this treats corrected files like they literally don't exist

      lift . E.where_ $ original E.?. SubmissionFileSubmission E.==. E.val (Just smid)
                  E.||. corrected E.?. SubmissionFileSubmission E.==. E.val (Just smid)

      lift . E.where_ . E.maybe E.true E.not_ $ original  E.?. SubmissionFileIsUpdate -- @original@ is unset or not an update
      lift . E.where_ . E.maybe E.true id     $ corrected E.?. SubmissionFileIsUpdate -- @corrected@ is unset or an update
      lift . E.where_ . E.maybe E.true E.not_ $ corrected E.?. SubmissionFileIsDeletion -- @corrected@ is unset or not a deletion
      return (original, corrected)
    dbtRowKey = (,) <$> views queryOriginal (E.?. SubmissionFileId) <*> views queryCorrected (E.?. SubmissionFileId)

    dbtProj = dbtProjId

    dbtColonnade = mconcat $ catMaybes
        [ Just . sortable (Just "path") (i18nCell MsgTableFileTitle) $ \t -> let
              mOrig = t ^? resultOriginal
              mCorr = t ^? resultCorrected
              fileTitle'' = submissionFileTitle . entityVal <$> (mOrig <|> mCorr)
              origIsFile = fmap (isJust . submissionFileContent . entityVal) mOrig
              corrIsFile = fmap (isJust . submissionFileContent . entityVal) mCorr
              isFile' = origIsFile <|> corrIsFile
            in maybeCell ((,) <$> fileTitle'' <*> isFile') $ \(fileTitle', isFile) -> if
              | Just True <- origIsFile -> anchorCell (subDownloadLink SubmissionOriginal fileTitle') [whamlet|#{fileTitle'}|]
              | otherwise               -> textCell $ bool (<> "/") id isFile fileTitle'
        , guardOn showCorrection . sortable (toNothing "state") (i18nCell MsgTableCorState) $ \t -> case t ^? resultCorrected of
            Nothing -> cell mempty
            Just (Entity _ SubmissionFile{..}) -> tellCell (Any True) $ if
              | isJust submissionFileContent -> anchorCell (subDownloadLink SubmissionCorrected submissionFileTitle) (i18n MsgFileCorrected :: Widget)
              | otherwise -> i18nCell MsgCorrected
        , Just . sortable (Just "time") (i18nCell MsgTableFileModified) $ \t -> let
              mOrig = t ^? resultOriginal
              mCorr = t ^? resultCorrected
              origTime = submissionFileModified . entityVal <$> mOrig
              corrTime = submissionFileModified . entityVal <$> mCorr
              fileTime = (max <$> origTime <*> corrTime) <|> origTime <|> corrTime
            in maybeCell fileTime dateTimeCell
        ]
      where 
        subDownloadLink sft fileTitle' = CSubmissionR tid ssh csh shn cID $ SubDownloadR sft fileTitle'

    dbtStyle = def

    dbtSorting = mconcat
        [ singletonMap "path" . SortColumn $ \r -> (E.unsafeSqlFunction "string_to_array" :: (E.SqlExpr (E.Value (Maybe String)), E.SqlExpr (E.Value String)) -> E.SqlExpr (E.Value [String])) (E.coalesce [views queryOriginal (E.?. SubmissionFileTitle) r, views queryCorrected (E.?. SubmissionFileTitle) r], E.val "/" :: E.SqlExpr (E.Value String))
        , singletonMap "time" . SortColumn $ \r -> (E.unsafeSqlFunction "GREATEST" ([views queryOriginal (E.?. SubmissionFileModified) r, views queryCorrected (E.?. SubmissionFileModified) r] :: [E.SqlExpr (E.Value (Maybe UTCTime))]) :: E.SqlExpr (E.Value (Maybe UTCTime)))
        ]
    dbtFilter = mconcat
        [ singletonMap "may-access" . FilterColumn $ \(Any b) r
            -> E.val b E.==. (E.val showCorrection E.||. E.isJust (views queryOriginal (E.?. SubmissionFileId) r))
        ]

    dbtFilterUI = mempty

    dbtParams = def

    dbtCsvEncode = noCsvEncode
    dbtCsvDecode = Nothing
    dbtExtraReps = []

    archiveTableValidator = def
      & defaultSorting [SortAscBy "path"]
      & forceFilter "may-access" (Any True)
   in over _1 getAny <$> dbTableWidget archiveTableValidator DBTable{..}
