"""
AWS S3 storage provider implementation.
Handles low-level operations with AWS S3 using boto3.
"""

import os
from pathlib import Path
from typing import Optional, List

import boto3
from botocore.exceptions import ClientError


class S3Provider:
    """
    Provider class for interacting with AWS S3.

    Attributes:
        bucket (str): The name of the S3 bucket.
        s3 (boto3.client): The initialized boto3 S3 client.
    """

    def __init__(self, bucket: str, region: Optional[str] = "ap-northeast-1", profile: Optional[str] = None):
        """
        Initializes the S3 client.

        Args:
            bucket (str): The name of the S3 bucket to use.
            region (str, optional): The AWS region. Defaults to "ap-northeast-1".
            profile (str, optional): The AWS profile name to use.
        """
        self.bucket = bucket
        # Use session to support profile-based credentials
        session = boto3.Session(profile_name=profile)
        self.s3 = session.client("s3", region_name=region)

    def list_files(self, prefix: str) -> List[str]:
        """
        Lists all files in the S3 bucket under a specific prefix.
        
        Args:
            prefix (str): The prefix (folder path) to search in.
            
        Returns:
            List[str]: A list of keys (full paths) found.
        """
        try:
            paginator = self.s3.get_paginator("list_objects_v2")
            pages = paginator.paginate(Bucket=self.bucket, Prefix=prefix)
            
            keys = []
            for page in pages:
                if "Contents" in page:
                    for obj in page["Contents"]:
                        keys.append(obj["Key"])
            return keys
        except ClientError:
            return []

    def upload(self, local_path: str, remote_key: str) -> bool:
        """
        Uploads a local file to a specific key in S3.

        Args:
            local_path (str): The path to the file on the local filesystem.
            remote_key (str): The key (path) where the file will be stored in S3.

        Returns:
            bool: True if the upload was successful, False otherwise.
        """
        try:
            self.s3.upload_file(local_path, self.bucket, remote_key)
            return True
        except ClientError:
            return False

    def download(self, remote_key: str, local_path: str) -> bool:
        """
        Downloads a file from S3 to the local filesystem.
        Automatically creates local directories if they don't exist.

        Args:
            remote_key (str): The key of the file to download from S3.
            local_path (str): The local path where the file should be saved.

        Returns:
            bool: True if the download was successful, False otherwise.
        """
        try:
            # Ensure local directory exists
            local_dir = os.path.dirname(local_path)
            if local_dir:
                Path(local_dir).mkdir(parents=True, exist_ok=True)
                
            self.s3.download_file(self.bucket, remote_key, local_path)
            return True
        except ClientError:
            return False

    def delete(self, remote_key: str) -> bool:
        """
        Deletes a file from the S3 bucket.

        Args:
            remote_key (str): The key of the file to delete from S3.

        Returns:
            bool: True if the deletion was successful, False otherwise.
        """
        try:
            self.s3.delete_object(Bucket=self.bucket, Key=remote_key)
            return True
        except ClientError:
            return False
