{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}

module Servant.Server.UsingConfigSpec where

import           Control.Monad.Trans.Except
import           Network.Wai
import           Test.Hspec (Spec, describe, it)
import           Test.Hspec.Wai

import           Servant
import           Servant.Server.UsingConfigSpec.TestCombinators

spec :: Spec
spec = do
  spec1
  spec2
  spec3
  spec4

-- * API

type OneEntryAPI =
  ExtractFromConfig :> Get '[JSON] String

testServer :: String -> ExceptT ServantErr IO String
testServer s = return s

oneEntryApp :: Application
oneEntryApp =
  serve (Proxy :: Proxy OneEntryAPI) config testServer
  where
    config :: Config '[String]
    config = "configEntry" :. EmptyConfig

type OneEntryTwiceAPI =
  "foo" :> ExtractFromConfig :> Get '[JSON] String :<|>
  "bar" :> ExtractFromConfig :> Get '[JSON] String

oneEntryTwiceApp :: Application
oneEntryTwiceApp = serve (Proxy :: Proxy OneEntryTwiceAPI) config $
  testServer :<|>
  testServer
  where
    config :: Config '[String]
    config = "configEntryTwice" :. EmptyConfig

-- * tests

spec1 :: Spec
spec1 = do
  describe "accessing config entries from custom combinators" $ do
    with (return oneEntryApp) $ do
      it "allows retrieving a ConfigEntry" $ do
        get "/" `shouldRespondWith` "\"configEntry\""

    with (return oneEntryTwiceApp) $ do
      it "allows retrieving the same ConfigEntry twice" $ do
        get "/foo" `shouldRespondWith` "\"configEntryTwice\""
        get "/bar" `shouldRespondWith` "\"configEntryTwice\""

type InjectAPI =
  InjectIntoConfig :> "untagged" :> ExtractFromConfig :>
    Get '[JSON] String :<|>
  InjectIntoConfig :> "tagged" :> ExtractFromConfig :>
    Get '[JSON] String

injectApp :: Application
injectApp = serve (Proxy :: Proxy InjectAPI) config $
  (\ s -> return s) :<|>
  (\ s -> return ("tagged: " ++ s))
  where
    config = EmptyConfig

spec2 :: Spec
spec2 = do
  with (return injectApp) $ do
    describe "inserting config entries with custom combinators" $ do
      it "allows to inject config entries" $ do
        get "/untagged" `shouldRespondWith` "\"injected\""

      it "allows to inject tagged config entries" $ do
        get "/tagged" `shouldRespondWith` "\"tagged: injected\""

type WithBirdfaceAPI =
  "foo" :> ExtractFromConfig :> Get '[JSON] String :<|>
  NamedConfigWithBirdface "sub" '[String] :>
    "bar" :> ExtractFromConfig :> Get '[JSON] String

withBirdfaceApp :: Application
withBirdfaceApp = serve (Proxy :: Proxy WithBirdfaceAPI) config $
  testServer :<|>
  testServer
  where
    config :: Config '[String, (NamedConfig "sub" '[String])]
    config =
      "firstEntry" :.
      (NamedConfig ("secondEntry" :. EmptyConfig)) :.
      EmptyConfig

spec3 :: Spec
spec3 = do
  with (return withBirdfaceApp) $ do
    it "allows retrieving different ConfigEntries for the same combinator" $ do
      get "/foo" `shouldRespondWith` "\"firstEntry\""
      get "/bar" `shouldRespondWith` "\"secondEntry\""

type NamedConfigAPI =
  WithNamedConfig "sub" '[String] (
    ExtractFromConfig :> Get '[JSON] String)

namedConfigApp :: Application
namedConfigApp = serve (Proxy :: Proxy NamedConfigAPI) config return
  where
    config :: Config '[NamedConfig "sub" '[String]]
    config = NamedConfig ("descend" :. EmptyConfig) :. EmptyConfig

spec4 :: Spec
spec4 = do
  with (return namedConfigApp) $ do
    describe "WithNamedConfig" $ do
      it "allows descending into a subconfig for a given api" $ do
        get "/" `shouldRespondWith` "\"descend\""