import os
from actions_logging.app_logging import logger
from build_and_deploy.get_service_type import parse_and_validate_repos_yaml
from github.constants import EKS_REPO_NAME, REPOS_YAML_PATH, OWNER
from github.get_changed_dir import get_changed_dirs
from github.env import get_required_env_var, exit_on_error_and_write_summary, write_github_env
from github.github_apis import get_file_content
from env_files.constants import DEFAULT_ENV_FILES_PATH
from lambdas.get_lambda_paths import get_lambda_root_path_from_repos_yaml, get_lambda_ms_path_from_root_path
from typing import List

def get_ms_names_from_repos_yaml(repo_name) -> list:
    """
    get the ms names from the repos.yaml file
    :param repo_name:
    :return: ms names list
    """
    try:
        repos_yaml_content = get_file_content(EKS_REPO_NAME, REPOS_YAML_PATH)
        repos_yaml_dict = parse_and_validate_repos_yaml(repos_yaml_content)
        if f"{OWNER}/" in repo_name:
            repo_name = repo_name.split('/')[-1]
        repo_data = repos_yaml_dict.get(repo_name)
        if not repo_data:
            exit_on_error_and_write_summary(f"Repo {repo_name} not found in repos.yaml")
        if not isinstance(repo_data, list):
            exit_on_error_and_write_summary(f"repo data {repo_data} is not a list. (IS_MULTI_SERVICE_REPO={os.getenv('IS_MULTI_SERVICE_REPO')}). something wrong")
        ms_names = [ms_data.get('ms_name') for ms_data in repo_data]
        return ms_names
    except Exception as e:
        raise RuntimeError(f"Error in get_ms_names_from_repos_yaml: {e}")


def get_svc_name_from_changed_dir(dirs_by_ms: List[str], pr_changed_dirs: List[str]) -> str:
    """
    Determines the service name based on the directories changed in a pull request.

    Args:
        dirs_by_ms (List[str]): A list of directory paths representing microservices.
        pr_changed_dirs (List[str]): A list of directory paths that were changed in the pull request.

    Returns:
        str: The name of the service corresponding to the changed directory. If more than one
             microservice directory is changed, the function exits with an error.

    Raises:
        SystemExit: If changes are detected in more than one microservice directory.

    Notes:
        - The function assumes that microservice directories are uniquely identifiable by their paths.
        - If a directory path starts with './', it is normalized by removing the prefix.
        - Logs information about the matching directories for debugging purposes.
    """
    changed_ms = set()
    svc_name = ''
    for ms_path in dirs_by_ms:
        if ms_path.startswith('./'):
            ms_path = ms_path[2:]
        for changed_dir in pr_changed_dirs:
            if changed_dir.startswith(ms_path) or changed_dir.startswith(f'./{ms_path}'):
                logger.info(f"changed dir {changed_dir} is in ms path {ms_path}")
                changed_ms.add(ms_path)
    if len(changed_ms) > 1:
        exit_on_error_and_write_summary(f"more then 1 ms has been changed. NOT ALLOWED: {changed_ms}")
    if changed_ms:
        svc_name = changed_ms.pop().split('/')[-1]
    return svc_name

def get_svc_names_from_changed_dirs(dirs_by_ms: List[str], pr_changed_dirs: List[str]) -> List[str]:
    """
    Determines the service names from the changed directories in a multi-service repository.

    Args:
        dirs_by_ms (List[str]): A list of directory paths representing the multi-service structure.
                                Each path corresponds to a service directory.
        pr_changed_dirs (List[str]): A list of directory paths that were changed in the pull request.

    Returns:
        List[str]: A list of service names corresponding to the changed directories. The service name
                   is derived from the last segment of the directory path.
    """
    changed_ms_dirs = set()
    svc_names = []
    for ms_path in dirs_by_ms:
        normalized_ms_path = ms_path.lstrip('./')
        for changed_dir in pr_changed_dirs:
            normalized_changed_dir = changed_dir.lstrip('./')
            if normalized_changed_dir.startswith(normalized_ms_path):
                logger.info(f"changed dir {changed_dir} is in ms path {ms_path}")
                changed_ms_dirs.add(ms_path)
    if changed_ms_dirs:
        svc_names = [d.split('/')[-1] for d in changed_ms_dirs]
    return svc_names


def main():
    """
    get the changed dirs in the PR and check if the changed dir is in the ms path
    will fail on more than one ms that has been changed in this pr
    """
    try:
        is_multi_service_repo = os.getenv('IS_MULTI_SERVICE_REPO')
        if not is_multi_service_repo == 'true':
            logger.info(f"not multi service repo (IS_MULTI_SERVICE_REPO={is_multi_service_repo}), exiting")
            exit(0)

        repo_name = get_required_env_var('GITHUB_REPOSITORY')
        svc_type = get_required_env_var('SVC_TYPE')
        ms_root_path = ''

        logger.info(f'GITHUB_REPOSITORY: {repo_name}')

        if svc_type == 'lambda':
            ms_root_path = get_lambda_root_path_from_repos_yaml(repo_name)

        ms_root_path = os.getenv('MS_ROOT_PATH', ms_root_path)  # path for artifact|s code
        pr_number = get_required_env_var("PR_NUMBER")
        changed_dirs = get_changed_dirs(full_repo_name=repo_name, pr_number=pr_number)

        logger.info(f'MS_ROOT_PATH: {ms_root_path}')
        logger.info(f'changed_dirs: {changed_dirs}')

        ms_names = get_ms_names_from_repos_yaml(repo_name)
        ms_paths = ms_names
        if ms_root_path:
            ms_paths = [f"{ms_root_path}/{ms_name}" for ms_name in ms_names]
        svc_name_from_code_changes = get_svc_name_from_changed_dir(ms_paths, changed_dirs)
        ms_env_paths = [f"{DEFAULT_ENV_FILES_PATH}/{ms_name}" for ms_name in ms_names]
        svc_name_from_env_changes = get_svc_name_from_changed_dir(ms_env_paths, changed_dirs)

        if svc_name_from_code_changes and svc_name_from_env_changes:
            if svc_name_from_code_changes != svc_name_from_env_changes:
                exit_on_error_and_write_summary(f"svc name from code changes {svc_name_from_code_changes} is different from svc name from env changes {svc_name_from_env_changes}")
        svc_name = svc_name_from_code_changes or svc_name_from_env_changes
        if not svc_name:
            exit_on_error_and_write_summary(f"svc name not found from code or env changes."
                                            f" its means that in this pr of this multi service repo there wasn't"
                                            f" any changes in the ms code or env files so no version will create")

        if svc_type == 'lambda':
            ms_path = get_lambda_ms_path_from_root_path(is_multi_service_repo, svc_name, ms_root_path)
            logger.info(f'MS_PATH is set to {ms_path}.')
            logger.info(f'LAMBDA_PATH is set to {ms_path}')
            write_github_env(ms_path, 'MS_PATH')

        logger.info(f"set SVC_NAME to {svc_name}")
        logger.info(f'MS_ROOT_PATH is set to {ms_root_path}.')
        write_github_env(svc_name, 'SVC_NAME')
        write_github_env(ms_root_path, 'MS_ROOT_PATH')
    except Exception as e:
        exit_on_error_and_write_summary(f"Error in validate_changes_in_multiservice_repo.main: {e}")


if __name__ == '__main__':
    main()
