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

module Network.PinPon.SwaggerAPI
  ( -- * Types
    API

    -- * Servant / WAI functions
  , app
  , api
  , server

    -- * Swagger
  , pinPonSwagger

     -- * Convenience functions
  , writeSwaggerJSON
  )
  where

import Protolude
import Control.Lens ((&), (.~), (?~))
import Data.Aeson.Encode.Pretty (encodePretty)
import qualified Data.ByteString.Lazy.Char8 as C8
import Data.Swagger
       (Swagger, URL(..), description, info, license, title, url, version)
import Network.Wai (Application)
import Servant ((:<|>)(..), Proxy(..), Server, serve)
import Servant.Swagger (toSwagger)
import Servant.Swagger.UI (SwaggerSchemaUI, swaggerSchemaUIServer)

import qualified Network.PinPon.API as PinPon (API, api, server)
import Network.PinPon.Config (Config(..))

-- | Combine all of the various individual service APIs (plus Swagger
-- support) into a single API type.
type API =
  PinPon.API :<|>
  SwaggerSchemaUI "swagger-ui" "swagger.json"

api :: Proxy API
api = Proxy

-- | A Servant 'Server' which serves the 'API' (including the Swagger
-- schema) on the given 'Config'.
--
-- Normally you will just use 'app', but this function is exported so
-- that you can extend/wrap 'API'.
server :: Config -> Server API
server config = PinPon.server config :<|> swaggerSchemaUIServer pinPonSwagger

-- | A WAI 'Network.Wai.Application' which runs the service, using the
-- given 'Config'.
app :: Config -> Application
app = serve api . server

pinPonSwagger :: Swagger
pinPonSwagger = toSwagger PinPon.api
  & info.title .~ "PinPon API"
  & info.version .~ "0.1"
  & info.description ?~ "A simple Internet-enabled doorbell notificaion service"
  & info.license ?~ ("BSD3" & url ?~ URL "https://opensource.org/licenses/BSD-3-Clause")

writeSwaggerJSON :: IO ()
writeSwaggerJSON = C8.writeFile "swagger.json" (encodePretty pinPonSwagger)