{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}

{-# OPTIONS_GHC -fno-warn-unused-binds #-}

module LendingClub.Internal.JSON
    ( jsonGet
--     , csvGet
--     , csvGetStream
    , investRequest
    ) where

import           Blaze.ByteString.Builder  (fromLazyByteString)
import           Network.Http.Client
import           OpenSSL
import           System.IO.Streams         (InputStream, write)

import           Control.Monad.Reader      (liftIO)

import           Data.Aeson                (FromJSON, ToJSON, encode)
import           Data.ByteString           (ByteString)
import qualified Data.ByteString.Char8     as C
import           Data.Monoid               ((<>))

import           GHC.Generics

import           LendingClub.Authorization
import           LendingClub.Invest
import           LendingClub.Money

-- | Make a GET request to LendingClub's JSON api, return the parsed JSON data
jsonGet
    :: FromJSON a
    => Authorization -- ^ The authorization for the lending club user
    -> ByteString -- ^ The name of the API service, relative to the API url
    -> IO a -- ^ JSON response
jsonGet auth url = issueRequest auth url GET "application/json" jsonHandler (Nothing :: Maybe ())

{- TODO Redo for LendingClub
-- | Make a GET request to Prosper's CSV api, return the raw CSV data
csvGet
    :: UserInfo -- ^ The user name and password for the prosper user
    -> ByteString -- ^ The name of the API service, relative to the API url
    -> Prosper ByteString -- ^ Raw CSV response
csvGet userInfo url = issueRequest userInfo url GET "text/csv" concatHandler []

-- | Make a GET request to Prosper's CSV api, return the raw CSV data
csvGetStream
    :: UserInfo -- ^ The user name and password for the prosper user
    -> ByteString -- ^ The name of the API service, relative to the API url
    -> (InputStream ByteString -> IO a)
    -> Prosper a -- ^ Raw CSV response
csvGetStream userInfo url handler =
    issueRequest userInfo url GET "text/csv" (\_ is -> handler is) []
-}

data InvestRequest = InvestRequest
    { aid    :: !Int
    , orders :: ![Order]
    } deriving (Generic, Show)

data Order = Order
    { loanId          :: !Int
    , requestedAmount :: !Money
    -- , portfolioId :: !Int -- ^ Implement later
    } deriving (Generic, Show)

instance ToJSON Order where
instance ToJSON InvestRequest where

investRequest
    :: Authorization
    -> InvestorId
    -> Int -- ^ The listing id
    -> Money -- ^ Amount
    -> IO InvestResponse -- ^ JSON response from LendingClub
investRequest auth (InvestorId invId) listingId amount =
    issueRequest auth ("accounts/" <> C.pack (show invId) <> "/orders") POST "application/json" jsonHandler $
        Just InvestRequest
            { aid = invId
            , orders = [Order listingId amount]
            }

apiUrl :: ByteString
apiUrl = "api.lendingclub.com"

issueRequest
    :: (FromJSON a, ToJSON b)
    => Authorization
    -> ByteString
    -> Method
    -> ContentType
    -> (Response -> InputStream ByteString -> IO a)
    -> Maybe b
    -> IO a
issueRequest (Authorization auth) url method ct handler mBody = withOpenSSL $ do
    ctx <- baselineContextSSL
    con <- openConnectionSSL ctx apiUrl 443
    req <- buildRequest $ do
        http method $ "/api/investor/v1/" <> url
        setHeader "Authorization" auth
        setContentType ct
    sendRequest con req (setBody mBody)
    resp <- receiveResponse con handler
    closeConnection con
    return resp
  where
    setBody Nothing = emptyBody
    setBody (Just body) = write (Just (buildJSON body))

    buildJSON = fromLazyByteString . encode