import os
import boto3
from botocore.exceptions import ClientError
from urllib.parse import urlparse
from datetime import datetime
import requests


class S3Uploader:
    """
    Simple helper to upload local files to S3 and return their S3 HTTPS URI.
    """

    def __init__(
        self,
        aws_access_key: str,
        aws_secret_key: str,
        region: str = "eu-west-2",
    ):
        self.aws_access_key = aws_access_key
        self.aws_secret_key = aws_secret_key
        self.region = region

        self.s3 = boto3.client(
            "s3",
            aws_access_key_id=self.aws_access_key,
            aws_secret_access_key=self.aws_secret_key,
            region_name=self.region,
        )

    def upload_file(self, file_path: str, bucket: str, prefix: str = "uploads/") -> str:
        """
        Upload a local file to an S3 bucket and return its HTTPS URI.

        Args:
            file_path (str): Path to the local file.
            bucket (str): S3 bucket name.
            prefix (str): Folder/prefix in the bucket.

        Returns:
            str: HTTPS-style S3 URI (not signed).
        """
        try:
            filename = os.path.basename(file_path)
            timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
            s3_key = os.path.join(prefix, f"{timestamp}_{filename}")

            self.s3.upload_file(file_path, bucket, s3_key)

            s3_uri = f"https://{bucket}.s3.{self.region}.amazonaws.com/{s3_key}"
            return s3_uri

        except ClientError as e:
            raise RuntimeError(f"❌ S3 upload failed: {e}")

    def print_bucket(self):
        print(self.s3.head_bucket(Bucket="retrofit-data-dev"))

    def generate_presigned_url(
        self, bucket: str, key: str, expires_in: int = 3600
    ) -> str:
        """
        Generate a temporary presigned URL for an S3 object.
        """
        try:
            return self.s3.generate_presigned_url(
                "get_object",
                Params={"Bucket": bucket, "Key": key},
                ExpiresIn=expires_in,
            )
        except ClientError as e:
            raise RuntimeError(f"❌ Failed to generate signed URL: {e}")

    def download_from_url(
        self, s3_url: str, local_dir: str = ".", expires_in: int = 3600
    ) -> str:
        """
        Download a file from a public or private S3 URL.
        If private, generates a presigned URL first.

        Args:
            s3_url (str): Full S3 HTTPS URL (e.g., https://bucket.s3.region.amazonaws.com/path/file.txt)
            local_dir (str): Folder to save the file in.
            expires_in (int): Presigned URL lifetime (seconds).

        Returns:
            str: Local file path of the downloaded file.
        """
        parsed = urlparse(s3_url)
        host_parts = parsed.netloc.split(".")
        if len(host_parts) < 3 or host_parts[1] != "s3":
            raise ValueError("❌ Not a valid S3 HTTPS URL")

        bucket = host_parts[0]
        key = parsed.path.lstrip("/")

        # Generate presigned URL (whether public or private)
        presigned_url = self.generate_presigned_url(bucket, key, expires_in)

        filename = os.path.basename(key)
        local_path = os.path.join(local_dir, filename)

        try:
            response = requests.get(presigned_url, stream=True)
            response.raise_for_status()

            os.makedirs(local_dir, exist_ok=True)
            with open(local_path, "wb") as f:
                for chunk in response.iter_content(chunk_size=8192):
                    f.write(chunk)

            print(f"✅ Downloaded: {local_path}")
            return local_path

        except requests.exceptions.RequestException as e:
            raise RuntimeError(f"❌ Failed to download file: {e}")
