{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Network.Mail.Mailgun.Domains where

import           Control.Lens
import           Control.Monad.Catch
import           Control.Monad.Reader.Class
import           Control.Monad.Trans
import qualified Data.Aeson as JS
import           Data.Aeson ((.:))
import           Data.Aeson.Filthy
import           Data.Aeson.Lens
import           Data.Machine
import           Data.Text (Text)
import qualified Data.Text as T
import           Data.Time
import           Network.Mail.Mailgun.API
import           Network.Mail.Mailgun.Config

data SpamAction
 = SpamDisabled
 | SpamBlock
 | SpamTag
 deriving (Show, Eq, Ord)

makePrisms ''SpamAction

instance JS.FromJSON SpamAction where
  parseJSON = JS.withText "SpamAction" $ \case
              "disabled" -> pure SpamDisabled
              "block" -> pure SpamBlock
              "tag" -> pure SpamTag
              s -> fail $ "Unknown SpamAction "++show s

instance JS.ToJSON SpamAction where
  toJSON SpamDisabled = JS.String "disabled"
  toJSON SpamBlock    = JS.String "block"
  toJSON SpamTag      = JS.String "tag"

data DomainType
 = CustomDomain
 | SandboxDomain
 deriving (Show, Eq, Ord)

makePrisms ''DomainType

instance JS.FromJSON DomainType where
  parseJSON = JS.withText "DomainType" $ \case
              "custom" -> pure CustomDomain
              "sandbox" -> pure SandboxDomain
              t -> fail $ "Unknown DomainType "++show t

instance JS.ToJSON DomainType where
  toJSON CustomDomain  = JS.String "custom"
  toJSON SandboxDomain = JS.String "sandbox"

data Domain
 = Domain
   { _domainCreated    :: UTCTime
   , _domainSmtpLogin  :: Text
   , _domainSmtpPass   :: Text
   , _domainName       :: Text
   , _domainWildcard   :: Bool
   , _domainSpamAction :: SpamAction
   , _domainActive     :: Bool
   , _domainType       :: DomainType
   }
 deriving (Show)

makeClassy ''Domain

instance JS.FromJSON Domain where
  parseJSON = JS.withObject "Domain" $ \v -> Domain
              <$> (fromRFC2822Time <$> v .: "created_at")
              <*> v .: "smtp_login"
              <*> v .: "smtp_password"
              <*> v .: "name"
              <*> v .: "wildcard"
              <*> v .: "spam_action"
              <*> ((==("active"::Text)) <$> v .: "state")
              <*> v .: "type"

instance JS.ToJSON Domain where
  toJSON d = JS.object
             [("created_at", d^.domainCreated.(to RFC2822Time).to JS.toJSON)
             ,("smtp_login", d^.domainSmtpLogin.to JS.toJSON)
             ,("smtp_password", d^.domainSmtpPass.to JS.toJSON)
             ,("name", d^.domainName.to JS.toJSON)
             ,("wildcard", d^.domainWildcard.to JS.toJSON)
             ,("spam_action", d^.domainSpamAction.to JS.toJSON)
             ,("state", JS.String $ if d^.domainActive then "active" else "unverified")
             ,("type", d^.domainType.to JS.toJSON)
             ]

getDomains :: (HasMailgunConfig c, MonadIO m, MonadThrow m, MonadReader c m) => SourceT m Domain
getDomains =
  getStream 0
    (\skip -> (skip, MGGet (const "/v3/domains") [("skip", T.pack $ show skip)
                                                ,("limit", T.pack $ show pagingSize)]))
    (\skipped respVal ->
       let mrs = respVal^?key "items"._JSON
       in fmap (\rs -> (if length rs == pagingSize then Just (skipped+length rs) else Nothing
                      ,rs)) mrs)
  where
    pagingSize = 500