{-# LANGUAGE OverloadedStrings #-}

module Yesod.Auth.OAuth2.Nylas
    ( oauth2Nylas
    ) where

import Yesod.Auth.OAuth2.Prelude

import Control.Monad (unless)
import qualified Data.ByteString.Lazy.Char8 as BL8
import Network.HTTP.Client
import qualified Network.HTTP.Types as HT
import qualified Yesod.Auth.OAuth2.Exception as YesodOAuth2Exception

newtype User = User Text

instance FromJSON User where
    parseJSON = withObject "User" $ \o -> User
        <$> o .: "id"

pluginName :: Text
pluginName = "nylas"

defaultScopes :: [Text]
defaultScopes = ["email"]

oauth2Nylas :: YesodAuth m => Text -> Text -> AuthPlugin m
oauth2Nylas clientId clientSecret =
    authOAuth2 pluginName oauth $ \manager token -> do
        req <- applyBasicAuth (encodeUtf8 $ atoken $ accessToken token) ""
            <$> parseRequest "https://api.nylas.com/account"
        resp <- httpLbs req manager
        let userResponse = responseBody resp

        -- FIXME: was this working? I'm 95% sure that the client will throw its
        -- own exception on unsuccessful status codes.
        unless (HT.statusIsSuccessful $ responseStatus resp)
            $ throwIO
            $ YesodOAuth2Exception.GenericError pluginName
            $ "Unsuccessful HTTP response: "
            <> BL8.unpack userResponse

        either
                (throwIO . YesodOAuth2Exception.JSONDecodingError pluginName)
                (\(User userId) -> pure Creds
                    { credsPlugin = pluginName
                    , credsIdent = userId
                    , credsExtra = setExtra token userResponse
                    }
                )
            $ eitherDecode userResponse
  where
    oauth = OAuth2
        { oauthClientId = clientId
        , oauthClientSecret = clientSecret
        , oauthOAuthorizeEndpoint = "https://api.nylas.com/oauth/authorize"
            `withQuery` [ ("response_type", "code")
                        , ( "client_id"
                          , encodeUtf8 clientId
                          )
            -- N.B. The scopes delimeter is unknown/untested. Verify that before
            -- extracting this to an argument and offering a Scoped function. In
            -- its current state, it doesn't matter because it's only one scope.
                        , scopeParam "," defaultScopes
                        ]
        , oauthAccessTokenEndpoint = "https://api.nylas.com/oauth/token"
        , oauthCallback = Nothing
        }