module Solr.HTTPRequestEncoder where

import Solr.Prelude
import qualified Data.ByteString.Lazy
import qualified Data.ByteString.Char8
import qualified Data.CaseInsensitive
import qualified JSONEncoder
import qualified Network.HTTP.Client
import qualified ByteString.TreeBuilder


encoder_request :: Request a -> a -> Network.HTTP.Client.Request
encoder_request (Request (Op impl)) input =
  appEndo (impl input) init
  where
    init =
      either (error . show) id $
      Network.HTTP.Client.parseUrl "http://www"


newtype Request a =
  Request (Op (Endo Network.HTTP.Client.Request) a)
  deriving (Contravariant, Divisible, Decidable, Semigroup, Monoid)

request_body :: Body a -> Request a
request_body (Body impl) =
  Request $
  Op $
  \input ->
    Endo $
    \request ->
      request {
        Network.HTTP.Client.requestBody = (impl input)
      }

request_method :: ByteString -> Request a
request_method method =
  Request $
  Op $
  const $
  Endo $
  \request ->
    request {
      Network.HTTP.Client.method = method
    }

request_header :: ByteString -> ByteString -> Request a
request_header name value =
  Request $
  Op $
  const $
  Endo $
  \request ->
    request {
      Network.HTTP.Client.requestHeaders =
        (Data.CaseInsensitive.mk name, value) :
        Network.HTTP.Client.requestHeaders request
    }

request_url :: ByteString -> Request a
request_url url =
  Request $
  Op $
  const $
  Endo $
  case parsedRequestMaybe of
    Just parsedRequest ->
      \request ->
        request {
          Network.HTTP.Client.secure =
            Network.HTTP.Client.secure parsedRequest,
          Network.HTTP.Client.host =
            Network.HTTP.Client.host parsedRequest,
          Network.HTTP.Client.port =
            Network.HTTP.Client.port parsedRequest,
          Network.HTTP.Client.path =
            Network.HTTP.Client.path parsedRequest,
          Network.HTTP.Client.queryString =
            Network.HTTP.Client.queryString parsedRequest
        }
    Nothing -> id
  where
    parsedRequestMaybe =
      Network.HTTP.Client.parseUrl $
      Data.ByteString.Char8.unpack $
      url


newtype Body a =
  Body (a -> Network.HTTP.Client.RequestBody)

instance Contravariant Body where
  {-# INLINE contramap #-}
  contramap f (Body impl) =
    Body (\input -> impl (f input))

body_unit :: Body ()
body_unit =
  Body $
  const $
  Network.HTTP.Client.RequestBodyBS mempty

body_json :: JSONEncoder.Value a -> Body a
body_json spec =
  Body $
  \input ->
    Network.HTTP.Client.RequestBodyLBS $
    ByteString.TreeBuilder.toLazyByteString $
    JSONEncoder.run spec input


-- * Specific
-------------------------

request_postJSON :: ByteString -> JSONEncoder.Value a -> Request a
request_postJSON url jsonEncoder =
  request_url url <>
  request_method "POST" <>
  request_header "content-type" "application/json" <>
  request_body (body_json jsonEncoder)