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

module Utils.Csv.Mail
  ( recodeCsv
  ) where

import Import.NoModel
import Model.Types.Csv

import qualified Data.Csv as Csv

import Data.Time.Clock.POSIX (getPOSIXTime)

import qualified Data.Conduit.Combinators as C

import Data.Encoding (encodeLazyByteStringExplicit, decodeLazyByteStringExplicit)


instance (RenderMessage site msg, YesodMail site) => ToMailPart site (msg, CsvRendered) where
  toMailPart (sheetName, csvRendered@CsvRendered{..}) = do
    encOpts <- lift askMailCsvOptions

    case encOpts ^. _csvFormat of
      CsvFormatOptions{}
        | Just csvOpts <- encOpts ^? _csvFormat . _CsvEncodeOptions -> do
            _partType .= decodeUtf8 typeCsv'
            _partEncoding .= QuotedPrintableText
            _partContent <~ fmap PartContent (liftHandler . runConduit $ C.sourceLazy (Csv.encodeByNameWith csvOpts csvRenderedHeader csvRenderedData) .| recodeCsv encOpts True C.sinkLazy)
        | otherwise -> error "encOpts is CsvFormatOptions but cannot be converted via _CsvEncodeOptions"
      CsvXlsxFormatOptions{} -> do
        pNow <- liftIO getPOSIXTime
        sheetName' <- lift $ ($ sheetName) <$> getMailMessageRender
        _partType .= decodeUtf8 typeXlsx
        _partEncoding .= Base64
        _partContent .= PartContent (fromXlsx pNow $ csvRenderedToXlsx sheetName' csvRendered)

recodeCsv :: MonadThrow m
          => CsvOptions
          -> Bool -- ^ recode from (internal) utf8 to user chosen coding?
          -> ConduitT ByteString o m a -> ConduitT ByteString o m a
recodeCsv encOpts toUser act = fromMaybe act $ do
  enc <- encOpts ^? _csvFormat . _csvEncoding

  let
    recode
      | toUser    = either throwM return . encodeLazyByteStringExplicit enc <=< either throwM return . decodeLazyByteStringExplicit UTF8
      | otherwise = either throwM return . encodeLazyByteStringExplicit UTF8 <=< either throwM return . decodeLazyByteStringExplicit enc

  return $ if
    | enc == "UTF8" -> act
    | FormatCsv <- fmt -> do
        inp <- C.sinkLazy
        inp' <- recode inp
        sourceLazy inp' .| act
    -- -- | FormatXlsx <- fmt -> do
    -- --     inp <- C.sinkLazy
    -- --     archive <- throwLeft $ Zip.toArchiveOrFail inp
    -- --     archive' <- traverseOf (_zEntries . traverse . _Entrty . _3) recode archive
    -- --     sourceLazy (Zip.fromArchive inp') .| act
    | otherwise -> act
  where
    
    fmt = encOpts ^. _csvFormat . _CsvFormat

    -- _zEntries :: Lens' Zip.Archive [Zip.Entry]
    -- _zEntries = lens (\Zip.Archive{..} -> zEntries) (\archive entries -> archive { zEntries = entries })

    -- _Entry :: Lens' Zip.Entry (FilePath, Integer, Lazy.ByteString)
    -- _Entry = lens (\entry@Zip.Entry{..} -> (eRelativePath, eLastModified, Zip.fromEntry entry)) (uncurry3 Zip.toEntry)
