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

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

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

module Bittrex.Internal where

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

import           Bittrex.Types
import qualified Control.Lens          as Lens
import           Data.Aeson
import           Data.Aeson.Lens       (key, nth)
import qualified Data.ByteString.Char8 as BSC8
import qualified Data.ByteString.Lazy  as LBS
import           Data.Char             (toLower)
import           Data.Digest.Pure.SHA
import           Data.List             (intercalate)
import           Data.List.Split       (splitOn)
import           Data.Monoid
import qualified Data.Text             as Text
import           Data.Time.Clock.POSIX
import           Debug.Trace
import           Flow                  ((.>), (|>))
import           Network.Wreq

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

-- | Default API options
defOpts :: APIOpts
defOpts = APIOpts PublicAPI [] "v1.1" mempty (APIKeys mempty mempty)

-- | Internal, used for dispatching an API call
callAPI
  :: (FromJSON v)
  => APIOpts
  -> IO (Either ErrorMessage v)
callAPI (APIOpts {..}) = do
  let (APIKeys {..}) = apiOptsKeys
  nonce <- head . splitOn "." . show <$> getPOSIXTime
  let addAuth = apiOptsAPIType `elem` [AccountAPI, MarketAPI]
  let authParams = [ [("apikey", apiKey) | addAuth]
                   , [("nonce", nonce)   | addAuth]
                   ] |> mconcat
  let url = [ "https://bittrex.com/api"
            , Text.unpack apiOptsVersion
            , toLower <$> show apiOptsAPIType
            , Text.unpack apiOptsPath
            ] |> intercalate "/"
  let go (k, v) = k <> "=" <> v <> "&"
  let urlForHash = [ url, "?", go =<< do apiOptsQueryParams ++ authParams
                   ] |> mconcat |> init
  let addHeader o = let secretLBS = LBS.fromStrict (BSC8.pack secretKey)
                        apisign = BSC8.pack
                                  $ showDigest
                                  $ hmacSha512 secretLBS
                                  $ LBS.fromStrict (BSC8.pack urlForHash)
                    in if addAuth
                       then o |> Lens.set (header "apisign") [apisign]
                       else o
  r <- getWith (addHeader defaults) urlForHash
  let Just (Bool success) = r |> Lens.preview (responseBody . key "success")
  let Just result = r |> Lens.preview (responseBody . key "result")
  let Just msg = r |> Lens.preview (responseBody . key "message")
  pure $ if success
         then case fromJSON result of
                Error s   -> Left (DecodeFailure s result)
                Success m -> Right m
         else case fromJSON msg of
                Error s   -> Left (DecodeFailure s msg)
                Success m -> Left (BittrexError m)

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