{-# OPTIONS_GHC -fno-warn-orphans #-}

module Handler.Submission.SubmissionUserInvite
  ( InvitableJunction(..), InvitationDBData(..), InvitationTokenData(..)
  , submissionUserInvitationConfig
  , getSInviteR, postSInviteR
  ) where

import Import

import Handler.Utils.Invitations

import Data.Aeson hiding (Result(..))

import qualified Data.HashSet as HashSet


instance IsInvitableJunction SubmissionUser where
  type InvitationFor SubmissionUser = Submission
  data InvitableJunction SubmissionUser = JunctionSubmissionUser
    deriving (Eq, Ord, Read, Show, Generic, Typeable)
  data InvitationDBData SubmissionUser = InvDBDataSubmissionUser
    deriving (Eq, Ord, Read, Show, Generic, Typeable)
  data InvitationTokenData SubmissionUser = InvTokenDataSubmissionUser
    deriving (Eq, Ord, Read, Show, Generic, Typeable)

  _InvitableJunction = iso
    (\SubmissionUser{..} -> (submissionUserUser, submissionUserSubmission, JunctionSubmissionUser))
    (\(submissionUserUser, submissionUserSubmission, JunctionSubmissionUser) -> SubmissionUser{..})

instance ToJSON (InvitableJunction SubmissionUser) where
  toJSON = genericToJSON defaultOptions { fieldLabelModifier = camelToPathPiece' 1 }
  toEncoding = genericToEncoding defaultOptions { fieldLabelModifier = camelToPathPiece' 1 }
instance FromJSON (InvitableJunction SubmissionUser) where
  parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = camelToPathPiece' 1 }

instance ToJSON (InvitationDBData SubmissionUser) where
  toJSON = genericToJSON defaultOptions { fieldLabelModifier = camelToPathPiece' 3 }
  toEncoding = genericToEncoding defaultOptions { fieldLabelModifier = camelToPathPiece' 3 }
instance FromJSON (InvitationDBData SubmissionUser) where
  parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = camelToPathPiece' 3 }

instance ToJSON (InvitationTokenData SubmissionUser) where
  toJSON = genericToJSON defaultOptions { constructorTagModifier = camelToPathPiece' 3 }
  toEncoding = genericToEncoding defaultOptions { constructorTagModifier = camelToPathPiece' 3 }
instance FromJSON (InvitationTokenData SubmissionUser) where
  parseJSON = genericParseJSON defaultOptions { constructorTagModifier = camelToPathPiece' 3 }

submissionUserInvitationConfig :: InvitationConfig SubmissionUser
submissionUserInvitationConfig = InvitationConfig{..}
  where
    invitationRoute (Entity subId Submission{..}) _ = do
      Sheet{..} <- getJust submissionSheet
      Course{..} <- getJust sheetCourse
      cID <- encrypt subId
      return $ CSubmissionR courseTerm courseSchool courseShorthand sheetName cID SInviteR
    invitationResolveFor _ = do
      cRoute <- getCurrentRoute
      case cRoute of
        Just (CSubmissionR _tid _ssh _csh _shn cID SInviteR) -> do
          subId <- decrypt cID
          bool notFound (return subId) =<< existsKey subId
        _other ->
          error "submissionUserInvitationConfig called from unsupported route"
    invitationSubject (Entity _ Submission{..}) _ = do
      Sheet{..} <- getJust submissionSheet
      Course{..} <- getJust sheetCourse
      return . SomeMessage $ MsgMailSubjectSubmissionUserInvitation courseTerm courseSchool courseShorthand sheetName
    invitationHeading (Entity _ Submission{..}) _ = do
      Sheet{..} <- getJust submissionSheet
      return . SomeMessage $ MsgSubmissionUserInviteHeading sheetName
    invitationExplanation _ _ = return [ihamlet|_{SomeMessage MsgSubmissionUserInviteExplanation}|]
    invitationTokenConfig (Entity _ Submission{..}) _ = do
      Sheet{..} <- getJust submissionSheet
      Course{..} <- getJust sheetCourse
      itAuthority <- HashSet.singleton . Right <$> liftHandler requireAuthId
      itAddAuth <- either throwM (return . Just) $ routeAuthTags (CSheetR courseTerm courseSchool courseShorthand sheetName SubmissionNewR)
      let itExpiresAt = Nothing
          itStartsAt = Nothing
      return InvitationTokenConfig{..}
    invitationRestriction _ _ = return Authorized
    invitationForm _ _ _ = pure (JunctionSubmissionUser, ())
    invitationInsertHook _ _ _ _ _ = id
    invitationSuccessMsg (Entity _ Submission{..}) _ = do
      Sheet{..} <- getJust submissionSheet
      return . SomeMessage $ MsgSubmissionUserInvitationAccepted sheetName
    invitationUltDest (Entity subId Submission{..}) _ = do
      Sheet{..} <- getJust submissionSheet
      Course{..} <- getJust sheetCourse
      cID <- encrypt subId
      return . SomeRoute $ CSubmissionR courseTerm courseSchool courseShorthand sheetName cID SubShowR

getSInviteR, postSInviteR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> CryptoFileNameSubmission -> Handler Html
getSInviteR = postSInviteR
postSInviteR = invitationR submissionUserInvitationConfig
