{-|
Module      : Gargantext.Core.Config.Mail
Description :
Copyright   : (c) CNRS, 2017-Present
License     : AGPL + CECILL v3
Maintainer  : team@gargantext.org
Stability   : experimental
Portability : POSIX

-}

{-# LANGUAGE TemplateHaskell #-}

module Gargantext.Core.Config.Mail (
  -- * Types
    GargMail(..)
  , LoginType(..)
  , SendEmailType(..)
  , MailConfig(..)
  , MailException(..)

  -- * Utility functions
  , gargMail

  -- * Lenses
  , mc_mail_from
  , mc_mail_host
  , mc_mail_login_type
  , mc_mail_password
  , mc_mail_port
  , mc_mail_user
  , mc_send_login_emails
  )
  where

import Control.Exception.Safe qualified as Exc
import Control.Monad.Fail (fail)
import Data.Maybe
import Data.Text (unpack)
import Data.Text qualified as T
import Gargantext.Prelude
import Network.Mail.Mime (plainPart)
import Network.Mail.SMTP hiding (htmlPart, STARTTLS)
import Network.Socket (PortNumber)
import Toml
import Toml.Schema


type Email = Text
type Name  = Text

data LoginType = NoAuth | Normal | SSL | TLS | STARTTLS
  deriving (Generic, Eq, Show, Read)
instance FromValue LoginType where
  fromValue (Toml.Text' _ t) =
    case t of
      "NoAuth" -> return NoAuth
      "Normal" -> return Normal
      "SSL" -> return SSL
      "TLS" -> return TLS
      "STARTTLS" -> return STARTTLS
      _ -> fail ("Cannot parse login type from " <> T.unpack t)
  fromValue _ = fail ("Expected text for login type")
instance ToValue LoginType where
  toValue v = toValue (show v :: Text)


data SendEmailType = SendEmailViaAws
                   | LogEmailToConsole
                   | WriteEmailToFile
    deriving (Show, Read, Enum, Bounded, Generic)


data MailConfig = MailConfig { _mc_mail_host         :: !T.Text
                             , _mc_mail_port         :: !PortNumber
                             , _mc_mail_user         :: !T.Text
                             , _mc_mail_password     :: !T.Text
                             , _mc_mail_login_type   :: !LoginType
                             , _mc_mail_from         :: !T.Text
                             , _mc_send_login_emails :: !SendEmailType
                             }
  deriving (Generic, Show)
instance FromValue MailConfig where
  fromValue = parseTableFromValue $ do
    _mc_mail_host <- reqKey "host"
    port <- reqKey "port" :: ParseTable l Int
    _mc_mail_user <- reqKey "user"
    _mc_mail_password <- reqKey "password"
    _mc_mail_login_type <- reqKey "login_type"
    _mc_mail_from <- reqKey "from"
    let _mc_send_login_emails = LogEmailToConsole
    return $ MailConfig { _mc_mail_port = fromIntegral port, .. }
instance ToValue MailConfig where
  toValue = defaultTableToValue
instance ToTable MailConfig where
  toTable (MailConfig { .. }) = table [ "port" .= (fromIntegral _mc_mail_port :: Int)
                                      , "host" .= _mc_mail_host
                                      , "user" .= _mc_mail_user
                                      , "password" .= _mc_mail_password
                                      , "from" .= _mc_mail_from
                                      , "login_type" .= _mc_mail_login_type ]

-- readConfig :: SettingsFile -> IO MailConfig
-- readConfig (SettingsFile fp) = do
--   eRes <- Toml.decodeFileEither mailCodec fp

--   case eRes of
--     Left err -> panicTrace ("Error reading TOML file (mail): " <> show err :: Text)
--     Right config -> return config


-- mailCodec :: Toml.TomlCodec MailConfig
-- mailCodec = MailConfig
--   <$> Toml.text "mail.host" .= _mc_mail_host
--   <*> Toml.read "mail.port" .= _mc_mail_port
--   <*> Toml.text "mail.user" .= _mc_mail_user
--   <*> Toml.text "mail.password" .= _mc_mail_password
--   <*> Toml.read "mail.login_type" .= _mc_mail_login_type
--   <*> Toml.text "mail.from" .= _mc_mail_from

  -- pure $ MailConfig { _mc_mail_host       =        cs $ val' "MAIL_HOST"
  --                   , _mc_mail_port       = read $ cs $ val' "MAIL_PORT"
  --                   , _mc_mail_user       =        cs $ val' "MAIL_USER"
  --                   , _mc_mail_from       =        cs $ val' "MAIL_FROM"
  --                   , _mc_mail_password   =        cs $ val' "MAIL_PASSWORD"
  --                   , _mc_mail_login_type = read $ cs $ val' "MAIL_LOGIN_TYPE"
  --                   }


newtype MailException =
  MailException SomeException
  deriving (Show)
instance Exception MailException


data GargMail = GargMail { gm_to      :: Email
                         , gm_name    :: Maybe Name
                         , gm_subject :: Text
                         , gm_body    :: Text
                         }

gargMail :: MailConfig -> GargMail -> IO ()
gargMail mc gm = do
  Exc.catch (gargMail' mc gm) $ \e -> Exc.throw (MailException e)

gargMail' :: MailConfig -> GargMail -> IO ()
gargMail' (MailConfig {..}) (GargMail { .. }) = do
  let host     = unpack _mc_mail_host
      user     = unpack _mc_mail_user
      password = unpack _mc_mail_password
  case _mc_mail_login_type of
    NoAuth   -> sendMail                   host mail
    Normal   -> sendMailWithLogin'         host _mc_mail_port user password mail
    SSL      -> sendMailWithLoginTLS'      host _mc_mail_port user password mail
    TLS      -> sendMailWithLoginTLS'      host _mc_mail_port user password mail
    STARTTLS -> sendMailWithLoginSTARTTLS' host _mc_mail_port user password mail
  where
    mail = simpleMail sender receiver cc bcc gm_subject [plainPart $ cs gm_body]

    sender   = Address (Just "GarganText Email") _mc_mail_from
    receiver = [Address gm_name gm_to]
    cc       = []
    bcc      = []

makeLenses ''MailConfig