import boto3
import requests
from botocore.client import Config
from botocore.exceptions import ClientError


class S3Client:
    def __init__(self, access_key, secret_key, env='prod'):
        self.endpoint = {
            'dev': 'https://flava-dev-object-storage-api.workers-hub.com',
            'prod': 'https://flava-prod-object-storage-api.workers-hub.com',
            'prod_top_secret': 'https://flava-topsecret-object-storage-api.workers-hub.com',
            'prod_secret': 'https://flava-secret-object-storage-api.workers-hub.com',
            'ssk_prod': 'https://flava-prod-ssk-object-storage-api.workers-hub.com'
        }
        # Validate environment
        if env not in ['dev', 'prod', 'prod_top_secret', 'prod_secret', 'ssk_prod']:
            raise ValueError(f"Invalid environment: {env}. Must be either 'dev' or 'prod'")
        
        self.fos_endpoint = self.endpoint[env]

        config = Config(
            s3={"addressing_style": "path"}
        )
        # Init S3 Client with FOS endpoint and AWS Keys
        try:
            self.s3_client = boto3.client(
                "s3",
                aws_access_key_id=access_key,
                aws_secret_access_key=secret_key,
                endpoint_url=self.fos_endpoint,
                config=config,
            )
        except Exception as e:
            raise RuntimeError(
                f"Failed to initialize S3 Client for {self.fos_endpoint}"
            ) from e

    def download_object(self, bucket_name, obj_name, dst_name):
        # Execute Download Object from FOS
        try:
            response = self.s3_client.download_file(Bucket=bucket_name, Key=obj_name, Filename=dst_name)
        except ClientError as e:
            return False, e.response
        return True, response

    def upload_object(self, bucket_name, obj_name, src_name):
        # GET presigned url for upload object on bucket (required)
        presigned_url = self.s3_client.generate_presigned_url(
            'put_object',
            Params={'Bucket': bucket_name, 'Key': obj_name},
            ExpiresIn=300,
        )
        # Upload file through presigend url
        with open(src_name, 'rb') as file:
            response = requests.put(presigned_url, data=file)
        if response.status_code == 200:
            return True, response
        return False, response

    def set_object_acl(self, bucket_name, obj_name, set_acl='public-read'):
        # Set the ACL to target object on bucket (default='public-read')
        try:
            self.s3_client.put_object_acl(ACL=set_acl, Bucket=bucket_name, Key=obj_name)
            return True, "Upload successful and ACL set to public-read"
        except Exception as e:
            return False, f"Upload successful but failed to set ACL: {str(e)}"

    def get_object(self, bucket_name, obj_name):
        # Execute GET Object from FOS
        try:
            response = self.s3_client.get_object(Bucket=bucket_name, Key=obj_name)
        except ClientError as e:
            return False, e.response
        return True, response

    def delete_object(self, bucket_name, obj_name):
        # GET presigned URL for delete object
        presigned_url = self.s3_client.generate_presigned_url(
            'delete_object',
            Params={'Bucket': bucket_name, 'Key': obj_name},
            ExpiresIn=300,
        )

        # Delete object through presigned URL
        response = requests.delete(presigned_url)
        if response.status_code == 204:
            return True, "Object deleted successfully"
        return False, f"Failed to delete object: {response.text}"
    
    def get_list_object(self, bucket_name):
        # Execute GET List Object from FOS
        try:
            response = self.s3_client.list_objects_v2(Bucket=bucket_name)
            objects = response.get('Contents', [])
            return True, objects
        except ClientError as e:
            return False, e.response
        except Exception as e:
            return False, {"code": 500, "msg": f"Failed to get list bucket: {str(e)}"}

    def update_bucket(self, bucket_name, action):
        # Update static website configuration of bucket
        try:
            if action == "enable":
                website_configuration = {
                    "IndexDocument": {"Suffix": "index.html"},
                    "ErrorDocument": {"Key": "error.html"}
                }
                self.s3_client.put_bucket_website(
                    Bucket=bucket_name,
                    WebsiteConfiguration=website_configuration
                )
                return {"code": 200, "msg": f"{bucket_name} enabled successfully"}
            elif action == "disable":
                self.s3_client.delete_bucket_website(Bucket=bucket_name)
                return {"code": 200, "msg": f"{bucket_name} disabled successfully"}
        except Exception as e:
                return {"code": 500, "msg": f"Failed to update bucket: {str(e)}"}
