{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE QuasiQuotes         #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Common where

import qualified Data.Aeson           as Aeson
import qualified Data.Aeson.QQ        as Aeson.QQ
import qualified Data.ByteString.Lazy as ByteString.Lazy
import           Data.Semigroup       ((<>))
import qualified Data.Text            as Text
import qualified Data.Text.Encoding   as Text.Encoding
import qualified Data.Text.Lazy.IO    as Text.Lazy.IO
import qualified Network.HTTP.Client  as HTTP.Client
import qualified Network.HTTP.Simple  as HTTP.Simple
import qualified Text.Pretty.Simple   as Pretty.Simple

pPrintJSON :: Aeson.Value -> IO ()
pPrintJSON
  = Text.Lazy.IO.putStrLn
  . Pretty.Simple.pString
  . Text.unpack
  . Text.Encoding.decodeUtf8
  . ByteString.Lazy.toStrict
  . Aeson.encode

pPrintToJSON :: Aeson.ToJSON a => a -> IO ()
pPrintToJSON = pPrintJSON . Aeson.toJSON

printRequest :: HTTP.Client.Request -> IO ()
printRequest request = do
  putStrLn
    $ (show . HTTP.Client.method) request
    ++ " "
    ++ (show . HTTP.Client.path) request
    ++ (Text.unpack . Text.Encoding.decodeUtf8 . HTTP.Client.queryString) request
  let
    printJSON bytes =
      case Aeson.decode bytes of
        Nothing                    -> return ()
        Just (json :: Aeson.Value) -> pPrintJSON json
  case HTTP.Client.requestBody request of
    HTTP.Client.RequestBodyLBS lazyBody -> printJSON lazyBody
    HTTP.Client.RequestBodyBS body -> printJSON . ByteString.Lazy.fromStrict $ body
    _ -> return ()

pPrintResponse :: HTTP.Client.Response Aeson.Value -> IO ()
pPrintResponse = pPrintJSON . HTTP.Client.responseBody

printHeader :: Text.Text -> IO ()
printHeader name = do
  putStrLn ""
  putStrLn "***"
  putStrLn $ "*** " <> Text.unpack name
  putStrLn "***"


host :: Text.Text
host = "127.0.0.1"

port :: Int
port = 9200

baseRequest :: HTTP.Simple.Request
baseRequest
  = HTTP.Simple.setRequestMethod "GET"
  . HTTP.Simple.setRequestPort port
  . HTTP.Simple.setRequestHost (Text.Encoding.encodeUtf8 host)
  $ HTTP.Simple.defaultRequest

createIndex :: Aeson.ToJSON a => Text.Text -> a -> IO ()
createIndex name settings
  = sendRequest_
  . HTTP.Simple.setRequestBodyJSON settings
  . HTTP.Simple.setRequestMethod "PUT"
  . HTTP.Simple.setRequestPath (Text.Encoding.encodeUtf8 ("/" <> name))
  $ baseRequest

deleteIndex :: Text.Text -> IO ()
deleteIndex name
  = sendRequest_
  . HTTP.Simple.setRequestMethod "DELETE"
  . HTTP.Simple.setRequestPath (Text.Encoding.encodeUtf8 ("/" <> name))
  $ baseRequest

refreshIndex :: Text.Text -> IO ()
refreshIndex name
  = sendRequest_
  . HTTP.Simple.setRequestMethod "POST"
  . HTTP.Simple.setRequestPath (Text.Encoding.encodeUtf8 ("/" <> name <> "/_refresh"))
  $ baseRequest

indexDocument :: Aeson.ToJSON a => Text.Text -> Text.Text -> Text.Text -> a -> IO ()
indexDocument indexName typeName documentId document
  = sendRequest_
  . HTTP.Simple.setRequestBodyJSON document
  . HTTP.Simple.setRequestMethod "PUT"
  . HTTP.Simple.setRequestPath (Text.Encoding.encodeUtf8 ("/" <> indexName <> "/" <> typeName <> "/" <> documentId))
  $ baseRequest

analyzeText :: Text.Text -> Text.Text -> Text.Text -> IO ()
analyzeText indexName analyzerName text
  =  sendRequest_
  . HTTP.Simple.setRequestBodyJSON [Aeson.QQ.aesonQQ|
{
  "analyzer": #{analyzerName},
  "text": #{text}
}
    |]
  . HTTP.Simple.setRequestPath (Text.Encoding.encodeUtf8 ("/" <> indexName <> "/_analyze"))
  $ Common.baseRequest

searchJSON :: Aeson.ToJSON a => Text.Text -> Text.Text -> a -> IO ()
searchJSON indexName typeName query
  = sendRequest_
  . HTTP.Simple.setRequestBodyJSON query
  . HTTP.Simple.setRequestMethod "GET"
  . HTTP.Simple.setRequestPath (Text.Encoding.encodeUtf8 ("/" <> indexName <> "/" <> typeName <> "/_search"))
  $ baseRequest

sendRequest_ :: HTTP.Simple.Request -> IO ()
sendRequest_ request = do
  _ <- sendRequest request
  return ()

sendRequest :: HTTP.Simple.Request -> IO (HTTP.Simple.Response Aeson.Value)
sendRequest request = do
  printRequest request
  response <- HTTP.Simple.httpJSON request
  Common.pPrintResponse response
  putStrLn ""
  return response
