import boto3
from botocore.exceptions import ClientError
import time

# A script to deploy a simple S3 bucket using a CloudFormation template via Boto3.

# --- Configuration ---
REGION = "us-east-1"
STACK_NAME = "MyBoto3S3BucketStack"
BUCKET_NAME_PREFIX = "my-boto3-cf-bucket"

cloudformation_client = boto3.client('cloudformation', region_name=REGION)
sts_client = boto3.client('sts', region_name=REGION)

def get_account_id():
    """Retrieves the AWS account ID."""
    try:
        return sts_client.get_caller_identity()['Account']
    except ClientError as e:
        print(f"Error getting account ID: {e}")
        raise

def create_template_body(account_id):
    """Creates the CloudFormation template body as a string."""
    return f"""
AWSTemplateFormatVersion: '2010-09-09'
Description: A simple S3 bucket CloudFormation template for Boto3 demo.

Resources:
  MyS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: {BUCKET_NAME_PREFIX}-{account_id}-{REGION}
      Tags:
        - Key: Environment
          Value: Development
        - Key: Project
          Value: CloudFormationBoto3Demo
    DeletionPolicy: Delete # Ensures bucket is deleted with stack

Outputs:
  BucketName:
    Description: Name of the S3 bucket
    Value: !Ref MyS3Bucket
"""

def create_cloudformation_stack(template_body):
    """Deploys a CloudFormation stack."""
    print(f"--- Creating CloudFormation Stack: {STACK_NAME} ---")
    try:
        cloudformation_client.create_stack(
            StackName=STACK_NAME,
            TemplateBody=template_body,
            OnFailure='ROLLBACK'
        )
        print("Waiting for stack creation to complete...")
        waiter = cloudformation_client.get_waiter('stack_create_complete')
        waiter.wait(StackName=STACK_NAME)
        print(f"Stack '{STACK_NAME}' created successfully.")
    except ClientError as e:
        if e.response['Error']['Code'] == 'AlreadyExistsException':
            print(f"Stack '{STACK_NAME}' already exists. Skipping creation.")
        else:
            print(f"Error creating stack: {e}")
            raise

def get_stack_output(output_key):
    """Retrieves an output value from the CloudFormation stack."""
    try:
        response = cloudformation_client.describe_stacks(StackName=STACK_NAME)
        outputs = response['Stacks'][0]['Outputs']
        for output in outputs:
            if output['OutputKey'] == output_key:
                return output['OutputValue']
        return None
    except ClientError as e:
        print(f"Error describing stack outputs: {e}")
        raise

def delete_cloudformation_stack():
    """Deletes the CloudFormation stack."""
    print(f"\n--- Deleting CloudFormation Stack: {STACK_NAME} ---")
    try:
        cloudformation_client.delete_stack(StackName=STACK_NAME)
        print("Waiting for stack deletion to complete...")
        waiter = cloudformation_client.get_waiter('stack_delete_complete')
        waiter.wait(StackName=STACK_NAME)
        print(f"Stack '{STACK_NAME}' deleted successfully.")
    except ClientError as e:
        if e.response['Error']['Code'] == 'StackNotFoundException':
            print(f"Stack '{STACK_NAME}' not found, skipping deletion.")
        else:
            print(f"Error deleting stack: {e}")
            raise

def main():
    try:
        account_id = get_account_id()
        template_body = create_template_body(account_id)
        create_cloudformation_stack(template_body)
        
        bucket_name = get_stack_output('BucketName')
        if bucket_name:
            print(f"\n--- S3 Bucket Name: {bucket_name} ---")
        
        print("\n--- CloudFormation S3 bucket deployment complete! ---")
        input("Press Enter to delete the CloudFormation stack...")

    except ClientError as e:
        print(f"An AWS client error occurred: {e}")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
    finally:
        delete_cloudformation_stack()
        print("\n--- CloudFormation S3 bucket demonstration and cleanup complete ---")

if __name__ == "__main__":
    main()
