-- |
-- Smith API definition response combinators.
--
-- You shouldn't need these unless defining custom/additional calls, see
-- 'Smith.Client.Api' for complete API definition.
--
module Smith.Client.Response (
    ResponseError (..)

  , Responder (..)

  , json
  ) where

import           Data.Aeson (Value)
import           Data.Aeson.Types (Parser)
import           Data.Bifunctor (Bifunctor (..))
import qualified Data.ByteString.Lazy as Lazy
import           Data.Text (Text)

import qualified Network.HTTP.Client as HTTP
import qualified Network.HTTP.Types as HTTP
import           Smith.Client.Error
import qualified Smith.Client.Serial.Decode as Decode


data ResponseError =
    ParseResponseError Int Lazy.ByteString Text
  | UnknownStatusResponseError Int Lazy.ByteString
    deriving (Eq, Ord, Show)

newtype Responder a =
  Responder {
      runResponder :: HTTP.Response Lazy.ByteString -> Either SmithError a
    }

json :: Int -> (Value -> Parser a) -> Responder a
json code parser =
  Responder $ \res ->
    case (HTTP.statusCode . HTTP.responseStatus) res of
      400 ->
        (first (SmithResponseParseError 400 (HTTP.responseBody res)) $
          Decode.parse Decode.errored (HTTP.responseBody res)) >>= Left
      403 ->
        (first (SmithResponseParseError 403 (HTTP.responseBody res)) $
          Decode.parse Decode.forbidden (HTTP.responseBody res)) >>= Left
      x | x == code ->
        first (SmithResponseParseError x (HTTP.responseBody res)) $
          Decode.parse parser (HTTP.responseBody res)
      x ->
        Left $
          SmithStatusCodeError x (HTTP.responseBody res)