{-|
Module      : GoPro.Plus.Auth
Description : Functionality for authenticating to GoPro Plus.
Copyright   : (c) Dustin Sallings, 2020
License     : BSD3
Maintainer  : dustin@spy.net
Stability   : experimental

GoPro Plus authentication.
-}

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RecordWildCards   #-}
{-# LANGUAGE TemplateHaskell   #-}

module GoPro.Plus.Auth (
  authenticate, refreshAuth,
  AuthInfo(..), access_token, expires_in, refresh_token, resource_owner_id,
  HasGoProAuth(..), withAuth, AuthReader, Token
  ) where

import           Control.Lens
import           Control.Monad.IO.Class   (MonadIO (..))
import           Control.Monad.Reader     (ReaderT (..), ask, runReaderT)
import           Data.Aeson               (FromJSON (..), genericParseJSON)
import           Data.Text                (Text)
import           Generics.Deriving.Base   (Generic)
import           Network.Wreq             (FormParam (..))


import           GoPro.Plus.Internal.HTTP

apiClientID, apiClientSecret :: String
apiClientID = "71611e67ea968cfacf45e2b6936c81156fcf5dbe553a2bf2d342da1562d05f46"
apiClientSecret = "3863c9b438c07b82f39ab3eeeef9c24fefa50c6856253e3f1d37e0e3b1ead68d"

authURL :: String
authURL = "https://api.gopro.com/v1/oauth2/token"

type Token = Text

-- | An Authentication response.
data AuthInfo = AuthInfo {
  _access_token        :: Token
  , _expires_in        :: Int
  , _refresh_token     :: Text
  , _resource_owner_id :: Text
  } deriving(Generic, Show)

class Monad m => HasGoProAuth m where
  goproAuth :: m AuthInfo

instance FromJSON AuthInfo where
  parseJSON = genericParseJSON jsonOpts

makeLenses ''AuthInfo

authenticate :: MonadIO m
             => String -- ^ Email/username
             -> String -- ^ Password
             -> m AuthInfo
authenticate username password =
  jpostWith defOpts authURL ["grant_type" := ("password" :: String),
                             "client_id" := apiClientID,
                             "client_secret" := apiClientSecret,
                             "scope" := ("root root:channels public me upload media_library_beta live" :: String),
                             "username" := username,
                             "password" := password]

-- | Refresh authentication credentials using a refresh token.
refreshAuth :: MonadIO m => AuthInfo -> m AuthInfo
refreshAuth AuthInfo{..} =
  jpostWith defOpts authURL ["grant_type" := ("refresh_token" :: String),
                             "client_id" := apiClientID,
                             "client_secret" := apiClientSecret,
                             "refresh_token" := _refresh_token]

type AuthReader = ReaderT AuthInfo

instance Monad m => HasGoProAuth (AuthReader m) where
  goproAuth = ask

-- | Convenient function for passing around auth info.  You probably
-- don't want to use this, but it can be convenient when
-- experimenting.
withAuth :: AuthInfo -> AuthReader m a -> m a
withAuth = flip runReaderT