AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: {{ description }}

Resources:
  # API Gateway
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      StageName: {{ backend_configuration.stage }}
      {% if backend_configuration.cors is equals(true) %}
      Cors:
        AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"
        AllowHeaders: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
        AllowOrigin: "'*'"
      {% endif %}

  # Lambda Function with Web Adapter
  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: {{ backend_configuration.built_artifacts_path }}
      Handler: {{ backend_configuration.startup_script }}
      Runtime: {{ backend_configuration.runtime }}
      MemorySize: {{ backend_configuration.memory_size }}
      Timeout: {{ backend_configuration.timeout }}
      Architectures:
        - {{ backend_configuration.architecture }}
      Environment:
        Variables:
          PORT: {{ backend_configuration.port }}
          AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap
          {% if backend_configuration.environment %}
          {% for key, value in backend_configuration.environment.items() %}
          {{ key }}: {{ value }}
          {% endfor %}
          {% endif %}
          {% if backend_configuration.database_configuration is exists %}
          TABLE_NAME: {{ backend_configuration.database_configuration.table_name }}
          {% endif %}
      {% if backend_configuration.database_configuration is exists %}
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref ApiDatabaseTable
      {% endif %}
      Layers:
        {% if backend_configuration.architecture is equals("arm64") %}
        - !Sub "arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerArm64:25"
        {% else %}
        - !Sub "arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:25"
        {% endif %}
      Events:
        ApiEvent:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /api/{proxy+}
            Method: ANY
        RootApiEvent:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /api
            Method: ANY

  {% if backend_configuration.database_configuration is exists %}
  # DynamoDB Table
  ApiDatabaseTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: {{ backend_configuration.database_configuration.table_name }}
      BillingMode: {% if backend_configuration.database_configuration.billing_mode is equals("PROVISIONED") %}PROVISIONED{% else %}PAY_PER_REQUEST{% endif %}
      AttributeDefinitions:
        {% for attr in backend_configuration.database_configuration.attribute_definitions %}
        - AttributeName: {{ attr.name }}
          AttributeType: {{ attr.type }}
        {% endfor %}
      KeySchema:
        {% for key in backend_configuration.database_configuration.key_schema %}
        - AttributeName: {{ key.name }}
          KeyType: {{ key.type }}
        {% endfor %}
      {% if backend_configuration.database_configuration.billing_mode is equals("PROVISIONED") %}
      ProvisionedThroughput:
        ReadCapacityUnits: {{ backend_configuration.database_configuration.read_capacity }}
        WriteCapacityUnits: {{ backend_configuration.database_configuration.write_capacity }}
      {% endif %}
  {% endif %}

  # S3 bucket for website content
  WebsiteBucket:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: Private
      CorsConfiguration:
        CorsRules:
          - AllowedHeaders:
              - '*'
            AllowedMethods:
              - GET
              - HEAD
            AllowedOrigins:
              - '*'
            MaxAge: 3000

  # CloudFront Origin Access Control
  CloudFrontOriginAccessControl:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Name: !Sub "${AWS::StackName}-oac"
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4

  # Bucket policy for CloudFront access
  WebsiteBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref WebsiteBucket
      PolicyDocument:
        Statement:
          - Action: s3:GetObject
            Effect: Allow
            Resource: !Sub "arn:aws:s3:::${WebsiteBucket}/*"
            Principal:
              Service: cloudfront.amazonaws.com
            Condition:
              StringEquals:
                AWS:SourceArn: !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${WebsiteDistribution}"

  # CloudFront distribution
  WebsiteDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        DefaultRootObject: {% if frontend_configuration.index_document is exists %}{{ frontend_configuration.index_document }}{% else %}index.html{% endif %}
        Origins:
          - DomainName: !GetAtt WebsiteBucket.RegionalDomainName
            Id: S3Origin
            OriginAccessControlId: !GetAtt CloudFrontOriginAccessControl.Id
            S3OriginConfig:
              OriginAccessIdentity: ""
          - DomainName: !Sub "${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com"
            Id: ApiOrigin
            OriginPath: "/{{ backend_configuration.stage }}"
            CustomOriginConfig:
              OriginProtocolPolicy: https-only
              OriginSSLProtocols: [TLSv1.2]
        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods: [GET, HEAD, OPTIONS]
          CachedMethods: [GET, HEAD]
          ForwardedValues:
            QueryString: false
            Cookies:
              Forward: none
          # SPA routing support
          FunctionAssociations:
            - EventType: viewer-request
              FunctionARN: !GetAtt RouterFunction.FunctionARN
        CacheBehaviors:
          - PathPattern: "/api/*"
            TargetOriginId: ApiOrigin
            ViewerProtocolPolicy: https-only
            AllowedMethods: [GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE]
            CachedMethods: [GET, HEAD]
            ForwardedValues:
              QueryString: true
              Headers: ["Authorization"]
              Cookies:
                Forward: all
            # Disable caching for API requests
            DefaultTTL: 0
            MinTTL: 0
            MaxTTL: 0
        PriceClass: PriceClass_100
        CustomErrorResponses:
          - ErrorCode: 404
            ResponseCode: 200
            ResponsePagePath: /{% if frontend_configuration.index_document is exists %}{{ frontend_configuration.index_document }}{% else %}index.html{% endif %}
          - ErrorCode: 403
            ResponseCode: 200
            ResponsePagePath: /{% if frontend_configuration.index_document is exists %}{{ frontend_configuration.index_document }}{% else %}index.html{% endif %}
        {% if frontend_configuration.custom_domain is exists %}
        Aliases:
          - {{ frontend_configuration.custom_domain }}
        ViewerCertificate:
          AcmCertificateArn: {{ frontend_configuration.certificate_arn }}
          SslSupportMethod: sni-only
          MinimumProtocolVersion: TLSv1.2_2021
        {% else %}
        ViewerCertificate:
          CloudFrontDefaultCertificate: true
        {% endif %}

  # CloudFront Function for routing
  RouterFunction:
    Type: AWS::CloudFront::Function
    Properties:
      Name: !Sub "${AWS::StackName}-router-function"
      AutoPublish: true
      FunctionCode: |
        function handler(event) {
          var request = event.request;
          var uri = request.uri;

          // Don't rewrite API requests
          if (uri.startsWith('/api/')) {
            return request;
          }

          // Standard SPA routing
          if (uri.includes('.')) {
            return request;
          }

          // Rewrite to index.html for SPA routing
          request.uri = '/{% if frontend_configuration.index_document is exists %}{{ frontend_configuration.index_document }}{% else %}index.html{% endif %}';

          return request;
        }
      FunctionConfig:
        Comment: "Rewrite requests for frontend routing"
        Runtime: cloudfront-js-1.0

Outputs:
  ApiEndpoint:
    Description: API Gateway endpoint URL
    Value: !Sub "https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/{{ backend_configuration.stage }}/"

  FunctionArn:
    Description: Lambda function ARN
    Value: !GetAtt ApiFunction.Arn

  FunctionName:
    Description: Lambda function name
    Value: !Ref ApiFunction

  {% if backend_configuration.database_configuration is exists %}
  TableName:
    Description: Name of the DynamoDB table
    Value: !Ref ApiDatabaseTable
  {% endif %}

  WebsiteBucket:
    Description: S3 bucket for website content
    Value: !Ref WebsiteBucket

  CloudFrontURL:
    Description: CloudFront distribution URL
    Value: !Sub "https://${WebsiteDistribution.DomainName}"

  CloudFrontDistributionId:
    Description: CloudFront distribution ID
    Value: !Ref WebsiteDistribution

  {% if frontend_configuration.custom_domain is exists %}
  CustomDomainURL:
    Description: Custom domain URL
    Value: !Sub "https://{{ frontend_configuration.custom_domain }}"
  {% endif %}
