#!/usr/bin/env python3

import boto3
import argparse
import os

# ==========================================================================
# SCRIPT: s3_upload_file.py
# DESCRIPTION: Uploads a local file to a specified Amazon S3 bucket.
#              This script is useful for automating the transfer of files
#              from a local machine or a server to S3 storage.
#
# USE CASE SCENARIO:
# A data pipeline generates daily reports locally, and these reports need
# to be uploaded to an S3 bucket for archival and further processing.
# This script can be integrated into a cron job or a larger workflow to
# automate the upload process.
#
# PREREQUISITES:
# 1.  **AWS Credentials:** The AWS CLI or SDK must be configured with credentials
#     that have the necessary permissions. This can be done via `~/.aws/credentials`,
#     environment variables, or an instance profile/IAM role for EC2 instances.
# 2.  **boto3 Library:** The AWS SDK for Python (`boto3`) must be installed.
#     You can install it using `pip install boto3`.
# 3.  **IAM Permissions:** The principal executing this script must have:
#     - `s3:PutObject` on the target S3 bucket.
# 4.  **Existing Resources:**
#     - A target S3 bucket where the file will be uploaded.
#     - A local file to be uploaded.
#
# HOW TO USE:
# 1.  **Save the script:** Save this content as `s3_upload_file.py`.
# 2.  **Make it executable (Linux/macOS):** `chmod +x s3_upload_file.py`
# 3.  **Prepare your local file:** Ensure the file you want to upload exists.
# 4.  **Run from your terminal:**
#     python s3_upload_file.py \
#       --bucket-name "my-target-s3-bucket" \
#       --local-file-path "./my_report.csv" \
#       --s3-key "reports/daily/my_report_20231026.csv" \
#       --region "us-east-1"
#
#     **Arguments:**
#     - `--bucket-name`: The name of the S3 bucket to upload the file to.
#     - `--local-file-path`: The path to the local file that will be uploaded.
#     - `--s3-key`: (Optional) The S3 key (full path within the bucket) for the uploaded file.
#                   If not provided, the script uses the local file's basename.
#     - `--region`: (Optional) The AWS region of the S3 bucket. Defaults to `us-east-1`.
#
# IMPORTANT CONSIDERATIONS:
# - For very large files, consider using `s3_client.upload_fileobj` or `s3_client.upload_file`
#   with `TransferConfig` for multipart uploads, which boto3 handles automatically for `upload_file`.
# - Ensure proper error handling and retry mechanisms for production-grade scripts.
# ==========================================================================

def upload_file_to_s3(
    bucket_name: str,
    local_file_path: str,
    s3_key: str = None,
    region_name: str = 'us-east-1'
):
    """
    Uploads a local file to a specified S3 bucket.

    This function takes a local file path and uploads it to an S3 bucket.
    It allows specifying the S3 key (destination path) or defaults to the
    local file's name. It includes basic error handling and checks for file existence.

    Args:
        bucket_name (str): The name of the S3 bucket where the file will be uploaded.
        local_file_path (str): The absolute or relative path to the local file to be uploaded.
        s3_key (str, optional): The S3 key (full path within the bucket) for the uploaded file.
                                If None, the basename of `local_file_path` is used as the S3 key.
        region_name (str): The AWS region where the S3 bucket is located. Defaults to 'us-east-1'.
    """
    # Initialize the boto3 S3 client for programmatic access to AWS S3 service.
    s3_client = boto3.client('s3', region_name=region_name)

    # ==========================================================================
    # STEP 1: Validate the existence of the local file.
    # The script cannot proceed if the source file does not exist.
    # ==========================================================================
    print(f"\n>>> Step 1: Checking for local file: '{local_file_path}'...")
    if not os.path.exists(local_file_path):
        print(f"Error: Local file not found at '{local_file_path}'. Please ensure the file exists. Exiting.")
        return # Exit the function if the file is not found.
    print(f"   Local file '{local_file_path}' found.")

    # ==========================================================================
    # STEP 2: Determine the S3 key (destination path) for the uploaded file.
    # If not provided, use the local file's name.
    # ==========================================================================
    if s3_key is None:
        # Use os.path.basename to extract just the filename from the local_file_path.
        s3_key = os.path.basename(local_file_path)
        print(f"   S3 key not provided. Using local filename as S3 key: '{s3_key}'.")
    else:
        print(f"   Using provided S3 key: '{s3_key}'.")

    print(f"\n>>> Step 3: Uploading '{local_file_path}' to s3://{bucket_name}/{s3_key} in region {region_name}...")

    # ==========================================================================
    # STEP 3: Perform the file upload to S3.
    # The `upload_file` method handles multipart uploads for large files automatically.
    # ==========================================================================
    try:
        s3_client.upload_file(local_file_path, bucket_name, s3_key)
        print(f"Successfully uploaded '{local_file_path}' to s3://{bucket_name}/{s3_key}.")
    except Exception as e:
        # Catch and print any exceptions that occur during the S3 upload.
        print(f"Error uploading file to S3: {e}")
        return # Exit the function on error.

    print("\n=== S3 file upload completed successfully. ===")

# ==========================================================================
# Main execution block to parse command-line arguments and call the function.
# This allows the script to be run from the command line with various options.
# ==========================================================================
if __name__ == "__main__":
    # Create an argument parser object.
    parser = argparse.ArgumentParser(
        description="""
        Uploads a local file to a specified Amazon S3 bucket. This script provides
        a simple way to transfer files to S3, useful for backups, data ingestion,
        or content distribution.
        """
    )
    # Define the command-line arguments the script expects.
    parser.add_argument(
        "--bucket-name", 
        required=True, 
        help="The name of the S3 bucket to upload the file to (e.g., 'my-data-archive')."
    )
    parser.add_argument(
        "--local-file-path", 
        required=True, 
        help="The path to the local file to upload (e.g., './documents/report.pdf')."
    )
    parser.add_argument(
        "--s3-key", 
        help="Optional. The S3 key (full path within the bucket) for the uploaded file.
              If not provided, the local file's basename will be used as the S3 key."
    )
    parser.add_argument(
        "--region", 
        default="us-east-1", 
        help="AWS region of the S3 bucket (default: us-east-1)."
    )

    # Parse the arguments provided by the user when running the script.
    args = parser.parse_args()

    # Call the main function with the parsed arguments.
    upload_file_to_s3(
        bucket_name=args.bucket_name,
        local_file_path=args.local_file_path,
        s3_key=args.s3_key,
        region_name=args.region
    )
