{-#LANGUAGE DeriveDataTypeable #-}
{-#LANGUAGE DeriveGeneric #-}
{-#LANGUAGE MultiParamTypeClasses #-}
{-#LANGUAGE OverloadedStrings #-}
{-#LANGUAGE ViewPatterns #-}

module Twilio.Call.Feedback
  ( -- * Resource
    Feedback(..)
  , Twilio.Call.Feedback.get
    -- * Types
  , Quality(..)
  , Issue(..)
  ) where

import Control.Monad
import Control.Monad.Catch
import Data.Aeson
import Data.Data
import Data.Monoid
import Data.Scientific
import Data.Time.Clock
import GHC.Generics

import Control.Monad.Twilio
import Twilio.Internal.Parser
import Twilio.Internal.Request
import Twilio.Internal.Resource as Resource
import Twilio.Types

{- Resource -}

-- | 'Feedback' is a subresource of a 'Call' instance resource. It represents a call quality feedback entry for a given phone call.
data Feedback = Feedback
  { sid          :: !CallSID
  , accountSID   :: !AccountSID
  , qualityScore :: !Quality
  , issues       :: ![Issue]
  , dateCreated  :: !UTCTime
  , dateUpdated  :: !UTCTime
  } deriving (Data, Eq, Generic, Ord, Read, Show, Typeable)

instance FromJSON Feedback where
  parseJSON (Object v) = Feedback
    <$>  v .: "sid"
    <*>  v .: "account_sid"
    <*>  v .: "quality_score"
    <*>  v .: "issues"
    <*> (v .: "date_created" >>= parseDateTime)
    <*> (v .: "date_updated" >>= parseDateTime)
  parseJSON _ = mzero

instance Get1 CallSID Feedback where
  get1 (getSID -> sid) = request parseJSONFromResponse =<< makeTwilioRequest
    ("/Calls/" <> sid <> "/Feedback.json")

-- | Get a 'Call''s 'Feedback' by 'CallSID'.
get :: MonadThrow m => CallSID -> TwilioT m Feedback
get = Resource.get

{- Types -}

-- | An integer 1 to 5 quality score where 1 represents very poor call quality and 5 represents a perfect call.
data Quality
  = Q1  -- ^ Very poor call quality
  | Q2
  | Q3
  | Q4
  | Q5  -- ^ A perfect call
  deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show, Typeable)

instance ToJSON Quality where
  toJSON Q1 = Number $ fromRational 1
  toJSON Q2 = Number $ fromRational 2
  toJSON Q3 = Number $ fromRational 3
  toJSON Q4 = Number $ fromRational 4
  toJSON Q5 = Number $ fromRational 5

instance FromJSON Quality where
  parseJSON (numberToMaybeInt -> Just 1) = return Q1
  parseJSON (numberToMaybeInt -> Just 2) = return Q2
  parseJSON (numberToMaybeInt -> Just 3) = return Q3
  parseJSON (numberToMaybeInt -> Just 4) = return Q4
  parseJSON (numberToMaybeInt -> Just 5) = return Q5
  parseJSON _ = mzero

numberToMaybeInt :: Value -> Maybe Int
numberToMaybeInt (Number n) = toBoundedInteger n
numberToMaybeInt _ = mzero