module Handler.CryptoIDDispatch
  ( getCryptoUUIDDispatchR
  , getCryptoFileNameDispatchR
  ) where

import Import

import Data.Proxy

import qualified Data.Text as Text

import Yesod.Core.Types (HandlerContents(..), ErrorResponse(..))

import qualified Control.Monad.Catch as E (Handler(..))

import           Data.CaseInsensitive   (CI)
import qualified Data.CaseInsensitive as CI


class CryptoRoute ciphertext plaintext where
  cryptoIDRoute :: p plaintext -> ciphertext -> Handler (Route UniWorX)

instance CryptoRoute UUID SubmissionId where
  cryptoIDRoute _ (CryptoID -> cID) = do
    (smid :: SubmissionId) <- decrypt cID
    cID' <- encrypt smid
    (tid,ssh,csh,shn) <- runDB $ do
      shid <- submissionSheet <$> get404 smid
      Sheet{..}  <- get404 shid
      Course{..} <- get404 sheetCourse
      return (courseTerm, courseSchool, courseShorthand, sheetName)
    return $ CSubmissionR tid ssh csh shn cID' SubShowR

instance CryptoRoute (CI FilePath) SubmissionId where
  cryptoIDRoute _ ciphertext
    | Just cID <- fromPathPiece . Text.pack $ CI.original ciphertext = do
        smid <- decrypt cID
        (tid,ssh,csh,shn) <- runDB $ do
          shid <- submissionSheet <$> get404 smid
          Sheet{..}  <- get404 shid
          Course{..} <- get404 sheetCourse
          return (courseTerm, courseSchool, courseShorthand, sheetName)
        return $ CSubmissionR tid ssh csh shn cID SubShowR
    | otherwise = notFound

instance CryptoRoute UUID UserId where
  cryptoIDRoute _ (CryptoID -> cID) = do
    (_ :: UserId) <- decrypt cID
    return $ AdminUserR cID

class Dispatch ciphertext (x :: [*]) where
  dispatchID :: p x -> ciphertext -> Handler (Maybe (Route UniWorX))

instance Dispatch ciphertext '[] where
  dispatchID _ _ = return Nothing

instance (CryptoRoute ciphertext plaintext, Dispatch ciphertext ps) => Dispatch ciphertext (plaintext ': ps) where
  dispatchID _ ciphertext = (<|>) <$> dispatchHead <*> dispatchTail
    where
      dispatchHead = (Just <$> cryptoIDRoute (Proxy :: Proxy plaintext) ciphertext) `catches` [ E.Handler handleHCError, E.Handler handleCryptoID ]
        where
          handleHCError :: HandlerContents -> Handler (Maybe a)
          handleHCError (HCError NotFound) = return Nothing
          handleHCError e = throwM e
          handleCryptoID :: CryptoIDError -> Handler (Maybe a)
          handleCryptoID _ = return Nothing
      dispatchTail = dispatchID (Proxy :: Proxy ps) ciphertext
  

getCryptoUUIDDispatchR :: UUID -> Handler ()
getCryptoUUIDDispatchR uuid = dispatchID p uuid >>= maybe notFound (redirectWith found302)
  where
    p :: Proxy '[ SubmissionId
                , UserId
                ]
    p = Proxy

getCryptoFileNameDispatchR :: CI FilePath -> Handler ()
getCryptoFileNameDispatchR path = dispatchID p path >>= maybe notFound (redirectWith found302)
  where
    p :: Proxy '[ SubmissionId ] 
    p = Proxy
