{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}
{-# LANGUAGE TemplateHaskell   #-}
{-# LANGUAGE LambdaCase        #-}

{-|
Module: AWSLambda.Events.S3Event
Description: Types for S3 Lambda events

Based on https://github.com/aws/aws-lambda-dotnet/tree/master/Libraries/src/Amazon.Lambda.S3Events
-}
module AWSLambda.Events.S3Event where

import           Control.Lens.TH
import           Control.Monad            (guard)
import           Data.Aeson               (FromJSON (..), withObject, (.:))
import           Data.Aeson.Casing        (aesonDrop, camelCase)
import           Data.Aeson.TH            (deriveFromJSON)
import           Data.Text                (Text)
import           Data.Time.Clock          (UTCTime)
import           Network.AWS.S3           (BucketName, ETag, Event(..), ObjectKey, ObjectVersionId)
import qualified Network.AWS.Types        as AWS

import           AWSLambda.Events.Records
import           AWSLambda.Orphans        ()

newtype UserIdentityEntity = UserIdentityEntity
  { _uiePrincipalId :: Text
  } deriving (Eq, Show)

$(deriveFromJSON (aesonDrop 4 camelCase) ''UserIdentityEntity)
$(makeLenses ''UserIdentityEntity)

data S3BucketEntity = S3BucketEntity
  { _sbeArn           :: !Text
  , _sbeName          :: !BucketName
  , _sbeOwnerIdentity :: !UserIdentityEntity
  } deriving (Eq, Show)

$(deriveFromJSON (aesonDrop 4 camelCase) ''S3BucketEntity)
$(makeLenses ''S3BucketEntity)

data S3ObjectEntity = S3ObjectEntity
  { _soeETag      :: !(Maybe ETag)
  , _soeKey       :: !ObjectKey
  , _soeSize      :: !(Maybe Integer)
  , _soeSequencer :: !Text
  , _soeVersionId :: !(Maybe ObjectVersionId)
  } deriving (Eq, Show)

$(deriveFromJSON (aesonDrop 4 camelCase) ''S3ObjectEntity)
$(makeLenses ''S3ObjectEntity)

newtype RequestParametersEntity = RequestParametersEntity
  { _rpeSourceIPAddress :: Text
  } deriving (Eq, Show)

$(deriveFromJSON (aesonDrop 4 camelCase) ''RequestParametersEntity)
$(makeLenses ''RequestParametersEntity)

data ResponseElementsEntity = ResponseElementsEntity
  { _reeXAmzId2       :: !Text
  , _reeXAmzRequestId :: !Text
  } deriving (Eq, Show)

instance FromJSON ResponseElementsEntity where
  parseJSON =
    withObject "ResponseElementsEntity" $
    \o ->
       ResponseElementsEntity <$> o .: "x-amz-id-2" <*> o .: "x-amz-request-id"
$(makeLenses ''ResponseElementsEntity)

data S3Entity = S3Entity
  { _seBucket          :: !S3BucketEntity
  , _seConfigurationId :: !Text
  , _seObject          :: !S3ObjectEntity
  , _seS3SchemaVersion :: !Text
  } deriving (Eq, Show)

$(deriveFromJSON (aesonDrop 3 camelCase) ''S3Entity)
$(makeLenses ''S3Entity)

data S3EventNotification = S3EventNotification
  { _senAwsRegion         :: !AWS.Region
  , _senEventName         :: !Event
  , _senEventSource       :: !Text
  , _senEventTime         :: !UTCTime
  , _senEventVersion      :: !Text
  , _senRequestParameters :: !RequestParametersEntity
  , _senResponseElements  :: !ResponseElementsEntity
  , _senS3                :: !S3Entity
  , _senUserIdentity      :: !UserIdentityEntity
  } deriving (Eq, Show)

instance FromJSON S3EventNotification where
  parseJSON = withObject "S3EventNotification" $ \o -> do
    _senEventSource <- o .: "eventSource"
    guard $ _senEventSource == "aws:s3"
    _senAwsRegion <- o .: "awsRegion"
    _senEventName <- o .: "eventName"
    _senEventTime <- o .: "eventTime"
    _senEventVersion <- o .: "eventVersion"
    _senRequestParameters <- o .: "requestParameters"
    _senResponseElements <- o .: "responseElements"
    _senS3 <- o .: "s3"
    _senUserIdentity <- o .: "userIdentity"
    return S3EventNotification {..}
$(makeLenses ''S3EventNotification)

type S3Event = RecordsEvent S3EventNotification

-- | Is the event an object creation event
isCreateEvent :: S3EventNotification -> Bool
isCreateEvent e = case _senEventName e of
  S3ObjectCreated -> True
  S3ObjectCreatedCompleteMultipartUpload -> True
  S3ObjectCreatedCopy -> True
  S3ObjectCreatedPost -> True
  S3ObjectCreatedPut -> True
  S3ObjectRemoved -> False
  S3ObjectRemovedDelete -> False
  S3ObjectRemovedDeleteMarkerCreated -> False
  S3ReducedRedundancyLostObject -> False

-- | Is the event an object removal event
isRemoveEvent :: S3EventNotification -> Bool
isRemoveEvent e = case _senEventName e of
  S3ObjectCreated -> False
  S3ObjectCreatedCompleteMultipartUpload -> False
  S3ObjectCreatedCopy -> False
  S3ObjectCreatedPost -> False
  S3ObjectCreatedPut -> False
  S3ObjectRemoved -> True
  S3ObjectRemovedDelete -> True
  S3ObjectRemovedDeleteMarkerCreated -> True
  S3ReducedRedundancyLostObject -> False