import os
from typing import List
from actions_logging.app_logging import logger
from github.env import write_github_env, exit_on_error_and_write_summary, get_required_env_var,write_github_summary
from github.github_apis import get_changed_files
from env_files.build_and_upload_dotenv import build_and_upload_dot_envs
from env_files.validate_env_vars import validate_env_vars_are_in_global
from env_files.get_semver_for_new_tag import get_version_and_changes
from env_files.version_history import (
    create_envs_initial_history,
    update_envs_history,
    upload_services_version_histories_to_s3
)
from env_files.utils import get_all_env_files_names
from env_files.constants import (
    INIT_MESSAGE,
    DEFAULT_ENV_FILES_PATH,
    VERSION_SUFFIX, VERSION_HISTORY_FOLDER
)


def get_pr_scopes(pr_changed_files: List[str], env_files_path: str = DEFAULT_ENV_FILES_PATH) -> tuple[bool, bool]:
    """
    this function will check if the pr has changes in the env files location
    :param pr_changed_files:
    :param env_files_path:
    :return: bool, bool for env files changed and not env files changed
    """
    try:
        logger.info_yellow("checking if the PR has changes in the env files, code or both")
        env_files_changed = False
        code_changed = False
        if not pr_changed_files:
            logger.warning("No files were changed in this PR")
            return env_files_changed, code_changed
        for file in pr_changed_files:
            # we've already found both types of changes, no need to continue
            if env_files_changed and code_changed:
                break
            if file.startswith(env_files_path):
                env_files_changed = True
                logger.info("env files changed detected")
                continue
            # we don't build artifacts for non-code changes
            if file.startswith('.github') or file in ['README.md', 'CHANGELOG.md']:
                continue
            else:
                if code_changed:
                    continue
                code_changed = True
                logger.info("code changed detected")
        if not env_files_changed and not code_changed:
            logger.info('no changes found in env files or code')
        return env_files_changed, code_changed
    except Exception as e:
        raise RuntimeError(f"An error occurred in get_pr_scopes as {e}")


def get_pr_scope_and_validate_global_rule(env_files_base_path: str, is_multi_service_repo: bool):
    """
    this function will get the pr scope and validate the global rule
    this function serve as the init part for env vars handling. kind of pre steps
    :param env_files_base_path:
    :param is_multi_service_repo:
    :return:
    """
    try:
        logger.info("getting pr scope and validate global rule:")
        pr_number = get_required_env_var("PR_NUMBER")
        repo_name = get_required_env_var("GITHUB_REPOSITORY")
        svc_type = get_required_env_var("SVC_TYPE")
        pr_changed_files = [file['filename'] for file in get_changed_files(repo_name, pr_number) if
                            file.get('filename')]
        logger.debug(f"pr changed files: {pr_changed_files}")

        env_files_changed, code_changed = get_pr_scopes(pr_changed_files, env_files_base_path)

        if code_changed:
            logger.info("Code changes found, setting BUILD_ARTIFACT to true")
            write_github_env('true', 'BUILD_ARTIFACT')
        if env_files_changed and svc_type in ['frontend']:
            logger.info(f"env files changed, since this is a {svc_type} app we need to build an artifact too, setting BUILD_ARTIFACT to true")
            write_github_env('true', 'BUILD_ARTIFACT')
        if not env_files_changed:
            logger.info_yellow("nor code nor env file changes found, skipping futher env vars changes handling")
            exit(0)
        logger.info_green("changes found in env files, will check global rule")
        logger.info(f"{repo_name} is a multi service repo? {is_multi_service_repo}")
        logger.debug(f"will iterate over env files and validate global rule")
        validate_env_vars_are_in_global(is_multi_service_repo,
                                                        env_files_base_path,
                                                        pr_changed_files)
    except Exception as e:
        raise RuntimeError(f"An error occurred in get_pr_scope_and_validate_global_rule as {e}")


def main():
    """
        will export BUILD_ARTIFACT
        check new rule of global env file
        will get ENV_FILE_SEMVER and calculate version from the last env version
        iterate over every env and:
          if there were changes in this env:
            generate new env file into s3
          update version_history.txt with env changes (if exists) and ENV_FILE_VERSION
    """
    try:
        if not os.path.isdir(DEFAULT_ENV_FILES_PATH):
            logger.warning(f"env files path {DEFAULT_ENV_FILES_PATH} does not exist, stopping handle env vars changes")
            exit(0)
        logger.info_green_bg("start to handle env vars changes")
        env_files_base_path = os.getenv("ENV_FILES_PATH", DEFAULT_ENV_FILES_PATH)
        is_multi_service_repo = os.getenv("IS_MULTI_SERVICE_REPO", 'false') == 'true'
        if os.getenv("SVC_TYPE", "") == "core-ecs":
            logger.info("this is a core-ecs service, will handle env vars changes as multi service repo")
            is_multi_service_repo = True
        get_pr_scope_and_validate_global_rule(env_files_base_path, is_multi_service_repo)
        logger.info_green("the changes in env files are valid. will create versions and version_history.txt")
        git_tag_version, changes = get_version_and_changes(env_files_base_path)
        git_tag = f"{git_tag_version}{VERSION_SUFFIX}"
        logger.info_green(f"got git tag version: {git_tag_version} and changes")
        logger.debug(f"changes: {changes}")
        all_env_files = get_all_env_files_names(env_files_base_path)
        logger.debug(f"got all env files: {all_env_files}")
        if changes == INIT_MESSAGE:  # first time:
            all_env_files_versions = create_envs_initial_history(all_env_files,
                                                                 is_multi_service_repo,
                                                                 git_tag,
                                                                 VERSION_HISTORY_FOLDER)
            logger.debug(f"after init history all_env_files_versions: {all_env_files_versions}")
        else:
            all_env_files_versions = update_envs_history(all_env_files, env_files_base_path, git_tag, changes,
                                                         is_multi_service_repo)
            logger.debug(f"after update history all_env_files_versions: {all_env_files_versions}")
        logger.info_green("version_history.txt updated or created for all envs")
        build_and_upload_dot_envs(all_env_files_versions, is_multi_service_repo)
        logger.info_green("env files uploaded to s3")
        upload_services_version_histories_to_s3(is_multi_service_repo)
        logger.info_green("version_history.txt uploaded to s3")
        write_github_env(git_tag, 'ENV_FILE_VERSION')
        write_github_summary(f":trophy: new env files version created: **{git_tag}**")
        logger.info_green_bg("done to handle env vars changes")
    except Exception as e:
        logger.trace_back(e.__traceback__)
        exit_on_error_and_write_summary(f"An error occurred in handle_env_vars_changes.main: {e}")


if __name__ == '__main__':
    main()