{-# LANGUAGE RecordWildCards #-}

-- | Functions for making Oz-authenticated requests.
--
-- This module is under construction.

module Network.Oz.Client
  ( header
  -- , withSession
  , reissue
  , Endpoints(..)
  , defaultEndpoints
  ) where

import           Data.ByteString           (ByteString)
import           Data.Text                 (Text)
import           Data.Time.Clock.POSIX     (POSIXTime, getPOSIXTime)

-- import Network.HTTP.Types.URI (URI)
import           Network.HTTP.Client       (Request, defaultManagerSettings,
                                            managerModifyRequest,
                                            managerRetryableException)
import           Network.HTTP.Types.Header (Header, hWWWAuthenticate)
import           Network.HTTP.Types.Method (Method)

import           Control.Exception         (SomeException)
import           Data.IORef                (newIORef, readIORef, writeIORef)

-- import Network.Wai (Request, requestHeaderHost, requestHeaders, remoteHost, requestMethod, rawPathInfo, rawQueryString)

{-
import           Network.Wreq.Session      (Session)
import qualified Network.Wreq.Session      as S
-}

import qualified Network.Hawk.Client       as Hawk
import           Network.Oz.Types

-- |A convenience utility to generate the application Hawk request
-- authorization header for making authenticated Oz requests.
header :: Text -> Method -> OzSealedTicket -> IO Hawk.Header
-- fixme: support hawk header options
header uri method t@OzSealedTicket{..} = Hawk.header uri method creds Nothing 0 Nothing
  where
    creds = ticketCreds t
    -- fixme: app and dlg need to get passed to header

ticketCreds :: OzSealedTicket -> Hawk.Credentials
ticketCreds OzSealedTicket{..} = Hawk.Credentials ozTicketId ozTicketKey ozTicketAlgorithm

{-
-- | Work in progress.
withSession :: Endpoints -> Text -> Hawk.Credentials -> (Session -> IO a) -> IO a
withSession ep uri creds act = do
  ref <- newIORef (Nothing :: Maybe OzTicket)
  S.withSessionControl Nothing settings act
  where
    settings = defaultManagerSettings
               { managerModifyRequest = addAuth
               , managerRetryableException = shouldRetry }
    addAuth :: Request -> IO Request
    -- fixme: check ticket ref
    addAuth = return
    shouldRetry :: SomeException -> Bool
    -- fixme: catch unauthorized and retry with auth
    shouldRetry = managerRetryableException defaultManagerSettings
-}

-- | Re-issues (refreshes) a ticket.
reissue :: Endpoints -> Hawk.Credentials -> OzTicket -> IO (Either String OzTicket)
reissue ep creds OzTicket{..} = undefined -- fixme: implement

-- | Re-issues a ticket if it has expired.
-- fixme: maybe need slightly earlier reissue
reissueMaybe :: Endpoints -> Hawk.Credentials -> OzTicket -> IO (Either String OzTicket)
reissueMaybe ep creds t = do
  now <- getPOSIXTime
  if now >= ozTicketExp t
    then reissue ep creds t
    else return (Right t)

{- connection.request(path, ticket, options, callback)

Requests a protected resource where:

path - the resource path (e.g. '/resource').
ticket - the application or user ticket. If the ticket is expired, it will automatically attempt to refresh it.
options - optional configuration object where:
method - the HTTP method (e.g. 'GET'). Defaults to 'GET'.
payload - the request payload object or string. Defaults to no payload.
callback - the callback method using the signature function(err, result, code, ticket) where:
err - an error condition.
result - the requested resource (parsed to object if JSON).
code - the HTTP response code.
ticket - the ticket used to make the request (may be different from the ticket provided when the ticket was expired and refreshed).

-> sessionRequest
  probably not needed because withSession should handle ticket


connection.app(path, options, callback)

Requests a protected resource using a shared application ticket where:

path - the resource path (e.g. '/resource').
options - optional configuration object where:
method - the HTTP method (e.g. 'GET'). Defaults to 'GET'.
payload - the request payload object or string. Defaults to no payload.
callback - the callback method using the signature function(err, result, code, ticket) where:
err - an error condition.
result - the requested resource (parsed to object if JSON).
code - the HTTP response code.
ticket - the ticket used to make the request (may be different from the ticket provided when the ticket was expired and refreshed).
Once an application ticket is obtained internally using the provided hawk credentials in the constructor, it will be reused by called to connection.app(). If it expires, it will automatically refresh and stored for future usage.

-> sessionRequestApp
   not sure if needed

-}