module Handler.Utils.Files
  ( sourceFile, sourceFile'
  , sourceFiles, sourceFiles'
  , SourceFilesException(..)
  ) where

import Import

import qualified Data.Conduit.Combinators as C

import Handler.Utils.Minio
import qualified Network.Minio as Minio

import qualified Data.ByteString.Base64.URL as Base64
import qualified Data.ByteArray as ByteArray


data SourceFilesException
  = SourceFilesMismatchedHashes
  | SourceFilesContentUnavailable
  deriving (Eq, Ord, Read, Show, Generic, Typeable)
  deriving anyclass (Exception)


sourceFiles :: ConduitT FileReference File (YesodDB UniWorX) ()
sourceFiles = C.mapM sourceFile

sourceFile :: FileReference -> DB File
sourceFile FileReference{..} = do
  mFileContent <- traverse get $ FileContentKey <$> fileReferenceContent
  fileContent <- if
    | is (_Just . _Nothing) mFileContent
    , Just fileContentHash <- fileReferenceContent -- Not a restriction
      -> maybeT (throwM SourceFilesContentUnavailable) $ do
        let uploadName = decodeUtf8 . Base64.encodeUnpadded $ ByteArray.convert fileContentHash
        uploadBucket <- getsYesod $ views appSettings appUploadCacheBucket
        fmap Just . hoistMaybe <=< runAppMinio . runMaybeT $ do
          objRes <- catchIfMaybeT minioIsDoesNotExist $ Minio.getObject uploadBucket uploadName Minio.defaultGetObjectOptions
          lift . runConduit $ Minio.gorObjectStream objRes .| C.fold
    | fmap (fmap fileContentHash) mFileContent /= fmap Just fileReferenceContent
      -> throwM SourceFilesMismatchedHashes
    | Just fileContent' <- fileContentContent <$> join mFileContent
      -> return $ Just fileContent'
    | otherwise
      -> return Nothing

  return File
    { fileTitle = fileReferenceTitle
    , fileContent
    , fileModified = fileReferenceModified
    }

sourceFiles' :: forall file. HasFileReference file => ConduitT file File (YesodDB UniWorX) ()
sourceFiles' = C.mapM sourceFile'

sourceFile' :: forall file. HasFileReference file => file -> DB File
sourceFile' = sourceFile . view (_FileReference . _1)
