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

-}

module Gargantext.Core.Mail where

import Control.Lens (view)
import Control.Monad.Trans.Control (MonadBaseControl)
import Data.List qualified as List
import Data.Text (splitOn)
import Gargantext.Core.Config (gc_frontend_config, HasConfig(..))
import Gargantext.Core.Config.Types (fc_backend_name, getPublicUrl)
import Gargantext.Core.Config.Mail (gargMail, GargMail(..), MailConfig)
import Gargantext.Core.Types.Individu
import Gargantext.Database.Schema.User (UserLight(..))
import Gargantext.Prelude
import Network.URI.Encode (encodeText)


-- | Tool to put elsewhere
isEmail :: Text -> Bool
isEmail = ((==) 2) . List.length . (splitOn "@")

------------------------------------------------------------------------
newtype SendEmail = SendEmail Bool

type EmailAddress  = Text
type Name          = Text
data ServerAddress = ServerAddress { sa_name :: Text
                                   , sa_url  :: Text
                                   }

data MailModel = Invitation { invitation_user :: NewUser GargPassword }
               | PassUpdate { passUpdate_user :: NewUser GargPassword }
               | MailInfo   { mailInfo_username :: Name
                            , mailInfo_address  :: EmailAddress
                            }
               | ForgotPassword { user :: UserLight }
------------------------------------------------------------------------

-- | Execute the given input action 'act', sending an email notification
-- only if 'SendEmail' says so.
withNotification :: (MonadBaseControl IO m, HasConfig env, MonadReader env m)
                 => SendEmail
                 -> MailConfig
                 -> (notificationBody -> MailModel)
                -- ^ A function which can build a 'MailModel' out of
                -- the returned type of the action.
                 -> m (a, notificationBody)
                -- ^ The action to run. Returns the value @a@ to return
                -- upstream alongside anything needed to build a 'MailModel'.
                 -> m a
withNotification (SendEmail doSend) cfg mkNotification act = do
  (r, notificationBody) <- act
  when doSend $ mail cfg (mkNotification notificationBody)
  pure r

------------------------------------------------------------------------
mail :: (MonadBaseControl IO m, MonadReader env m, HasConfig env)
     => MailConfig
     -- ^ The configuration for the email
     -> MailModel
     -- ^ The notification we want to emit.
     -> m ()
mail mailCfg model = do
  cfg <- view hasConfig
  let
    (m,u)   = email_to         model
    subject = email_subject    model
    body    = emailWith (ServerAddress (view (gc_frontend_config . fc_backend_name) cfg)
                                       (getPublicUrl $ view gc_frontend_config cfg)) model
  liftBase $ gargMail mailCfg (GargMail { gm_to = m
                                        , gm_name = Just u
                                        , gm_subject = subject
                                        , gm_body = body })

------------------------------------------------------------------------
emailWith :: ServerAddress -> MailModel -> Text
emailWith server model =
  unlines $ [ "Hello" ]
          <> bodyWith server model
          <> email_disclaimer
          <> email_signature

------------------------------------------------------------------------
email_to :: MailModel -> (EmailAddress, Name)
email_to (Invitation user) = email_to' user
email_to (PassUpdate user) = email_to' user
email_to (MailInfo { .. })    = (mailInfo_address, mailInfo_username)
email_to (ForgotPassword { user = UserLight { .. }}) = (userLight_email, userLight_username)

email_to' :: NewUser GargPassword -> (EmailAddress, Name)
email_to' (NewUser u m _) = (m,u)

------------------------------------------------------------------------
bodyWith :: ServerAddress -> MailModel -> [Text]
bodyWith server@(ServerAddress name _url) (Invitation u) = [ "Congratulation, you have been granted a user account to test the"
                                 , "new GarganText platform called " <> name <> " !"
                                 ] <> (email_credentials server u)

bodyWith server (PassUpdate u) = [ "Your account password have been updated on the GarganText platform!"
                                 ] <> (email_credentials server u)

bodyWith (ServerAddress _ url) (MailInfo _ _) = [ "Your last analysis is over on the server: " <> url]
bodyWith _server (ForgotPassword { user = UserLight { userLight_forgot_password_uuid = Nothing }}) =
  [ "Cannot send you link to forgot password, no UUID" ]
bodyWith server (ForgotPassword { user = UserLight { userLight_forgot_password_uuid = Just uuid }}) =
  [ "Click on this link to restore your password: "
  , forgot_password_link server uuid ]

forgot_password_link :: ServerAddress -> Text -> Text
forgot_password_link (ServerAddress _ server) uuid = server <> "/#/forgotPassword?uuid=" <> uuid <> "&server=" <> encodeText server

------------------------------------------------------------------------
email_subject :: MailModel -> Text
email_subject (Invitation _)     = "[GarganText] Invitation"
email_subject (PassUpdate _)     = "[GarganText] Update"
email_subject (MailInfo _ _)     = "[GarganText] Info"
email_subject (ForgotPassword _) = "[GarganText] Forgot Password"


email_credentials :: ServerAddress -> NewUser GargPassword -> [Text]
email_credentials (ServerAddress _ server) (NewUser u _ (GargPassword p)) =
          [ ""
          , "You can log in to: " <> server
          , "Your username is: "  <> u
          , "Your password is: "  <> p
          , ""
          ]

email_disclaimer :: [Text]
email_disclaimer =
            [ ""
            , "/!\\ Please note that your account is opened for beta tester only. Hence"
            , "we cannot guarantee neither the perenniality nor the stability of the"
            , "service at this stage. It is therefore advisable to back up important"
            , "data regularly."
            , ""
            , "/!\\ Gargantext is an academic service supported by CNRS/ISC-PIF partners."
            , "In case of congestion on this service, access to members of the ISC-PIF"
            , "partners will be privileged."
            , ""
            , "If you log in you agree with the following terms of use:"
            , "     https://gitlab.iscpif.fr/humanities/tofu/tree/master"
            , ""
            , "Your feedback will be valuable for further development of the platform,"
            , "do not hesitate to contact us and to contribute on our forum:"
            , "     https://discourse.iscpif.fr/c/gargantext"
            , ""
            ]

email_signature :: [Text]
email_signature =
          [ "With our best regards,"
          , "-- "
          , "The Gargantext Team (CNRS)"
          ]
