{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TemplateHaskell #-}

module HSForce
    ( login,
      soapLogin,
      restLogin,
      defaultLoginRequest,
      HSForce.versions,
      HSForce.query,
      HSForce.queryAll,
      HSForce.queryMore,
      HSForce.queryAllMore,
      HSForce.recordCount,
      HSForce.insert,
      HSForce.update,
      HSForce.upsert,
      HSForce.delete,
      HSForce.describe,
      HSForce.describeDetail,
      HSForce.describeGlobal,
      HSForce.explain,
      HSForce.Types.SObject(..),
      HSForce.Client.SFClient(..),
    ) where

import Network.HTTP.Conduit
import Network.URI
import Network.URI.Encode as URI
import System.IO
import System.Environment
import Data.Aeson as JSON
import Data.Aeson.TH (deriveJSON, defaultOptions, Options(..))
import Data.ByteString.Char8 as B8
import Data.ByteString.Lazy.Char8 as BL8
import Data.Proxy as DP
import Data.Maybe
import Data.List as L
import Text.HTML.TagSoup.Entity
import Text.XML.HaXml
import Text.XML.HaXml.Posn
import Text.XML.HaXml.Util
import Text.XML.HaXml.Xtract.Parse
import Text.Regex.Posix
import GHC.Generics
import HSForce.Util
import HSForce.Client
import HSForce.Types

query :: (FromJSON a) => SFClient -> String -> DP.Proxy a -> IO (QueryResponse a)
query client q _ = do
  let path = dataPath client ++ "/query/?q=" ++ URI.encode q
  response <- requestGet client path
  let res = (JSON.decode $ responseBody response) :: (FromJSON a) => Maybe (QueryResponse a)
  return (fromJust res)

queryMore :: (FromJSON a) => SFClient -> String -> DP.Proxy a -> IO (QueryResponse a)
queryMore client qpath _ = do
  response <- requestGet client qpath
  let res = (JSON.decode $ responseBody response) :: (FromJSON a) => Maybe (QueryResponse a)
  return (fromJust res)

queryAll :: (FromJSON a) => SFClient -> String -> DP.Proxy a -> IO (QueryResponse a)
queryAll client q _ = do
  let path = dataPath client ++ "/queryAll/?q=" ++ URI.encode q
  response <- requestGet client path
  let res = (JSON.decode $ responseBody response) :: (FromJSON a) => Maybe (QueryResponse a)
  return (fromJust res)

queryAllMore :: (FromJSON a) => SFClient -> String -> DP.Proxy a -> IO (QueryResponse a)
queryAllMore client qpath _ = do
  response <- requestGet client qpath
  let res = (JSON.decode $ responseBody response) :: (FromJSON a) => Maybe (QueryResponse a)
  return (fromJust res)

explain :: SFClient -> String -> IO (Explain)
explain client q = do
  let path = dataPath client ++ "/query/?explain=" ++ URI.encode q
  response <- requestGet client path
  let res = (JSON.decode $ responseBody response) :: Maybe Explain
  return (fromJust res)

search :: (FromJSON a) => SFClient -> String -> DP.Proxy a -> IO (QueryResponse a)
search client q _ = do
  let path = dataPath client ++ "/search/?q=" ++ URI.encode q
  response <- requestGet client path
  let res = (JSON.decode $ responseBody response) :: (FromJSON a) => Maybe (QueryResponse a)
  return (fromJust res)

recordCount :: SFClient -> [String] -> IO (RecordCount)
recordCount client objects = do
  let path = dataPath client ++ "/limits/recordCount?sObjects=" ++ L.intercalate "," objects
  response <- requestGet client path
  let res = (JSON.decode $ responseBody response) :: Maybe RecordCount
  return (fromJust res)

versions :: SFClient -> IO ([Version])
versions client = do
  response <- requestGet client "/services/data"
  let res = (JSON.decode $ responseBody response) :: Maybe [Version]
  return (fromJust res)

batchRequest :: (ToJSON a) => SFClient -> [BatchRequest a] -> IO ()
batchRequest client requests = do
  let path = dataPath client ++ "/composite/batch"
  response <- requestPost client path $ BL8.unpack $ JSON.encode requests -- TODO: impl
  return ()

tree :: (ToJSON a, SObject a) => SFClient -> [a] -> IO ()
tree client objects = do
  let path = dataPath client ++ "/composite/tree/" ++ typeName (objects !! 0)
  response <- requestPost client path $ BL8.unpack $ JSON.encode objects
  return ()

insert :: (SObject a, ToJSON a) => SFClient -> a -> IO ()
insert client object = do
  let path = dataPath client ++ "/sobjects/" ++ typeName object
  response <- requestPost client path $ BL8.unpack $ JSON.encode object
  return ()

update :: (SObject a, ToJSON a) => SFClient -> a -> IO ()
update client object = do
  let path = dataPath client ++ "/sobjects/" ++ typeName object ++ '/':getSfid object
  response <- requestPatch client path $ BL8.unpack $ JSON.encode object
  return ()

upsert :: (SObject a, ToJSON a) => SFClient -> a -> String -> String -> IO ()
upsert client object upsertKey upsertKeyValue = do
  let path = dataPath client ++ "/sobjects/" ++ typeName object ++ '/':upsertKey ++ '/':upsertKeyValue
  response <- requestPatch client path $ BL8.unpack $ JSON.encode object
  return ()

delete :: (SObject a, ToJSON a) => SFClient -> a -> IO ()
delete client object = do
  let path = dataPath client ++ "/sobjects/" ++ typeName object ++ '/':getSfid object
  response <- requestDelete client path
  return ()

describe :: (FromJSON a) => SFClient -> String -> DP.Proxy a -> IO (DescribeResponse a)
describe client objectName _ = do
  let path = dataPath client ++ "/sobjects/" ++ objectName
  response <- requestGet client path
  let res = (JSON.decode $ responseBody response) :: (FromJSON a) => Maybe (DescribeResponse a)
  return (fromJust res)

describeDetail :: SFClient -> String -> IO (DescribeDetail)
describeDetail client objectName = do
  let path = dataPath client ++ "/sobjects/" ++ objectName ++ "/describe"
  response <- requestGet client path
  let res = (JSON.decode $ responseBody response) :: Maybe DescribeDetail
  return (fromJust res)

describeGlobal :: SFClient -> IO GlobalDescribeResponse
describeGlobal client = do
  let path = dataPath client ++ "/sobjects"
  response <- requestGet client path
  let res = (JSON.decode $ responseBody response) :: Maybe GlobalDescribeResponse
  return (fromJust res)