{-# LANGUAGE AllowAmbiguousTypes   #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE TypeOperators         #-}
module Boots.Endpoint.Class(
    registerEndpoint
  ) where

import           Boots
import           Boots.Factory.Web
import qualified Data.HashMap.Strict     as HM
import qualified Data.Swagger            as S
import           Data.Text               (Text)
import           Servant
import           Servant.Server.Internal

data EndpointTag

instance HasServer api ctx
  => HasServer (EndpointTag :> api) ctx where
  type ServerT (EndpointTag :> api) m = ServerT api m
  route _ b = pathRouter "endpoints" . route (Proxy @api) b
  hoistServerWithContext _ = hoistServerWithContext (Proxy @api)

instance HasSwagger api => HasSwagger (EndpointTag :> api) where
  toSwagger _ = toSwagger (Proxy @api) & S.applyTags [S.Tag "endpoints" (Just "Endpoints API") Nothing]

-- | Register endpoint, use this function to create custom endpoints.
registerEndpoint
  :: forall context env api n
  . ( HasSwagger api
    , HasServer api context
    , HasWeb context env
    , MonadIO n
    , MonadMask n)
  => Text -- ^ Endpoint name, used for path, @/endpoints/:name@.
  -> Proxy context -- ^ Context proxy.
  -> Proxy api -- ^ Api proxy.
  -> ServerT api (App (AppEnv env)) -- ^ Api server.
  -> Factory n (WebEnv env context) ()
registerEndpoint name pc _ server = do
  WebEnv{..} <- getEnv
  let ok = enabled endpoint && HM.lookup name (endpoints endpoint) /= Just False
  when ok $ logDebug $ "Endpoint " <> toLogStr name <> " actived."
  tryServeWithSwagger ok pc (Proxy @(EndpointTag :> api)) server