content: {-|
  Module      : Web.Scotty.Cookie
  Copyright   : (c) 2014, 2015 Mārtiņš Mačs,
                (c) 2023 Marco Zocca
  
  License     : BSD-3-Clause
  Maintainer  :
  Stability   : experimental
  Portability : GHC
  
  This module provides utilities for adding cookie support inside @scotty@ applications. Most code has been adapted from 'scotty-cookie'.
  
  == Example
  
  A simple hit counter that stores the number of page visits in a cookie:
  
  @
  \{\-\# LANGUAGE OverloadedStrings \#\-\}
  
  import Control.Monad
  import Data.Monoid
  import Data.Maybe
  import qualified Data.Text.Lazy as TL
  import qualified Data.Text.Lazy.Read as TL (decimal)
  import Web.Scotty (scotty, html, get)
  import Web.Scotty.Cookie (getCookie, setSimpleCookie)
  
  main :: IO ()
  main = scotty 3000 $
      get \"/\" $ do
          hits <- liftM (fromMaybe \"0\") $ 'getCookie' \"hits\"
          let hits' =
                case TL.decimal $ TL.fromStrict hits of
                  Right n -> TL.pack . show . (+1) $ (fst n :: Integer)
                  Left _  -> \"1\"
          'setSimpleCookie' \"hits\" $ TL.toStrict hits'
          html $ mconcat [ \"\<html\>\<body\>\"
                         , hits'
                         , \"\<\/body\>\<\/html\>\"
                         ]
  @
  -}
  {-# LANGUAGE OverloadedStrings #-}
  module Web.Scotty.Cookie (
      -- * Set cookie
        setCookie
      , setSimpleCookie
      -- * Get cookie(s)
      , getCookie
      , getCookies
      -- * Delete a cookie
      , deleteCookie
      -- * Helpers and advanced interface (re-exported from 'cookie')
      , CookiesText
      , makeSimpleCookie
      -- ** cookie configuration
      , SetCookie
      , defaultSetCookie
      , setCookieName
      , setCookieValue
      , setCookiePath
      , setCookieExpires
      , setCookieMaxAge
      , setCookieDomain
      , setCookieHttpOnly
      , setCookieSecure
      , setCookieSameSite
      , SameSiteOption
      , sameSiteNone
      , sameSiteLax
      , sameSiteStrict
      ) where
  
  import           Control.Monad.IO.Class (MonadIO(..))
  
  -- bytestring
  import Data.ByteString.Builder (toLazyByteString)
  import qualified Data.ByteString.Lazy as BSL (toStrict)
  -- cookie
  import Web.Cookie (SetCookie, setCookieName , setCookieValue, setCookiePath, setCookieExpires, setCookieMaxAge, setCookieDomain, setCookieHttpOnly, setCookieSecure, setCookieSameSite, renderSetCookie, defaultSetCookie, CookiesText, parseCookiesText, SameSiteOption, sameSiteStrict, sameSiteNone, sameSiteLax)
  -- scotty
  import Web.Scotty.Action (ActionT, addHeader, header)
  -- time
  import Data.Time.Clock.POSIX ( posixSecondsToUTCTime )
  -- text
  import Data.Text (Text)
  import qualified Data.Text.Encoding as T (encodeUtf8)
  import Web.Scotty.Util (decodeUtf8Lenient)
  
  -- | Set a cookie, with full access to its options (see 'SetCookie')
  setCookie :: (MonadIO m)
            => SetCookie
            -> ActionT m ()
  setCookie c = addHeader "Set-Cookie"
    $ decodeUtf8Lenient
    $ BSL.toStrict
    $ toLazyByteString
    $ renderSetCookie c
  
  
  -- | 'makeSimpleCookie' and 'setCookie' combined.
  setSimpleCookie :: (MonadIO m)
                  => Text -- ^ name
                  -> Text -- ^ value
                  -> ActionT m ()
  setSimpleCookie n v = setCookie $ makeSimpleCookie n v
  
  -- | Lookup one cookie name
  getCookie :: (Monad m)
            => Text -- ^ name
            -> ActionT m (Maybe Text)
  getCookie c = lookup c <$> getCookies
  
  
  -- | Returns all cookies
  getCookies :: (Monad m)
             => ActionT m CookiesText
  getCookies = (maybe [] parse) <$> header "Cookie"
      where parse = parseCookiesText . T.encodeUtf8
  
  -- | Browsers don't directly delete a cookie, but setting its expiry to a past date (e.g. the UNIX epoch) ensures that the cookie will be invalidated (whether and when it will be actually deleted by the browser seems to be browser-dependent).
  deleteCookie :: (MonadIO m)
               => Text -- ^ name
               -> ActionT m ()
  deleteCookie c = setCookie $ (makeSimpleCookie c "") { setCookieExpires = Just $ posixSecondsToUTCTime 0 }
  
  
  -- | Construct a simple cookie (an UTF-8 string pair with default cookie options)
  makeSimpleCookie :: Text -- ^ name
                   -> Text -- ^ value
                   -> SetCookie
  makeSimpleCookie n v = defaultSetCookie { setCookieName  = T.encodeUtf8 n
                                          , setCookieValue = T.encodeUtf8 v
                                          }
  
  