{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}

module Network.PinPon.WireTypes.SNS
  ( -- * Amazon Simple Notification Service on-the-wire types
    Message(..)
  , defaultText
  , apnsPayload
  , apnsSandboxPayload
  , defaultMessage
  ) where

import Protolude
import Control.Lens (makeLenses)
import Data.Aeson.Types
       ((.=), Pair, Series, ToJSON(..), object, pairs)
import Data.Monoid ((<>))
import Data.Text (Text)

import Network.PinPon.Util (encodeText)
import qualified Network.PinPon.WireTypes.APNS as APNS (Payload(..))

-- $setup
-- >>> :set -XOverloadedStrings

-- | A multi-platform SNS message.
data Message = Message
  { _defaultText        :: !Text              -- ^ The default message contents (required by SNS)
  , _apnsPayload        :: Maybe APNS.Payload -- ^ Optional production APNS payload
  , _apnsSandboxPayload :: Maybe APNS.Payload -- ^ Optional sandbox APNS payload
  } deriving (Show, Generic)

makeLenses ''Message

-- | A @pinpon@-specific default value for 'Message'.
defaultMessage :: Message
defaultMessage = Message "Someone is ringing the doorbell!" Nothing Nothing

instance ToJSON Message where
  toJSON (Message d p s) = object $! ["default" .= d] <> apns p <> apnsSandbox s
    where
      apns :: Maybe APNS.Payload -> [Pair]
      apns Nothing = mempty
      apns v = ["APNS" .= encodeText v]
      apnsSandbox :: Maybe APNS.Payload -> [Pair]
      apnsSandbox Nothing = mempty
      apnsSandbox v = ["APNS_SANDBOX" .= encodeText v]
  toEncoding (Message d p s) = pairs $! "default" .= d <> apns p <> apnsSandbox s
    where
      apns :: Maybe APNS.Payload -> Series
      apns Nothing = mempty
      apns v = "APNS" .= encodeText v
      apnsSandbox :: Maybe APNS.Payload -> Series
      apnsSandbox Nothing = mempty
      apnsSandbox v = "APNS_SANDBOX" .= encodeText v

-- $
-- >>> import Network.PinPon.WireTypes.APNS (Alert(..), Aps(..), Payload(..))
-- >>> import Data.Aeson (encode)
-- >>> let alert1 = Alert "This is a production alert title" "This is a production alert body"
-- >>> let aps1 = Aps alert1 "production default"
-- >>> let payload1 = Payload aps1
-- >>> let msg1 = Message "This is the default message" (Just payload1) Nothing
-- >>> let encodedMsg1 = encode $ toJSON msg1
-- >>> encodedMsg1 == "{\"default\":\"This is the default message\",\"APNS\":\"{\\\"aps\\\":{\\\"alert\\\":{\\\"title\\\":\\\"This is a production alert title\\\",\\\"body\\\":\\\"This is a production alert body\\\"},\\\"sound\\\":\\\"production default\\\"}}\"}" || encodedMsg1 == "{\"APNS\":\"{\\\"aps\\\":{\\\"alert\\\":{\\\"title\\\":\\\"This is a production alert title\\\",\\\"body\\\":\\\"This is a production alert body\\\"},\\\"sound\\\":\\\"production default\\\"}}\",\"default\":\"This is the default message\"}"
-- True
-- >>> let alert2 = Alert "This is a sandbox alert title" "This is a sandbox alert body"
-- >>> let aps2 = Aps alert2 "sandbox default"
-- >>> let payload2 = Payload aps2
-- >>> let msg2 = Message "This is the default message" Nothing (Just payload2)
-- >>> let encodedMsg2 = encode $ toJSON msg2
-- >>> encodedMsg2 == "{\"default\":\"This is the default message\",\"APNS_SANDBOX\":\"{\\\"aps\\\":{\\\"alert\\\":{\\\"title\\\":\\\"This is a sandbox alert title\\\",\\\"body\\\":\\\"This is a sandbox alert body\\\"},\\\"sound\\\":\\\"sandbox default\\\"}}\"}" || encodedMsg2 == "{\"default\":\"This is the default message\",\"APNS_SANDBOX\":\"{\\\"aps\\\":{\\\"alert\\\":{\\\"title\\\":\\\"This is a sandbox alert title\\\",\\\"body\\\":\\\"This is a sandbox alert body\\\"},\\\"sound\\\":\\\"sandbox default\\\"}}\"}"
-- True
-- >>> let msg3 = Message "This is the default message" (Just payload1) (Just payload2)
-- >>> let encodedMsg3 = encode $ toJSON msg3
-- >>> encodedMsg3 == "{\"default\":\"This is the default message\",\"APNS_SANDBOX\":\"{\\\"aps\\\":{\\\"alert\\\":{\\\"title\\\":\\\"This is a sandbox alert title\\\",\\\"body\\\":\\\"This is a sandbox alert body\\\"},\\\"sound\\\":\\\"sandbox default\\\"}}\",\"APNS\":\"{\\\"aps\\\":{\\\"alert\\\":{\\\"title\\\":\\\"This is a production alert title\\\",\\\"body\\\":\\\"This is a production alert body\\\"},\\\"sound\\\":\\\"production default\\\"}}\"}" || encodedMsg3 == "{\"APNS\":\"{\\\"aps\\\":{\\\"alert\\\":{\\\"title\\\":\\\"This is a production alert title\\\",\\\"body\\\":\\\"This is a production alert body\\\"},\\\"sound\\\":\\\"production default\\\"}}\",\"default\":\"This is the default message\",\"APNS_SANDBOX\":\"{\\\"aps\\\":{\\\"alert\\\":{\\\"title\\\":\\\"This is a sandbox alert title\\\",\\\"body\\\":\\\"This is a sandbox alert body\\\"},\\\"sound\\\":\\\"sandbox default\\\"}}\"}"
-- True