{-# LANGUAGE DataKinds                  #-}
{-# LANGUAGE DeriveDataTypeable         #-}
{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE TypeApplications           #-}
{-# LANGUAGE TypeOperators              #-}

-- | Servant types and client for the authorization API
module Microsoft.Translator.API.Auth (

      SubscriptionKey (..)
    , AuthToken
    , TranslatorException
    , issueToken

) where

import           Microsoft.Translator.Exception

import           Control.Arrow        (left)
import           Data.Bifunctor
import           Data.ByteString.Lazy (toStrict)
import           Data.Monoid
import           Data.String
import           Data.Text            (Text)
import           Data.Text.Encoding   (decodeUtf8')
import           Data.Typeable
import           GHC.Generics         (Generic)
import           Network.HTTP.Client  hiding (Proxy)
import qualified Network.HTTP.Media   as M
import           Servant.API
import           Servant.Client


authUrl :: BaseUrl
authUrl = BaseUrl Https "api.cognitive.microsoft.com" 443 "/sts/v1.0"

-- | MS Microsoft.Translator token service API
--   http://docs.microsofttranslator.com/oauth-token.html
type AuthAPI =
    "issueToken"
        :> QueryParam "Subscription-Key" SubscriptionKey
        :> Post '[JWT] AuthToken

-- | A key to your subscription to the service. Used to retrieve an 'AuthToken'.
newtype SubscriptionKey
    = SubKey Text
    deriving (Show, ToHttpApiData, IsString)

-- | The JSON Web Token issued by MS Microsoft.Translator token service. Consists of wrapped text.
--   Valid for ten minutes.
newtype AuthToken
    = AuthToken Text
    deriving (Show, Generic)

-- | JSON Web Token content type
data JWT
    deriving Typeable

instance Accept JWT where
    contentType _ = "application" M.// "jwt" M./: ("charset", "us-ascii")

instance MimeUnrender JWT AuthToken where
    mimeUnrender _ = fmap AuthToken . left show . decodeUtf8' . toStrict

instance ToHttpApiData AuthToken where
    toUrlPiece (AuthToken txt) = "Bearer " <> txt


authClient :: Maybe SubscriptionKey -> ClientM AuthToken
authClient = client (Proxy @ AuthAPI)

-- | Retrieve a token from the API. It will be valid for 10 minutes.
issueToken :: Manager -> SubscriptionKey -> IO (Either TranslatorException AuthToken)
issueToken man key = first APIException <$>
    runClientM (authClient $ Just key) (ClientEnv man authUrl)