import getopt
from datetime import datetime, timezone
from pathlib import Path
from re import match
from urllib import parse

from colorama import Fore

from common import (
    exec_command,
    exit_message,
    get_env_variable,
    get_env_variable_required,
    get_last_version,
    get_version,
    is_path_exist,
    is_semver,
    print_message,
)
from common.artifactory_helpers import configure_go_repository
from common.extensions import Extensions
from common.sonar_helper import SonarHelper, SonarScannerType
from common.validations.buildable import BuildableProject
from common.validations.changelog import ValidateChangelog
from common.validations.issues import check_and_validate_issues


class BaseGolangCi(object):
    def __init__(self, argv):
        self.buildable_project = BuildableProject()

        self.maven_username = get_env_variable_required("MAVEN_USERNAME")
        self.maven_password = get_env_variable_required("MAVEN_PASSWORD")
        self.sci_go_test_tags = get_env_variable("SCI_GO_TEST_TAGS")

        try:
            self._parse_opts(argv)
        except getopt.GetoptError as err:
            exit_message(err)

        self._prepare()

        print_message(
            "Atenção: Este script será descontinuado em breve."
            "Por favor, atualize o CI utilizando o Bastion."
            "Caso não seja possível, entre em contato com o time de DevSecOps.",
            Fore.YELLOW,
        )

    def _parse_opts(self, argv):
        """Serve de interface para chamar a publicação de cada classe filha"""

    def _prepare(self):
        """Prepares the environment to execute golang pipelines"""
        self._switch_go_version()

        configure_go_repository()

        usr = parse.quote(self.maven_username)
        pwd = parse.quote(self.maven_password.replace("\\", ""))

        self._go_exec(
            'env -w GOPROXY="https://proxy.golang.org,'
            f"https://{usr}:{pwd}@maven.proxy.senior.com.br/artifactory/go,"
            'direct"'
        )

        self._go_exec("env -w GONOSUMDB=git.senior.com.br")
        self._go_exec("env -w GOOS=linux")
        self._go_exec("env -w GOARCH=amd64")
        self._go_exec("env -w CGO_ENABLED=0")

    def _switch_go_version(self):
        version = self._determine_go_version()

        if version:
            exec_command(
                f"bash switch-go {version}",
                error_message=f"Ocorreu um erro ao tentar trocar a versão do Go para {version}",
            )
        else:
            go_version = get_env_variable("GO_VERSION")

            print_message(
                f"Não foi possível determinar a versão do Go no projeto, usando versão padrão da imagem: {go_version}"
            )

    @staticmethod
    def _determine_go_version():
        """Determines golang version based on go.mod"""

        if is_path_exist("go.mod"):
            with open("go.mod", encoding="utf8") as go_mod:
                lines = go_mod.readlines()
                for line in lines:
                    if line.startswith("go "):
                        return line[3:].strip()

        return None

    def _validation(self):
        check_and_validate_issues()

        extensions = Extensions()

        print_message("Executando validação de código")

        extensions.before_build()

        validate_changelog = ValidateChangelog()
        validate_changelog.validate()

        self._compile()

        print_message("Executando lint")

        if is_path_exist(".lint"):
            exit_message("The folder '.lint' cannot exists in project")

        Path(".lint").mkdir(parents=True)

        lint_command = "golangci-lint run --out-format colored-line-number:stdout,checkstyle:.lint/lint.xml"

        if self.sci_go_test_tags:
            lint_command = f"{lint_command} --build-tags {self.sci_go_test_tags}"

        exec_command(
            lint_command, error_message="Análise estática finalizada com erro."
        )

        extensions.after_build()

        print(" ")
        print_message(
            "Verificação de código e analise estática finalizados com sucesso.",
            Fore.GREEN,
        )
        print(" ")

    def _release(self, versioning_release):
        if versioning_release == "snapshot":
            self.publish()
        else:
            check_and_validate_issues()

            exec_command(f"bash senior-ci/ci/ci.sh -r {versioning_release}")

    def publish(self):
        """Serve de interface para chamar a publicação de cada classe filha"""

    def _run_sonar(self):
        check_and_validate_issues()

        if not self.buildable_project.skip_build():
            self._tests()

        sonar_helper = SonarHelper(sonar_scanner_type=SonarScannerType.GO)

        sonar_helper.scanner_analyze()

    def _compile(self):
        self._go_exec("mod tidy")
        self._go_exec("build")

    def _tests(self):
        self._compile()

        print_message("Executando testes")

        test_command = "test"

        if self.sci_go_test_tags:
            test_command = f"{test_command} -tags {self.sci_go_test_tags}"

        test_command = f"{test_command} -v -coverpkg=./... -coverprofile={SonarHelper.GO_COVERAGE_OUTPUT_PATH} ./..."

        self._go_exec(test_command)

    @staticmethod
    def _go_exec(command):
        """Executes a go command"""
        exec_command(
            f"go {command}",
            error_message="Ocorreu um erro ao tentar executar o comando go",
        )

    @staticmethod
    def get_version():
        """Checks the project version, changing it to golang standards"""

        project_version = get_version()

        if not is_semver(project_version):
            ci_commit_sha = get_env_variable("CI_COMMIT_SHA")

            commit_hash = ci_commit_sha[:12]

            ci_commit_timestamp = get_env_variable("CI_COMMIT_TIMESTAMP")

            timestamp = datetime.fromisoformat(ci_commit_timestamp)
            timestamp = timestamp.astimezone(timezone.utc)
            timestamp = timestamp.strftime("%Y%m%d%H%M%S")

            last_version = get_last_version()

            if not last_version:
                last_version = "0.0.0"

            project_version = f"v{last_version}-0.{timestamp}-{commit_hash}"
        else:
            project_version = f"v{project_version}"

        return project_version
