{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, TypeApplications #-}

-- |
-- Example usage:
--
-- > {-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}
-- >
-- > import Network.AWS.CloudFront.SignedCookies
-- >
-- > import qualified Data.Text.IO
-- >
-- > main :: IO ()
-- > main = do
-- >
-- >   -- Construct an IAM policy that expires three days from now
-- >   policy :: Policy <- simplePolicy
-- >     (Resource "https://example.com/secrets/*")
-- >     (Lifespan (3 * nominalDay))
-- >
-- >   -- Parse the .pem file to get the private key
-- >   key :: PrivateKey <- readPrivateKeyPemFile
-- >     (PemFilePath "./pk-APKAIATXN3RCIOVT5WRQ.pem")
-- >
-- >   -- Construct signed cookies
-- >   cookies :: CookiesText <- createSignedCookies
-- >     (KeyPairId "APKAIATXN3RCIOVT5WRQ") key policy
-- >
-- >   Data.Text.IO.putStrLn (renderCookiesText cookies)
--
-- Output:
--
-- > Cookie: CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc29...
-- > Cookie: CloudFront-Signature=wMN6V3Okxk7sdSPZeebMh-wo...
-- > Cookie: CloudFront-Key-Pair-Id=APKAIATXN3RCIOVT5WRQ
--
-- You can see a very similar example in action in the
-- "Network.AWS.CloudFront.SignedCookies.CLI" module.

module Network.AWS.CloudFront.SignedCookies

  (
  -- * Creating signed cookies
    createSignedCookies

  -- ** Defining a CloudFront policy
  , simplePolicy
  , Policy (..)
  , Resource (..)
  , Lifespan (..)
  , StartTime (..)
  , EndTime (..)
  , IpAddress (..)

  -- ** Getting your private key
  , readPrivateKeyPemFile
  , PemFilePath (..)
  , KeyPairId (..)
  , PrivateKey

  -- * Miscellaneous

  -- ** Cookies
  , CookiesText
  , renderCookiesText

  -- ** Time
  , NominalDiffTime
  , POSIXTime
  , nominalDay
  , getPOSIXTime

  -- ** Text
  , Text

  ) where

import Network.AWS.CloudFront.SignedCookies.Crypto
import Network.AWS.CloudFront.SignedCookies.Encoding
import Network.AWS.CloudFront.SignedCookies.Policy
import Network.AWS.CloudFront.SignedCookies.Types

-- base
import Data.Coerce (coerce)
import Data.Semigroup ((<>))

-- text
import qualified Data.Text as Text

-- time
import Data.Time.Clock.POSIX (getPOSIXTime)
import Data.Time.Clock (nominalDay)

createSignedCookies
  :: KeyPairId
    -- ^ A CloudFront key pair ID, which must be associated with a
    --   trusted signer in the CloudFront distribution that you
    --   specify in the 'policyResource'.
  -> PrivateKey
    -- ^ The private key associated with the 'KeyPairId'. See
    --   'readPrivateKeyPemFile' for how to read this key from a
    --   @.pem@ file you downloaded from AWS.
  -> Policy
    -- ^ The policy specifies what resource is being granted, for what
    --   time period, and to what IP addresses. Construct a policy
    --   using the 'Policy' constructor or with the 'simplePolicy'
    --   function.
  -> IO CookiesText

createSignedCookies kpid key policy = do

  let
    policyBS :: ByteString = policyJSON policy

  sigBS <- sign key policyBS

  pure
    [ ( "CloudFront-Policy"      , base64Encode policyBS        )
    , ( "CloudFront-Signature"   , base64Encode sigBS           )
    , ( "CloudFront-Key-Pair-Id" , coerce @KeyPairId @Text kpid )
    ]

-- | Format a list of cookies as HTTP request headers.
renderCookiesText :: CookiesText -> Text
renderCookiesText =
  Text.unlines . map (\(k, v) -> "Cookie: " <> k <> "=" <> v)