{-# LANGUAGE OverloadedStrings #-}
module Redmine.Rest(expandOptions
                   , increaseQueryRange
                   , initOpt
                   , toJsonBody
                   , queryRedmineAvecOptions
                   , runQuery

                   , ParamRest
                   ) where

import Data.Aeson
import Data.Monoid
import qualified Data.Map as Map
import Redmine.Types
import Redmine.Manager
import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as S8
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Char8 as L8
import Network.Connection (TLSSettings (..))
import Network.HTTP.Conduit
import Network.HTTP.Base (RequestMethod(..))
import Network.HTTP.Types.Header (Header, hContentType)
import qualified Data.Text as T

import Debug.Trace

queryRedmine :: RedmineMng -> RequestMethod -> S.ByteString -> (Maybe S.ByteString) -> IO L.ByteString
queryRedmine mng m req b = do
          request <- creerRqt mng m req b
          let settings = mkManagerSettings (TLSSettingsSimple True False False) Nothing
          response  <- withManagerSettings settings $ httpLbs request
          return $ responseBody response

creerRqt :: RedmineMng -> RequestMethod -> S.ByteString -> Maybe S.ByteString -> IO Request
creerRqt (RedmineMng h) m r body                       =  fmap (f body) $ parseUrl $ S8.unpack (h <> r)
  where
    ct = (hContentType, "application/json") :: Header
    f (Just b) req = trace (S8.unpack b) req'
      where req' = req{method = S8.pack . show $ m, requestHeaders = ct : requestHeaders req, requestBody = RequestBodyBS b}
    f Nothing  req = req{method = S8.pack . show $ m, requestHeaders = ct : requestHeaders req}

creerRqt (RedmineMngWithProxy h u p) m r b               = fmap (addProxy u p) (creerRqt (RedmineMng h) m r b)
creerRqt (RedmineMngWithAuth h l pass) m r b             = fmap (applyBasicAuth l pass) (creerRqt (RedmineMng h) m r b)
creerRqt (RedmineMngWithAuthAndProxy h l pass u p) m r b = fmap (applyBasicAuth l pass) (creerRqt (RedmineMngWithProxy h u p) m r b)

type ParamRest = Map.Map S.ByteString S.ByteString

-- Remplace par urlEncodedBody
expandOptions :: ParamRest -> S.ByteString
expandOptions = Map.foldrWithKey (\k a res -> res <> k <> "=" <> a <> "&") "?"

bsAInt :: S.ByteString -> Int
bsAInt = read . S8.unpack

increaseQueryRange :: ParamRest -> ParamRest
increaseQueryRange param =
  let offset = bsAInt $ Map.findWithDefault "0" "offset" param
      limit  = bsAInt $ Map.findWithDefault "100" "limit" param
      nouvelOffset = offset + limit
  in Map.insert "offset" (S8.pack $ show nouvelOffset) param

-- Réécrire avec les autres modes
queryRedmineAvecOptions :: (FromJSON a, Monoid a, Collection a) =>
                           RedmineMng -> RequestMethod -> S.ByteString -> ParamRest -> Maybe S.ByteString -> Manager -> IO( Maybe a)
queryRedmineAvecOptions redmineMng m req param body mng = -- MaybeT IO $ withSocketsDo $
  do
    request   <- creerRqt redmineMng m (req <> expandOptions param) body
    --traceM (S8.unpack $ (req <> (expandOptions param)))
    let settings = mkManagerSettings (TLSSettingsSimple True False False) Nothing
    response  <- withManagerSettings settings $ httpLbs request
    parsedRes <- debugResult . eitherDecode . responseBody $ response
    --putStrLn $ show parsedRes
    case parsedRes of
      Just a | 0 == longueur a -> return $ Just a
             | otherwise       ->
                do let hausse = increaseQueryRange param
                   --traceM . show $ hausse
                   reste <- queryRedmineAvecOptions redmineMng m req hausse body mng
                   case reste of
                      Just b -> return . Just $ mappend a b
                      Nothing -> return $ Just a
                   --return (reste >>= (\b -> Just $ mappend a b ))

      Nothing -> return Nothing

-- |The function 'debugResult' is used to print the parsing error statement
-- and continue the processing.
debugResult :: Either String a -> IO(Maybe a)
debugResult res = case res of
                    Left msg -> putStrLn msg >> return Nothing
                    Right v  -> return (Just v)

--contentType = mkHeader HdrContentType "application/json"

runQuery :: FromJSON a => RedmineMng -> RequestMethod -> S.ByteString -> Maybe S.ByteString -> IO( Maybe a)
runQuery mng m requete body = do -- withSocketsDo $ do
  toto <- queryRedmine mng m requete body
  (debugResult . eitherDecode) toto

toJsonBody :: ToJSON a => a -> Maybe S8.ByteString
toJsonBody = Just . L8.toStrict . encode . toJSON

initOpt = Map.fromList [("offset","0"), ("limit","100")] :: Map.Map S8.ByteString S8.ByteString