import os

import boto3
from actions_logging.app_logging import logger
from aws.assume_iam import assume_role_get_creds
from botocore.exceptions import ClientError, NoCredentialsError, PartialCredentialsError
from github.env import exit_on_error_and_write_summary, get_required_env_var


def create_s3_client(env_name: str = ""):
    try:
        credentials = None
        if env_name:
            credentials = assume_role_get_creds(env_name)
            logger.info_yellow(f"Assumed role for {env_name}")
        if not env_name or not credentials:
            credentials = {
                "AccessKeyId": get_required_env_var("AWS_ACCESS_KEY_ID"),
                "SecretAccessKey": get_required_env_var("AWS_SECRET_ACCESS_KEY"),
                "SessionToken": get_required_env_var("AWS_SESSION_TOKEN"),
            }
        s3 = boto3.client(
            "s3",
            aws_access_key_id=credentials["AccessKeyId"],
            aws_secret_access_key=credentials["SecretAccessKey"],
            aws_session_token=credentials["SessionToken"],
        )
        return s3
    except NoCredentialsError:
        exit_on_error_and_write_summary("Credentials not available in create_s3_client.")
    except Exception as e:
        exit_on_error_and_write_summary(f"Error in create_s3_client: {e}")


def copy_s3_file(source_bucket: str, source_key: str, dest_bucket: str, dest_key: str):
    logger.debug(f"Copying file from {source_bucket}/{source_key} to {dest_bucket}/{dest_key}")
    s3 = boto3.resource("s3")

    copy_source = {"Bucket": source_bucket, "Key": source_key}
    try:
        s3.meta.client.copy(copy_source, dest_bucket, dest_key)
        logger.info(f"File copied from {source_bucket}/{source_key} to {dest_bucket}/{dest_key} successfully.")
    except NoCredentialsError:
        exit_on_error_and_write_summary("Credentials not available.")
    except PartialCredentialsError:
        exit_on_error_and_write_summary("Incomplete credentials provided.")
    except Exception as e:
        exit_on_error_and_write_summary(f"Error occurred: {e}")


def download_file_from_s3(
    bucket_name: str, file_name: str, file_name_to_save: str = "", env_name: str = "", skip_errors: bool = False
) -> None:
    try:
        s3 = create_s3_client(env_name)
        if not file_name_to_save:
            file_name_to_save = file_name
        logger.info(f"Downloading file {file_name} from bucket {bucket_name} and saving to {file_name_to_save}")

        if os.path.dirname(file_name_to_save):
            os.makedirs(os.path.dirname(file_name_to_save), exist_ok=True)
        s3.download_file(bucket_name, file_name, file_name_to_save)
        logger.info("Download complete")
    except Exception as e:
        msg = f"Error in download_file_from_s3: {e}"
        if skip_errors:
            logger.error(msg)
        else:
            exit_on_error_and_write_summary(msg)


def check_file_exists_in_s3(bucket_name: str, file_name: str, env_name: str = "") -> bool:
    try:
        logger.info(f"Checking if file {file_name} exists in bucket {bucket_name}")
        s3 = create_s3_client(env_name)
        s3.head_object(Bucket=bucket_name, Key=file_name)
        logger.info(f"File {file_name} exists in bucket {bucket_name}")
        return True
    except ClientError as e:
        if e.response["Error"]["Code"] == "404":
            logger.error(f"File {file_name} not found in bucket {bucket_name}")
            return False
        else:
            exit_on_error_and_write_summary(f"Client error in check_file_exists_in_s3: {e}")
    except Exception as e:
        exit_on_error_and_write_summary(f"General error in check_file_exists_in_s3: {e}")


def upload_file_to_s3(bucket_name: str, file_name: str, file_path: str = None, env_name: str = "") -> None:
    """
    create_s3_client assumes role if env_name is provided, if you don't need to assume role, don't provide env_name
    """
    try:
        s3 = create_s3_client(env_name)
        if not file_path:
            file_path = file_name
        logger.debug(f"uploading file {file_name} to bucket {bucket_name} with the path {file_path}")
        s3.upload_file(file_name, bucket_name, file_path)
        logger.info(f"done uploading file {file_name} to bucket {bucket_name}")
    except Exception as e:
        exit_on_error_and_write_summary(f"error in upload_file_to_s3: {e}")


def get_s3_tag(bucket_name: str, file_name: str, tag_key: str, env_name: str = "") -> str:
    s3 = create_s3_client(env_name)
    logger.info(f"grabbing tag for {file_name}")
    try:
        response = s3.get_object_tagging(Bucket=bucket_name, Key=file_name)
        for tag in response["TagSet"]:
            if tag["Key"] == tag_key:
                logger.info(f"tag found: {tag['Value']}")
                return tag["Value"]
        logger.warning_yellow(f"Tag {tag_key} not found for {file_name}")
        return ""
    except s3.exceptions.NoSuchKey:
        exit_on_error_and_write_summary(f"error in get_s3_tag: {s3.exceptions.NoSuchKey}")


def check_and_create_folder(
    bucket_name: str, folder_name: str, create_if_not_exists: bool = False, env_name: str = ""
) -> str:
    try:
        s3 = create_s3_client(env_name)
        # List objects in the bucket with the specified prefix (folder name)
        response = s3.list_objects_v2(Bucket=bucket_name, Prefix=folder_name)
        # Check if the folder exists
        folder_exists = "Contents" in response
        if not folder_exists and create_if_not_exists:
            # Create the folder by uploading an empty object with the folder name
            s3.put_object(Bucket=bucket_name, Key=folder_name)
            logger.info(f"Folder '{folder_name}' created in the bucket '{bucket_name}'.")
        elif folder_exists:
            logger.info(f"Folder '{folder_name}' already exists in the bucket '{bucket_name}'.")
        else:
            logger.info(f"Folder '{folder_name}' does not exist in the bucket '{bucket_name}' and was not created.")
        return folder_exists
    except Exception as e:
        exit_on_error_and_write_summary(f"error in check_and_create_folder: {e}")


def delete_file_from_s3(bucket_name: str, file_name: str, env_name: str = ""):
    s3 = create_s3_client(env_name)
    logger.info(f"Deleting {file_name} from s3://{bucket_name}.")
    s3.delete_object(Bucket=bucket_name, Key=file_name)
    logger.info(f"s3://{bucket_name}/{file_name} was successfully deleted.")


def s3bucket_exists(client, bucket_name: str) -> bool:
    try:
        client.head_bucket(Bucket=bucket_name)
        logger.info(f"S3 bucket '{bucket_name}' exists in current AWS account")
        return True
    except ClientError as e:
        error_code = e.response.get("Error", {}).get("Code", "Unknown")
        if error_code == "404":
            logger.info(f"S3 bucket '{bucket_name}' does not exist")
            return False

        elif error_code == "403":
            logger.error(f"Access denied to S3 bucket '{bucket_name}'")
            raise e

        raise e
    except Exception as e:
        logger.error(f"Unexpected error checking S3 bucket '{bucket_name}': {e}")
        raise e
