#!/usr/bin/env python3

import argparse
from typing import List, Optional

from kolga.settings import settings
from kolga.utils.general import get_track


class Devops:
    def __init__(self) -> None:

        settings.load_plugins()

        self.parser = argparse.ArgumentParser(description="Anders Devops")
        subparsers = self.parser.add_subparsers(
            dest="command", metavar="<command>", required=True
        )

        create_images_parser = subparsers.add_parser(
            "create_images", help="Builds Docker images from a local Dockerfile"
        )

        create_images_parser.add_argument(
            "--git-submodule-depth", dest="git_submodule_depth", default=1, type=int
        )

        create_images_parser.add_argument(
            "--git-submodule-jobs", dest="git_submodule_jobs", default=2, type=int
        )

        deploy_application_parser = subparsers.add_parser(
            "deploy_application",
            help="Deploy an application based on the current Docker image",
        )
        deploy_application_parser.add_argument("-t", "--track", dest="track")

        deploy_service_parser = subparsers.add_parser(
            "deploy_service", help="Deploy a service supported by the devops pipeline"
        )
        deploy_service_parser.add_argument("-e", "--env-var", dest="envvar")
        deploy_service_parser.add_argument(
            "-p", "--projects", dest="projects", nargs="+"
        )
        deploy_service_parser.add_argument("-s", "--service", dest="service")
        deploy_service_parser.add_argument("-t", "--track", dest="track")

        subparsers.add_parser("docker_test_image", help="Print image tag")

        subparsers.add_parser("help", help="Prints this help message")

        subparsers.add_parser("logo", help="Prints the magnificent Anders DevOps logo")

        review_cleanup_parser = subparsers.add_parser(
            "review_cleanup", help="Cleans up the current namespace"
        )
        review_cleanup_parser.add_argument("-t", "--track", dest="track")

        test_setup_parser = subparsers.add_parser(
            "test_setup",
            help="Sets up an environment for running tests on built Docker image",
        )

        test_setup_parser.add_argument(
            "--git-submodule-depth", dest="git_submodule_depth", default=1, type=int
        )

        test_setup_parser.add_argument(
            "--git-submodule-jobs", dest="git_submodule_jobs", default=2, type=int
        )

    def run_command(self) -> None:
        args = vars(self.parser.parse_args())
        command = args.pop("command")

        # use dispatch pattern to invoke method with same name
        with settings.plugin_manager.lifecycle.application():
            getattr(self, command)(**args)

    def create_images(self, git_submodule_depth: int, git_submodule_jobs: int) -> None:
        from kolga.libs.docker import Docker
        from kolga.libs.git import Git
        from kolga.libs.project import Project
        from kolga.utils.general import (
            BUILT_DOCKER_TEST_IMAGE,
            create_artifact_file_from_dict,
            env_var_safe_key,
        )

        if git_submodule_depth:
            g = Git()
            g.update_submodules(depth=git_submodule_depth, jobs=git_submodule_jobs)

        d = Docker()
        d.setup_buildkit()
        d.login()
        d.build_stages(push_images=True)

        track = get_track("development")
        project = Project(track=track)
        artifact_data = {
            f"{env_var_safe_key(project.name)}_{BUILT_DOCKER_TEST_IMAGE}": d.test_image_tag()
        }

        create_artifact_file_from_dict(
            env_dir=settings.BUILD_ARTIFACT_FOLDER,
            data=artifact_data,
            filename="docker_build",
        )

    def deploy_application(self, track: Optional[str] = None) -> None:
        from kolga.libs.kubernetes import Kubernetes
        from kolga.libs.project import Project
        from kolga.libs.vault import Vault

        track = get_track(track)
        main_project = Project(track=track)
        projects = main_project.dependency_projects + [main_project]

        k = Kubernetes(track=track)
        k.setup_helm()
        namespace = k.create_namespace()

        v = Vault(track)
        v.login()

        for project in projects:
            secret_data = {}
            if settings.VAULT_ADDR:
                secret_data.update(v.get_secrets())
            file_secrets_paths = k.create_file_secrets_from_environment(
                namespace=namespace,
                track=track,
                project=project,
                secret_name=project.file_secret_name,
            )
            secret_data.update(project.secret_data)
            secret_data.update(file_secrets_paths)

            k.create_secret(
                data=secret_data,
                namespace=namespace,
                track=track,
                secret_name=project.secret_name,
                project=project,
            )
            # TODO: Move this to the Project class
            k.create_basic_auth_secret(
                namespace=namespace, track=track, project=project
            )
            k.create_application_deployment(
                namespace=namespace, track=track, project=project
            )

    def deploy_service(
        self,
        envvar: str,
        projects: List[str],
        service: str,
        track: Optional[str] = None,
    ) -> None:
        from kolga.libs.kubernetes import Kubernetes
        from kolga.libs.service import Service
        from kolga.libs.services import services
        from kolga.utils.general import create_artifact_file_from_dict

        service_class = services.get(service, None)
        if not service_class:
            raise Exception(f"The service {service} is currently not supported")

        track = get_track(track)
        k = Kubernetes(track=track)
        k.setup_helm()
        namespace = k.create_namespace()

        service_instance = service_class(
            name=service, track=track, artifact_name=envvar
        )

        for project in projects:
            project_service = Service(
                name=project, track=track, chart_path=k.get_helm_path()
            )
            service_instance.add_prerequisite(project_service)

        service_instance.setup_prerequisites()
        k.deploy_service(service=service_instance, track=track, namespace=namespace)
        create_artifact_file_from_dict(
            env_dir=settings.SERVICE_ARTIFACT_FOLDER,
            data=service_instance.get_artifacts(),
            filename=service,
        )

    def help(self) -> None:
        self.parser.print_help()

    def review_cleanup(self, track: Optional[str] = None) -> None:
        from kolga.libs.kubernetes import Kubernetes

        track = get_track(track)
        k = Kubernetes(track=track)
        k.delete_namespace()

    def test_setup(self, git_submodule_depth: int, git_submodule_jobs: int) -> None:
        from kolga.libs.docker import Docker
        from kolga.libs.git import Git

        if git_submodule_depth:
            g = Git()
            g.update_submodules(depth=git_submodule_depth, jobs=git_submodule_jobs)

        d = Docker()
        d.login()

        test_image = settings.BUILT_DOCKER_TEST_IMAGE
        if not test_image:
            raise ValueError(f"No test image {test_image} found")
        d.pull_image(test_image)

    def docker_test_image(self) -> None:
        from kolga.libs.docker import Docker

        d = Docker()
        print(d.test_image_tag())  # noqa: T001

    def logo(self) -> None:
        logo = """

           _  __ __  _             
          | |/ //_/ | |            
          | ' / ___ | | __ _  __ _ 
          |  < / _ \\| |/ _` |/ _` |
          | . \\ (_) | | (_| | (_| |
          |_|\\_\\___/|_|\\__, |\\__,_|
                        __/ |      
                       |___/       
    DevOps Tooling by Anders Innovations Oy
        """  # noqa: W291
        print(logo)  # noqa: T001


if __name__ == "__main__":
    do = Devops()
    do.run_command()
