{-|
Module      : AWS.Lambda.Events.S3
Description : Data types for working with S3 events.
Copyright   : (c) Nike, Inc., 2019
License     : BSD3
Maintainer  : nathan.fairhurst@nike.com, fernando.freire@nike.com
Stability   : stable
-}

{-# LANGUAGE DuplicateRecordFields #-}

module AWS.Lambda.Events.S3 (
  PrincipalIdentity(..),
  Records(..),
  RequestParameters(..),
  ResponseElements(..),
  S3Bucket(..),
  S3Config(..),
  S3Event(..),
  S3Object(..)
) where

import Data.Aeson       (FromJSON (..), Value (Object), withObject, (.:), (.:?))
import Data.Aeson.Types (typeMismatch)
import Data.Text        (Text)
import Data.Time.Clock  (UTCTime)
import GHC.Generics     (Generic)

newtype Records = Records {
  records :: [S3Event]
} deriving (Show, Eq)

instance FromJSON Records where
  parseJSON = withObject "Records" $ \v -> Records <$> v .: "Records"

data PrincipalIdentity = PrincipalIdentity {
  principalId :: Text
} deriving (Show, Eq, Generic)

instance FromJSON PrincipalIdentity

data S3Bucket = S3Bucket {
  arn           :: Text,
  name          :: Text,
  ownerIdentity :: PrincipalIdentity
} deriving (Show, Eq, Generic)

instance FromJSON S3Bucket

data S3Config = S3Config {
  bucket          :: S3Bucket,
  configurationId :: Text,
  object          :: S3Object,
  s3SchemaVersion :: Text
} deriving (Show, Eq, Generic)

instance FromJSON S3Config

data ResponseElements = ResponseElements {
  amazonId        :: Text,
  amazonRequestId :: Text
} deriving (Show, Eq)

instance FromJSON ResponseElements where
  parseJSON = withObject  "ResponseElements" $ \v ->
    ResponseElements <$> v .: "x-amz-id-2" <*> v .: "x-amz-request-id"

data RequestParameters = RequestParameters {
  sourceIPAddress :: Text
} deriving (Show, Eq, Generic)

instance FromJSON RequestParameters

-- | Event data sent by S3 when triggering a Lambda.
data S3Event = S3Event {
  awsRegion         :: Text,
  eventName         :: Text,
  eventSource       :: Text,
  eventTime         :: UTCTime,
  eventVersion      :: Text,
  requestParameters :: RequestParameters,
  responseElements  :: ResponseElements,
  s3                :: S3Config,
  userIdentity      :: PrincipalIdentity
} deriving (Show, Eq, Generic)

instance FromJSON S3Event

-- | S3 object representations based on event type received.
--
-- Currently only Put/Delete events can trigger Lambdas
data S3Object =
  PutObject {
    eTag      :: Text,
    sequencer :: Text,
    key       :: Text,
    size      :: Int
  } | DeleteObject {
    sequencer :: Text,
    key       :: Text
  } deriving (Show, Eq, Generic)

instance FromJSON S3Object where
  parseJSON (Object o) = do
    maybeEtag  <- o .:? "eTag"
    maybeSize  <- o .:? "size"
    key'       <- o .:  "key"
    sequencer' <- o .:  "sequencer"

    return $ case (maybeEtag, maybeSize) of
      (Just etag', Just size') -> PutObject etag' sequencer' key' size'
      _                        -> DeleteObject sequencer' key'

  parseJSON invalid    = typeMismatch "S3Object" invalid