{-# OPTIONS_GHC -fno-warn-orphans #-}
module Servant.Auth.Docs
  (
  -- | The purpose of this package is provide the instance for 'servant-auth'
  -- combinators needed for 'servant-docs' documentation generation.
  --
  -- >>> type API = Auth '[JWT, Cookie, BasicAuth] Int :> Get '[JSON] Int
  -- >>> putStr $ markdown $ docs (Proxy :: Proxy API)
  -- ## GET /
  -- ...
  -- #### Authentication
  -- ...
  -- This part of the API is protected by the following authentication mechanisms:
  -- ...
  --  * JSON Web Tokens ([JWTs](https://en.wikipedia.org/wiki/JSON_Web_Token))
  --  * [Cookies](https://en.wikipedia.org/wiki/HTTP_cookie)
  --  * [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
  -- ...
  -- ...
  -- Clients must supply the following data
  -- ...
  -- One of the following:
  -- ...
  --  * A JWT Token signed with this server's key
  --  * Cookies automatically set by browsers, plus a header
  --  * Cookies automatically set by browsers, plus a header
  -- ...

  -- * Re-export
    JWT
  , BasicAuth
  , Cookie
  , Auth
  ) where

import Control.Lens          ((%~), (&), (|>))
import Data.List             (intercalate)
import Data.Monoid
import Data.Proxy            (Proxy (Proxy))
import Servant.API           hiding (BasicAuth)
import Servant.Auth
import Servant.Docs          hiding (pretty)
import Servant.Docs.Internal (DocAuthentication (..), authInfo)

instance (AllDocs auths, HasDocs api) => HasDocs (Auth auths r :> api) where
  docsFor _ (endpoint, action) =
    docsFor (Proxy :: Proxy api) (endpoint, action & authInfo %~ (|> info))
    where
      (intro, reqData) = pretty $ allDocs (Proxy :: Proxy auths)
      info = DocAuthentication intro reqData


pretty :: [(String, String)] -> (String, String)
pretty [] = error "shouldn't happen"
pretty [(i, d)] =
  ( "This part of the API is protected by " <> i
  , d
  )
pretty rs =
  ( "This part of the API is protected by the following authentication mechanisms:\n\n"
  ++  " * " <> intercalate "\n * " (fst <$> rs)
  , "\nOne of the following:\n\n"
  ++  " * " <> intercalate "\n * " (snd <$> rs)
  )


class AllDocs (x :: [*]) where
  allDocs :: proxy x
              -- intro, req
          -> [(String, String)]

instance (OneDoc a, AllDocs as) => AllDocs (a ': as) where
  allDocs _ = oneDoc (Proxy :: Proxy a) : allDocs (Proxy :: Proxy as)

instance AllDocs '[] where
  allDocs _ = []

class OneDoc a where
  oneDoc :: proxy a -> (String, String)

instance OneDoc JWT where
  oneDoc _ =
    ("JSON Web Tokens ([JWTs](https://en.wikipedia.org/wiki/JSON_Web_Token))"
     , "A JWT Token signed with this server's key")

instance OneDoc Cookie where
  oneDoc _ =
    ("[Cookies](https://en.wikipedia.org/wiki/HTTP_cookie)"
    , "Cookies automatically set by browsers, plus a header")

instance OneDoc BasicAuth where
  oneDoc _ =
    ( "[Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)"
    , "Cookies automatically set by browsers, plus a header")

-- $setup
-- >>> instance ToSample Int where toSamples _ = singleSample 1729