from dataclasses import dataclass
from typing import NoReturn, Optional

from colorama import Fore
from gitlab import GitlabCreateError, GitlabGetError, const
from yaml import FullLoader, dump, load_all

from argocd.helper import get_deployment_files
from common import (
    exit_message,
    get_env_variable,
    get_env_variable_required,
    print_message,
)
from common.gitlab_helper import GitlabHelper


@dataclass
class ArgoCDApp(object):
    name: str
    path: str
    namespace: Optional[str] = None
    domain: Optional[str] = None
    label: Optional[str] = None
    to_delete: bool = False


class ArgoCDCenter(object):
    def __init__(self) -> NoReturn:
        self.ci_default_branch = get_env_variable_required("CI_DEFAULT_BRANCH")

        self.leaf_manifest_project = None

        manifest_project_ref = self._get_manifest_project_ref()

        if manifest_project_ref:
            self.gitlab_helper = GitlabHelper()

            self.service_project = self.gitlab_helper.get_raw_project(
                get_env_variable_required("CI_PROJECT_ID")
            )
            self.manifest_project = self.gitlab_helper.get_raw_project(
                manifest_project_ref
            )
            if "leaf" not in self.manifest_project.name:
                # Get the environment value of project, because on CI the environment is replaced by another.
                try:
                    self.leaf_manifest_project = self.gitlab_helper.get_raw_project(
                        self.service_project.variables.get("SCI_MANIFEST_PROJECT").value
                    )
                except GitlabGetError:
                    pass

            self.ci_default_branch = self.manifest_project.default_branch

    def get_yaml_by_path(self, file_path) -> dict:
        yaml_file = self.manifest_project.files.get(
            file_path=file_path, ref=self.ci_default_branch
        )
        try:
            return next(load_all(yaml_file.decode(), Loader=FullLoader))
        except Exception as err:
            print_message(err, Fore.YELLOW)
            return None

    def copy_deployment_from_leaf(self, image_name, svc_name) -> bool:
        blobs_result_search = self.leaf_manifest_project.search(
            const.SEARCH_SCOPE_BLOBS, f"{image_name}:"
        )

        manifest_was_implemented = False
        mr = None

        if not blobs_result_search:
            base_leaf_name = self.leaf_manifest_project.name.replace(
                "-k8s-manifests", ""
            )
            projects_found = self.gitlab_helper.search_through_projects(base_leaf_name)
            for project in projects_found:
                project_to_search = self.gitlab_helper.get_raw_project(project["id"])
                deployment_in_subproject = project_to_search.search(
                    const.SEARCH_SCOPE_BLOBS, f"{image_name}:"
                )
                if deployment_in_subproject:
                    blobs_result_search = deployment_in_subproject
                    self.leaf_manifest_project = project_to_search
                    break

        deployment_files = get_deployment_files(
            self.leaf_manifest_project, blobs_result_search
        )

        if deployment_files:
            branch_name = f"deploy/{svc_name}"
            print_message(
                "Criando/Atualizando branch de implantação no repositório de manifestos..."
            )
            try:
                self.manifest_project.branches.get(branch_name)
            except GitlabGetError:
                self.manifest_project.branches.create(
                    {"branch": branch_name, "ref": "develop"}
                )

            actions = []
            for deployment_file in deployment_files:
                actions.append(
                    {
                        "action": "create",
                        "file_path": deployment_file.path,
                        "content": dump(deployment_file.content),
                    },
                )
            commit_message = f"Implantando serviço {svc_name}."
            try:
                self.manifest_project.commits.create(
                    {
                        "branch": branch_name,
                        "commit_message": commit_message,
                        "actions": actions,
                    }
                )
                mr = self.manifest_project.mergerequests.create(
                    {
                        "source_branch": branch_name,
                        "target_branch": "develop",
                        "title": commit_message,
                        "remove_source_branch": True,
                    }
                )
                print_message(
                    f"MR de implantação do serviço criado, avise o seu SRE para revisar: {mr.web_url}",
                )
            except GitlabCreateError:
                exit_message(
                    f"Já existe um MR de implantação para o branch {branch_name}",
                )

            manifest_was_implemented = True

        if not manifest_was_implemented:
            exit_message(
                f'Não foi encontrado nenhum manifesto para atualização da imagem "{image_name}"',
            )

        return bool(mr)

    @staticmethod
    def _get_manifest_project_ref():
        ci_project_name = get_env_variable_required("CI_PROJECT_NAME")

        if "k8s-manifests" in ci_project_name:
            return get_env_variable_required("CI_PROJECT_ID")

        return get_env_variable("SCI_MANIFEST_PROJECT")
