Parameters:
  CoreStack:
    Description: The foundational stack
    Type: String
  Hl7ParsingLibKey:
    Description: Key for our HL7 parsing library
    Type: String
  TriggerLambdaKey:
    Type: String
  TriggerLambdaVersion:
    Type: String
  TriggerHandler:
    Type: String
  PrepareLambdaKey:
    Type: String
  PrepareLambdaVersion:
    Type: String
  PrepareHandler:
    Type: String
  ParseLambdaKey:
    Type: String
  ParseLambdaVersion:
    Type: String
  ParseHandler:
    Type: String

Resources:
  #-------------------------------------------------------------- Lambda to connect SNS and Step Function
  TriggerLambdaFunction:
    Type: AWS::Lambda::Function
    Properties: 
      FunctionName: !Sub "${AWS::StackName}_trigger"
      Description: Function to connect SNS to Step Function
      Code:
        S3Bucket:
          Fn::ImportValue: !Sub "${CoreStack}-ArtifactBucket"
        S3Key: !Ref TriggerLambdaKey
        S3ObjectVersion: !Ref TriggerLambdaVersion
      Handler: !Ref TriggerHandler
      Role: !GetAtt TriggerLambdaRole.Arn
      Runtime: python3.9
      Environment:
        Variables:
          bucket_name: 
            Fn::ImportValue: !Sub "${CoreStack}-Bucket"
          topic: 
            Fn::ImportValue: !Sub "${CoreStack}-Topic"
          state_machine: !Ref StateMachine
      
  TriggerLambdaLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Join ['/', ['/aws/lambda', !Ref TriggerLambdaFunction]]
      RetentionInDays: 1 # Keep logs for a short duration

  TriggerLambdaRole:
    Type: AWS::IAM::Role
    Properties: 
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal:
            Service: [lambda.amazonaws.com]
          Action: ['sts:AssumeRole']
      ManagedPolicyArns: 
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole # Provides access to CloudWatch for logging
      Policies:
      - PolicyName: sns
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: ['sns:publish']
            Resource:
            - Fn::ImportValue: !Sub "${CoreStack}-Topic"      
      - PolicyName: step_function
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: ['states:StartSyncExecution']
            Resource: !Ref StateMachine

  # Permission for SNS topic to invoke this Lambda
  TriggerLambdaResourcePolicy:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref TriggerLambdaFunction
      Principal: sns.amazonaws.com
      Action: "lambda:InvokeFunction"
      SourceArn:
        Fn::ImportValue: !Sub "${CoreStack}-Topic"

  # Subscription to SNS topic
  Subscription:
    Type: AWS::SNS::Subscription
    Properties:
      TopicArn:
        Fn::ImportValue: !Sub "${CoreStack}-Topic"
      Endpoint: !GetAtt TriggerLambdaFunction.Arn
      FilterPolicy:
        protocol: [hl7v2]
        format: [er7]
      Protocol: lambda
  
  #-------------------------------------------------------------- Lambda Layer
  Hl7apyLayer:
    Type: AWS::Lambda::LayerVersion
    Properties: 
      CompatibleRuntimes: [python2.7, python3.6, python3.7, python3.8, python3.9]
      Content: 
        S3Bucket:
          Fn::ImportValue: !Sub "${CoreStack}-ArtifactBucket"
        S3Key: !Ref Hl7ParsingLibKey
      LayerName: hl7apy
      Description: "HL7apy parser library"
      LicenseInfo: MIT

  #-------------------------------------------------------------- Preparing Lambda
  PrepareLambdaFunction:
    Type: AWS::Lambda::Function
    Properties: 
      FunctionName: !Sub ${AWS::StackName}_prepare
      Description: Prepares our ER7 message by applying common syntax corrections
      Code:
        S3Bucket:
          Fn::ImportValue: !Sub "${CoreStack}-ArtifactBucket"
        S3Key: !Ref PrepareLambdaKey
        S3ObjectVersion: !Ref PrepareLambdaVersion
      Handler: !Ref PrepareHandler
      Role: !GetAtt PrepareLambdaRole.Arn
      Runtime: python3.9

  PrepareLambdaLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Join ['/', ['/aws/lambda', !Ref PrepareLambdaFunction]]
      RetentionInDays: 1 # Keep logs for a short duration

  PrepareLambdaRole:
    Type: AWS::IAM::Role
    Properties: 
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal:
            Service: [lambda.amazonaws.com]
          Action: ['sts:AssumeRole']
      ManagedPolicyArns: 
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole # Provides access to CloudWatch for logging

  #-------------------------------------------------------------- Parsing Lambda
  ParseLambdaFunction:
    Type: AWS::Lambda::Function
    Properties: 
      FunctionName: !Sub ${AWS::StackName}_parse
      Description: Parse an ER7 message to JSON
      Layers: [!Ref Hl7apyLayer]
      Code:
        S3Bucket:
          Fn::ImportValue: !Sub "${CoreStack}-ArtifactBucket"
        S3Key: !Ref ParseLambdaKey
        S3ObjectVersion: !Ref ParseLambdaVersion
      Handler: !Ref ParseHandler
      Role: !GetAtt ParseLambdaRole.Arn 
      Runtime: python3.9

  ParseLambdaLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Join ['/', ['/aws/lambda', !Ref ParseLambdaFunction]]
      RetentionInDays: 1 # Keep logs for a short duration

  ParseLambdaRole:
    Type: AWS::IAM::Role
    Properties: 
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal:
            Service: [lambda.amazonaws.com]
          Action: ['sts:AssumeRole']
      ManagedPolicyArns: 
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole # Provides access to CloudWatch for logging

  #-------------------------------------------------------------- Step Function
  StateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      StateMachineName: !Sub ${AWS::StackName}_step_function
      StateMachineType: EXPRESS
      RoleArn: !GetAtt StateMachineRole.Arn
      TracingConfiguration:
        Enabled: true
      Definition:
        StartAt: PrepareEr7
        States:
          PrepareEr7:
            Type: Task
            Resource: !GetAtt PrepareLambdaFunction.Arn
            InputPath: $.Message # What to pass to the Lambda
            ResultPath: $.er7 # Where to append Lambda results
            Next: ParseEr7
          ParseEr7:  
            Type: Task
            Resource: !GetAtt ParseLambdaFunction.Arn
            InputPath: $.er7
            ResultPath: $.json
            OutputPath: $.json # Filter the output
            End: true
      LoggingConfiguration:
        Destinations:
        - CloudWatchLogsLogGroup:
            LogGroupArn: !GetAtt StateMachineLogGroup.Arn
        IncludeExecutionData: True
        Level: ALL
            
  StateMachineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Action: ['sts:AssumeRole']
          Effect: Allow
          Principal:
            Service: [states.amazonaws.com]
      ManagedPolicyArns: []
      Policies:
      - PolicyName: StateMachineRolePolicy
        PolicyDocument:
          Statement:
          - Action: ['lambda:InvokeFunction', 'logs:*']
            Resource: "*"
            Effect: Allow

  StateMachineLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Join ['/', ['/aws/stepfunction', !Sub "${AWS::StackName}_step_function"]] # Cannot use !Ref as that would create a circular dependency
      RetentionInDays: 14

Outputs:
  StateMachine:
    Value: !Ref StateMachine
    Export: 
      Name: !Sub ${AWS::StackName}-StateMachine