from fastapi import UploadFile, HTTPException
import boto3
from botocore.exceptions import BotoCoreError, ClientError
import os
from datetime import datetime
from dotenv import load_dotenv
import json

load_dotenv()
AWS_REGION = os.getenv('AWS_REGION')
BUCKET_NAME = os.getenv('BUCKET_NAME')
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')

class S3Service:
    def __init__(self):
        """
        Initialize the AWS client
        """
        self.bucket_name = BUCKET_NAME
        self.s3_client = boto3.client(
            's3',
            region_name=AWS_REGION,
            aws_access_key_id=AWS_ACCESS_KEY_ID,
            aws_secret_access_key=AWS_SECRET_ACCESS_KEY
        )

    def create_directory(self, directory_name: str):
        """
        Create a 'directory' in S3 (actually a prefix ending with '/')
        """
        try:
            self.s3_client.put_object(Bucket=self.bucket_name, Key=f"{directory_name}/")
            return {"message": f"Directory '{directory_name}' created successfully."}
        except (BotoCoreError, ClientError) as e:
            raise HTTPException(status_code=400, detail=str(e))

    def upload_object(self, directory_name: str, sanitized_name: str, content: str, ContentType: str = "text/markdown"):
        """
        Upload a file to the specified S3 directory
        """
        try:
            file_key = f"{directory_name}/{sanitized_name}"
            extra_args = {"ACL": "public-read"}
            # self.s3_client.upload_fileobj(file.file, self.bucket_name, file_key)
            self.s3_client.put_object(
                Bucket=BUCKET_NAME,
                Key=file_key,
                Body=content,
                ContentType=ContentType
            )
            file_url = f"https://{self.bucket_name}.s3.{self.s3_client.meta.region_name}.amazonaws.com/{file_key}"
            return {"message": f"File '{sanitized_name}' uploaded to '{directory_name}' successfully.","file_url": file_url}
        except (BotoCoreError, ClientError) as e:
            raise HTTPException(status_code=400, detail=str(e))
        
    def upload_imscc_to_s3(self, local_path: str, s3_directory: str):
        """
        Upload a locally generated IMSCC (ZIP) file to S3 in correct binary format
        and return both public URL & presigned URL.

        Args:
            local_path (str): Path like './result/MyCourse.imscc'
            s3_directory (str): Directory inside S3, e.g. 'Syllabuild_Courses/some-id'

        Returns:
            dict: URLs and metadata
        """
        if not os.path.exists(local_path):
            raise FileNotFoundError(f"IMSCC file not found: {local_path}")

        file_name = os.path.basename(local_path)
        s3_key = f"{s3_directory}/{file_name}"

        try:
            # Upload in binary mode using upload_file (the safest for large files)
            self.s3_client.upload_file(
                Filename=local_path,
                Bucket=BUCKET_NAME,
                Key=s3_key,
                ExtraArgs={
                    "ContentType": "application/zip",   # REQUIRED for Canvas
                    # "ACL": "public-read"               # Or remove if you want it private
                }
            )

            # Public URL (works only if ACL = public-read)
            public_url = f"https://{BUCKET_NAME}.s3.{AWS_REGION}.amazonaws.com/{s3_key}"

            # Presigned URL for Canvas import (ALWAYS WORKS)
            presigned_url = self.s3_client.generate_presigned_url(
                "get_object",
                Params={"Bucket": BUCKET_NAME, "Key": s3_key},
                ExpiresIn=3600  # 1 hour
            )

            return {
                "s3_key": s3_key,
                "public_url": public_url,
                "presigned_url": presigned_url,
                "message": f"Uploaded {file_name} to S3 successfully."
            }

        except (BotoCoreError, ClientError) as e:
            raise Exception(f"S3 Upload failed: {e}")

    def fetch_object(self,directory_name: str):
        """
        Fetch files in a specific S3 directory
        """
        try:
            response = self.s3_client.list_objects_v2(Bucket=self.bucket_name, Prefix=f"{directory_name}/")
            if 'Contents' not in response:
                return {"files": []}
            
            files = [obj['Key'] for obj in response['Contents']]
            return {"files": files}
        except (BotoCoreError, ClientError) as e:
            raise HTTPException(status_code=400, detail=str(e))

    def get_directory_size(self, directory_name: str):
        """
        Get the total size of files in a directory
        """
        try:
            response = self.s3_client.list_objects_v2(Bucket=self.bucket_name, Prefix=f"{directory_name}/")
            if 'Contents' not in response:
                return {"directory_size": 0}
            
            total_size = sum(obj['Size'] for obj in response['Contents'])
            return {"directory_size": total_size}
        except (BotoCoreError, ClientError) as e:
            raise HTTPException(status_code=400, detail=str(e))

    def get_object_url(self, file_key):
        file_url = f"https://{self.bucket_name}.s3.{self.s3_client.meta.region_name}.amazonaws.com/{file_key}"
        return file_url

    def delete_file(self, file_key: str):
        """
        Delete a file from the S3 bucket.

        Args:
            file_key (str): The key (path) of the file to delete in the S3 bucket.

        Returns:
            dict: Success message.
        """
        try:
            self.s3_client.delete_object(Bucket=self.bucket_name, Key=file_key)
            return {"message": f"File '{file_key}' deleted successfully."}
        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))