import json
import os

import boto3
from botocore.exceptions import NoCredentialsError, PartialCredentialsError

from actions_logging.app_logging import logger
from github.env import exit_on_error_and_write_summary, get_required_env_var
from github.get_file_from_github import (create_folder_if_not_exists,
                                         get_file_from_github)
from terragrunt.constants import DEPLOY_CONFIG, DEPLOY_CONFIG_PATH

class ConfigError(Exception):
    pass

s3_client = boto3.client('s3')

def normalize_path(path):
    return path.strip('/')

def get_modules_versions(semver):
    return f"modules_versions_{semver}.json"

def sanitize_work_dir(work_dir):
    sanitized_work_dir = work_dir.strip().strip('/')
    if not os.path.isdir(sanitized_work_dir):
        exit_on_error_and_write_summary(f"{work_dir} should be a manifest's directory.")
    return sanitized_work_dir

def safe_path(work_dir):
    if work_dir.startswith('/'):
        work_dir = work_dir[1:]
    if work_dir.endswith('/'):
        work_dir = work_dir[:-1]
    return work_dir.replace('/', '_')



def get_env_config(env_name):
    try:
        deploy_config = get_deploy_config()
        environments = deploy_config['environments']
    except KeyError:
        raise ConfigError("Environments block missing from deploy config. Update the configuration and re-try")
    try:
        env_data = environments[env_name]
    except KeyError:
        raise ConfigError(f"Environment data for '{env_name}' is missing from deploy config. Update the configuration and re-try")

    return env_data

def get_from_env_config(env_name, key):
    env_config = None
    try:
        logger.debug(f"fetching deploy config for {env_name} from {DEPLOY_CONFIG_PATH}")
        env_config = get_env_config(env_name)
    except Exception as ce:
        raise ConfigError(f"couldn't get deployment configuration: {ce}")
    try:
        logger.debug(f"fetching {key} from env config for {env_name}")
        val = env_config[key]
        logger.debug(f"{key} is {val}")
        return val
    except Exception as ce:
        raise ConfigError(f"'{key}' configuration is missing for {env_name} in deploy config. Update the configuration and re-try")

def get_bucket_name(env_name):
    try:
        bucket_name = get_from_env_config(env_name, 'plans_bucket')
        return str(bucket_name)
    except Exception as ce:
        exit_on_error_and_write_summary(f"couldn't get plans_bucket, error occured: {ce}")

def download_file_from_s3(bucket_name, object_path, local_path):
    s3_url = f"s3://{bucket_name}/{object_path}"
    try:
        s3_client.download_file(bucket_name, object_path, local_path)
        logger.info(f"{local_path} downloaded successfully from {s3_url}")
    except Exception as e:
        exit_on_error_and_write_summary(f"Failed to download {object_path} to {local_path} from {s3_url} due to error {e}")

def upload_file_to_s3(bucket_name, source, dst_path):
    if not source or not os.path.isfile(source):
        exit_on_error_and_write_summary(f"File {source} not found.")
    logger.info(f"File {source} found. Preparing to upload...")

    s3_url = f"s3://{bucket_name}/{dst_path}"
    try:
        s3_client.upload_file(source, bucket_name, dst_path)
        logger.info(f"{source} uploaded to {s3_url}")
    except NoCredentialsError:
        exit_on_error_and_write_summary('Credentials not available')
    except PartialCredentialsError:
        exit_on_error_and_write_summary('Incomplete credentials')
    except s3_client.exceptions.ClientError as e:
        exit_on_error_and_write_summary(f'Client error: {e}')
    except Exception as e:
        exit_on_error_and_write_summary(f"Failed to upload {source} to {s3_url} due to error: {e}")

def get_s3_path(gh_org_repo, *args):
    repo = gh_org_repo.split('/')[1]
    path_parts = [repo]
    for arg in args:
        path_parts.append(normalize_path(arg))
    return os.path.join(*path_parts)


def load_json_data(filename):
    """ Load JSON data from a file. """
    try:
        logger.debug(f"Loading JSON data from {filename}")
        with open(filename, 'r') as file:
            data = json.load(file)
            return data
    except Exception as e:
        exit_on_error_and_write_summary(f"Could not read json file due to the following error: {e}")


def get_deploy_config(config_path=''):
    """ in case the file is needed from a specific git ref (branch/tag) download it and put under that git ref folder"""
    try:
        deploy_config_path = config_path if config_path else DEPLOY_CONFIG_PATH
        deploy_config = load_json_data(deploy_config_path)
        if not deploy_config:
            raise ValueError(f"Empty deploy config on path {deploy_config_path}")
        return deploy_config
    except Exception as e:
        exit_on_error_and_write_summary(f"couldn't load IaC deploy config from {deploy_config_path} due to error: {e}")


def get_deploy_config_from_default_branch():
    """ in case the deploy config file is needed from default branch for security reasons"""
    try:
        repo = get_required_env_var('GITHUB_REPOSITORY')
        download_folder = os.path.join(os.getcwd(), 'default_branch', '.github')
        create_folder_if_not_exists(download_folder)
        get_file_from_github(repo, DEPLOY_CONFIG_PATH, download_folder)
        return get_deploy_config(config_path=os.path.join(download_folder, DEPLOY_CONFIG))
    except Exception as e:
        exit_on_error_and_write_summary(f"couldn't load IaC deploy config from default branch due to error: {e}")

