AWSTemplateFormatVersion: '2010-09-09'
Description: 'Serverless API with Lambda, API Gateway, and DynamoDB - Best Practices'

Resources:
  ApiTable:
    Type: AWS::DynamoDB::Table
    Properties:
      BillingMode: PAY_PER_REQUEST
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      SSESpecification:
        SSEEnabled: true
      StreamSpecification:
        StreamViewType: NEW_AND_OLD_IMAGES
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH

  ApiFunction:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.13
      Handler: index.handler
      Architectures:
        - arm64
      Environment:
        Variables:
          TABLE_NAME: !Ref ApiTable
      Code:
        ZipFile: |
          import json
          import os
          import boto3
          from datetime import datetime
          
          dynamodb = boto3.resource('dynamodb')
          table = dynamodb.Table(os.environ['TABLE_NAME'])
          
          def handler(event, context):
              try:
                  method = event['httpMethod']
                  
                  if method == 'GET':
                      response = table.scan()
                      return {
                          'statusCode': 200,
                          'headers': {'Content-Type': 'application/json'},
                          'body': json.dumps(response['Items'])
                      }
                  
                  elif method == 'POST':
                      body = json.loads(event['body'])
                      item = {
                          'id': body['id'],
                          'data': body.get('data', ''),
                          'timestamp': datetime.utcnow().isoformat()
                      }
                      table.put_item(Item=item)
                      return {
                          'statusCode': 201,
                          'headers': {'Content-Type': 'application/json'},
                          'body': json.dumps(item)
                      }
                  
                  return {
                      'statusCode': 405,
                      'body': json.dumps({'error': 'Method not allowed'})
                  }
              
              except Exception as e:
                  return {
                      'statusCode': 500,
                      'body': json.dumps({'error': str(e)})
                  }
      Role: !GetAtt ApiFunctionRole.Arn
      ReservedConcurrentExecutions: 10
      LoggingConfig:
        LogGroup: !Ref ApiFunctionLogGroup

  ApiFunctionRole:
    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
      Policies:
        - PolicyName: DynamoDBAccess
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - dynamodb:GetItem
                  - dynamodb:PutItem
                  - dynamodb:Scan
                  - dynamodb:Query
                Resource: !GetAtt ApiTable.Arn

  ApiFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays: 7

  RestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: !Sub ${AWS::StackName}-api
      EndpointConfiguration:
        Types:
          - REGIONAL

  ApiResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref RestApi
      ParentId: !GetAtt RestApi.RootResourceId
      PathPart: items

  ApiMethodGet:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref RestApi
      ResourceId: !Ref ApiResource
      HttpMethod: GET
      AuthorizationType: NONE
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations

  ApiMethodPost:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref RestApi
      ResourceId: !Ref ApiResource
      HttpMethod: POST
      AuthorizationType: NONE
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations

  ApiDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn:
      - ApiMethodGet
      - ApiMethodPost
    Properties:
      RestApiId: !Ref RestApi

  ApiStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      RestApiId: !Ref RestApi
      DeploymentId: !Ref ApiDeployment
      StageName: prod
      TracingEnabled: true
      MethodSettings:
        - ResourcePath: /*
          HttpMethod: '*'
          LoggingLevel: INFO
          DataTraceEnabled: true
          MetricsEnabled: true

  ApiGatewayAccount:
    Type: AWS::ApiGateway::Account
    Properties:
      CloudWatchRoleArn: !GetAtt ApiGatewayCloudWatchRole.Arn

  ApiGatewayCloudWatchRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: apigateway.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs

  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref ApiFunction
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*/*

Outputs:
  ApiUrl:
    Value: !Sub https://${RestApi}.execute-api.${AWS::Region}.amazonaws.com/prod/items
    Description: API endpoint URL
  TableName:
    Value: !Ref ApiTable
    Description: DynamoDB table name
  FunctionName:
    Value: !Ref ApiFunction
    Description: Lambda function name
