--------------------------------------------------------------------------------
-- Haskell bindings for the Mondo API                                         --
-- Written by Michael B. Gale (michael.gale@cl.cam.ac.uk)                     --
--------------------------------------------------------------------------------

{-# LANGUAGE FunctionalDependencies #-}

module Mondo.API where

--------------------------------------------------------------------------------

import GHC.TypeLits

import qualified Data.ByteString.Internal as BS
import Data.Proxy
import Data.Monoid

import Control.Monad.Trans
import Control.Monad.Trans.Except
import Control.Monad.Trans.Reader

import Network.HTTP.Client (Manager, newManager)
import Network.HTTP.Client.TLS

import Servant.API hiding (addHeader)
import Servant.Common.Req (Req(..), addHeader)
import Servant.Client

import Mondo.Types

--------------------------------------------------------------------------------

data MondoAuth

instance HasClient api => HasClient (MondoAuth :> api) where
    type Client (MondoAuth :> api) = String -> Client api

    clientWithRoute Proxy req url mgr val =
        clientWithRoute (Proxy :: Proxy api) (mondoAuthReq val req) url mgr

mondoAuthReq :: String -> Req -> Req
mondoAuthReq token = addHeader "Authorization" ("Bearer " <> token)

--------------------------------------------------------------------------------

data MondoAccount

instance HasClient api => HasClient (MondoAccount :> api) where
    type Client (MondoAccount :> api) = AccountID -> Client api

    clientWithRoute Proxy req url mgr val =
        clientWithRoute (Proxy :: Proxy api) (addHeader "account_id" val req) url mgr

--------------------------------------------------------------------------------

-- | A type representing the Mondo API.
type MondoAPI =
      MondoAuth :> "accounts" :> Get '[JSON] AccountsResponse
 :<|> MondoAuth :> "balance" :> QueryParam "account_id" AccountID :> Get '[JSON] Balance
 :<|> MondoAuth :> "transactions" :> Capture "transaction_id" String :> Get '[JSON] TransactionResponse
 :<|> MondoAuth :> "transactions" :> QueryParam "account_id" AccountID :> Get '[JSON] Transactions


mondoAPI :: Proxy MondoAPI
mondoAPI = Proxy

-- | The URL of the Mondo API.
mondoURL :: BaseUrl
mondoURL = BaseUrl Https "api.getmondo.co.uk" 443 "/"

--------------------------------------------------------------------------------

type Mondo = ReaderT String (ReaderT Manager (ExceptT ServantError IO))

withMondo :: String -> Mondo a -> IO (Either ServantError a)
withMondo tkn m = do
    manager <- newManager tlsManagerSettings
    runExceptT (runReaderT (runReaderT m tkn) manager)

credential :: Mondo String
credential = ask

manager :: Mondo Manager
manager = lift ask

--------------------------------------------------------------------------------

api = client mondoAPI mondoURL

getAccounts :: Mondo AccountsResponse
getAccounts = do
    mgr <- manager
    tkn <- credential
    let
        fn = getNth (Proxy :: Proxy 0) $ api mgr
    lift $ lift $ fn tkn

getBalance :: String -> Mondo Balance
getBalance acc = do
    mgr <- manager
    tkn <- credential
    let
        fn = getNth (Proxy :: Proxy 1) $ api mgr
    lift $ lift $ fn tkn (Just acc)

getTransaction :: String -> Mondo Transaction
getTransaction tr = do
    mgr <- manager
    tkn <- credential
    let
        fn = getNth (Proxy :: Proxy 2) $ api mgr
    lift $ lift (transaction <$> fn tkn tr)

getTransactions :: AccountID -> Mondo Transactions
getTransactions acc = do
    mgr <- manager
    tkn <- credential
    let
        fn = getNth (Proxy :: Proxy 3) $ api mgr
    lift $ lift $ fn tkn (Just acc)

--------------------------------------------------------------------------------

class GetNth (n :: Nat) a b | n a -> b where
    getNth :: Proxy n -> a -> b

instance x ~ y => GetNth 0 x y where
    getNth _ x = x

instance {-# OVERLAPPING #-}
  GetNth 0 (x :<|> y) x where
      getNth _ (x :<|> _) = x

instance
  (GetNth (n-1) x y) => GetNth n (a :<|> x) y where
      getNth _ (_ :<|> x) = getNth (Proxy :: Proxy (n-1)) x


--------------------------------------------------------------------------------