{-# LANGUAGE ConstraintKinds   #-}
{-# LANGUAGE OverloadedStrings #-}

-- | Some helper functions for creating values in
-- [lucid](https://hackage.haskell.org/package/lucid) DSLs that work
-- with [servant](https://hackage.haskell.org/package/servant).
module Lucid.Servant (
    safeHref_,
    safeAbsHref_,
    safeRelHref_,
    linkHref_,
    linkAbsHref_,
    linkRelHref_,
    ) where

import           Data.Proxy
                 (Proxy)
import           Data.Semigroup
                 ((<>))
import qualified Data.Text           as T
import           Lucid
                 (Attribute)
import           Lucid.Html5
                 (href_)
import           Servant.API
                 (toUrlPiece)
import           Servant.Utils.Links
                 (HasLink, IsElem, Link, MkLink, safeLink')

-- | 'safeLink' variant which creates lucid's 'Attribute' given base url.
--
-- >>> type API = "path" :> Get '[JSON] Int
-- >>> let api = Proxy :: Proxy API
--
-- >>> safeHref_ "" api api
-- Attribute "href" "path"
--
-- >>> safeHref_ "/" api api
-- Attribute "href" "/path"
--
-- >>> safeHref_ "http://example.com" api api
-- Attribute "href" "http://example.com/path"
--
-- >>> safeHref_ "http://example.com/" api api
-- Attribute "href" "http://example.com/path"
--
safeHref_
    :: (IsElem endpoint api, HasLink endpoint)
    => T.Text
    -> Proxy api -> Proxy endpoint -> MkLink endpoint Attribute
safeHref_ = safeLink' . linkHref_

-- | @'safeLink' "/"@
safeAbsHref_
    :: (IsElem endpoint api, HasLink endpoint)
    => Proxy api -> Proxy endpoint -> MkLink endpoint Attribute
safeAbsHref_ = safeLink' linkAbsHref_

-- | @'safeLink' ""@
safeRelHref_
    :: (IsElem endpoint api, HasLink endpoint)
    => Proxy api -> Proxy endpoint -> MkLink endpoint Attribute
safeRelHref_ = safeLink' linkRelHref_

-- | Create an `href` attribute from a 'Link', with given base url.
--
-- "servant" ensures that any 'Link' is valid within an API.
-- This function ensures it is possible to navigate to that endpoint from
-- a page which shares a root with that API.
linkHref_ :: T.Text -> Link -> Attribute
linkHref_ burl = href_ . (burl <+>) . toUrlPiece

-- | Create an `href` attribute from a 'Link', with leading '/'.
--
-- "servant" ensures that any 'Link' is valid within an API.
-- This function ensures it is possible to navigate to that endpoint from
-- a page which shares a root with that API.
linkAbsHref_ :: Link -> Attribute
linkAbsHref_ = linkHref_ "/"

-- | Create an `href` attribute from a 'Link', as a relative link.
--
-- "servant" ensures that any 'Link' is valid within an API.
-- Use this function if a relative link (no leading '/') is required.
linkRelHref_ :: Link -> Attribute
linkRelHref_ = href_ . toUrlPiece

(<+>) :: T.Text -> T.Text -> T.Text
burl <+> path
    | T.null burl        = path
    | T.last burl == '/' = burl <> path
    | otherwise          = burl <> "/" <> path

-- $setup
--
-- >>> :set -XOverloadedStrings -XDataKinds -XTypeOperators
-- >>> import Servant.API
-- >>> import Data.Proxy (Proxy (..))
--