{-|
Module      : Servant.API.SMSC.RU.API
Description : Contains raw servant API for smsc.ru service
Copyright   : (c) Anton Gushcha, 2016
License     : MIT
Maintainer  : ncrashed@gmail.com
Stability   : experimental
Portability : Portable
-}
module Servant.API.SMSC.RU.API(
  -- * API
    SMSCAPI
  -- ** Endpoints
  , SendResponse(..)
  , PhoneResp(..)
  , SendErrorCode(..)
  , SendEndpoint
  ) where 

import Control.Monad
import Data.Aeson
import Data.Monoid 
import Data.Scientific
import Data.Text
import GHC.Generics
import Servant.API 

import Data.ByteString.Lazy (ByteString)

-- | All supported API of smsc.ru service
type SMSCAPI = SendEndpoint

-- | Error type
data SendErrorCode = 
    SendParametersError
  | SendInvalidLoginPass
  | SendLowFunds
  | SendBlockedDueErrors
  | SendInvalidDateFormat
  | SendForbiddenMessage
  | SendInvalidPhoneFormat
  | SendMessageCannotBeDelivered
  | SendTooMuchMessages
  deriving (Generic, Show, Eq, Bounded, Enum)

-- | Convert from received error code
fromErrorCode :: Word -> Maybe SendErrorCode
fromErrorCode w = case w of 
  1 -> Just SendParametersError
  2 -> Just SendInvalidLoginPass
  3 -> Just SendLowFunds
  4 -> Just SendBlockedDueErrors
  5 -> Just SendInvalidDateFormat
  6 -> Just SendForbiddenMessage
  7 -> Just SendInvalidPhoneFormat
  8 -> Just SendMessageCannotBeDelivered
  9 -> Just SendTooMuchMessages
  _ -> Nothing

instance FromJSON SendErrorCode where 
  parseJSON (Number s) = case fromErrorCode =<< toBoundedInteger s of 
    Nothing -> fail $ "unknown error code " <> show s
    Just c -> return c 
  parseJSON _ = mzero

-- | Additional info about phone number
data PhoneResp = PhoneResp {
  phoneNumber :: !Text 
, phoneMccmnc :: !Text 
, phoneCost :: !Text 
, phoneStatus :: !Text 
, phoneError :: !Text
} deriving (Generic, Show)

instance FromJSON PhoneResp where 
  parseJSON (Object o) = PhoneResp 
    <$> o .: "phone"
    <*> o .: "mccmnc"
    <*> o .: "cost"
    <*> o .: "status"
    <*> o .: "error"
  parseJSON _ = mzero

-- | Response for 'SendEndpoint'
data SendResponse = 
    -- | Server returned error payload
    SendError {
      sendError :: !Text -- ^ Desciption of error
    , sendErrorCode :: !SendErrorCode -- ^ Error code
    , sendErrorId :: !(Maybe Text) -- ^ ID of failed message
    }
    -- | Server retunred success payload
  | SendSuccess {
      sendSuccId :: !(Maybe Word) -- ^ ID of sended message
    , sendSuccCnt :: !Word -- ^ Count of messages sended (huge payloads are splitted)
    , sendSuccCost :: !(Maybe Text) -- ^ Cost of sending
    , sendSuccBalance :: !(Maybe Text) -- ^ Funds of the account left after the call
    , sendSuccPhones :: !(Maybe [PhoneResp]) -- ^ Additional info by phone number
    }
  deriving (Generic, Show)

instance FromJSON SendResponse where 
  parseJSON (Object o) = do 
    merr <- o .:? "error"
    case merr of 
      Nothing -> SendSuccess
        <$> o .:? "id"
        <*> o .: "cnt"
        <*> o .:? "cost"
        <*> o .:? "balance"
        <*> o .:? "phones"
      Just err -> SendError err 
        <$> o .: "error_code"
        <*> o .:? "id"
  parseJSON _ = mzero
  
-- | Endpoint for sending sms, this is low-level most general 
-- API that is used to build small helper functions.
type SendEndpoint = "sys" :> "send.php"
  :> QueryParam "login" Text
  :> QueryParam "psw" Text 
  :> QueryParam "phones" Text
  :> QueryParam "mes" Text 
  -- additional parameters
  :> QueryParam "id" Text 
  :> QueryParam "sender" Text 
  :> QueryParam "translit" Text
  :> QueryParam "tinyurl" Text 
  :> QueryParam "time" Text 
  :> QueryParam "tz" Text 
  :> QueryParam "period" Double
  :> QueryParam "freq" Word
  :> QueryParam "flash" Word 
  :> QueryParam "bin" Text 
  :> QueryParam "push" Word 
  :> QueryParam "hlr" Word 
  :> QueryParam "ping" Word 
  :> QueryParam "mms" Word 
  :> QueryParam "mail" Word 
  :> QueryParam "call" Word 
  :> QueryParam "voice" Text 
  :> QueryParam "param" Text 
  :> QueryParam "subj" Text 
  :> QueryParam "charset" Text 
  :> QueryParam "cost" Word 
  :> QueryParam "fmt" Word -- should be always 3 
  :> QueryParam "list" Text 
  :> QueryParam "valid" Text 
  :> QueryParam "maxsms" Word 
  :> QueryParam "imgcode" Text 
  :> QueryParam "userip" Text 
  :> QueryParam "err" Word 
  :> QueryParam "op" Word 
  :> QueryParam "pp" Text
  :> ReqBody '[OctetStream] ByteString
  :> Post '[JSON] SendResponse