"""Scripts for Flutter CI"""

import getopt
from enum import Enum
from pathlib import Path

from colorama import Fore

from common import (
    ExitCode,
    exec_command,
    exit_message,
    get_env_variable,
    get_env_variable_required,
    print_message,
)
from common.extensions import Extensions
from common.flutter_helper import run_full_coverage_package
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 Platform(Enum):
    ANDROID = 1
    IOS = 2


class FlutterAppsCI(object):
    def __init__(self, argv):
        try:
            self.opts, self.args = getopt.getopt(
                argv, "vbr:x", ["android", "ios", "flavor=", "target=", "apk"]
            )
        except getopt.GetoptError as err:
            exit_message(err)

        self.platform = None
        self.flavor = None
        self.target = None
        self.build_apk = False

        self._validade_options()
        self._configure_platform()
        self._configure_flavor()
        self.buildable_project = BuildableProject()
        self.extensions = Extensions()

        self.no_tree_shake_icons = get_env_variable("SCI_NO_TREE_SHAKE_ICONS")

        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 exec_ci(self):
        self._install()
        for opt, arg in self.opts:
            if opt == "-v":
                self._validation()
            elif opt == "-b":
                self._build()
            elif opt == "-r":
                self._release(arg)
            elif opt == "-x":
                self._exec_sonar()
            else:
                exit_message("É preciso informar a opção referente a ação escolhida")

    def _validation(self):
        check_and_validate_issues()

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

        self.extensions.before_build()

        validate_changelog = ValidateChangelog()
        validate_changelog.validate()

        self._exec_flutter_analyze()

        self.extensions.after_build()

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

    def _build(self):
        print_message("Executando build")
        pipeline_id = get_env_variable_required("CI_PIPELINE_ID")

        if not self.platform:
            exit_message("É preciso informar a plataforma que deseja realizar o build.")

        self.extensions.before_build()

        string_build = "fvm flutter build"
        if self.platform == Platform.ANDROID:
            string_build += " apk" if self.build_apk else " appbundle"
        else:
            string_build += " ios"

        if self.no_tree_shake_icons:
            string_build += " --no-tree-shake-icons"

        string_build += f" --release --build-number={pipeline_id}"

        if self.flavor and self.target:
            string_build += f" --flavor={self.flavor} --target={self.target}"

        if self.platform == Platform.ANDROID:
            # Busca a Keystore das variáveis de ambiente e salva em um arquivo.
            exec_command(
                "cat $SCI_ANDROID_KEYSTORE_JKS | base64 --decode > ./android/upload-keystore.jks"
            )

        # Executa build
        command_result = exec_command(string_build)

        self.extensions.after_build()

        if command_result.exit_code == ExitCode.SUCCESS:
            print(" ")
            print_message("Build finalizado com sucesso.", Fore.GREEN)
            print(" ")
        else:
            exit_message("Build finalizado com erro, verifique.")

    @staticmethod
    def _release(versioning_release):
        check_and_validate_issues()

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

    @staticmethod
    def _exec_flutter_analyze():
        print(" ")
        print_message(
            "Iniciando a execução da análise estática",
            Fore.YELLOW,
        )
        print(" ")

        command_result_analyze = exec_command(
            "fvm flutter analyze --no-pub --current-package --congratulate --preamble "
            "--no-fatal-infos --no-fatal-warnings --suppress-analytics --write=analyzer-output.txt"
        )

        if command_result_analyze.exit_code == ExitCode.SUCCESS:
            print(" ")
            print_message(
                "Análise estática finalizada com sucesso.",
                Fore.GREEN,
            )
        else:
            exit_message("Análise estática finalizada com erro.")

    @staticmethod
    def _exec_tests():
        print_message("Executando testes")

        run_full_coverage_package()

        command_result_test = exec_command(
            "fvm flutter test --no-pub --reporter=expanded --coverage --suppress-analytics"
        )

        if command_result_test.exit_code == ExitCode.ERROR:
            exit_message("Testes finalizados com erro, verifique.")

    def _exec_sonar(self):
        check_and_validate_issues()

        if not self.buildable_project.skip_build():
            self.extensions.before_build()

            self._exec_flutter_analyze()
            self._exec_tests()

            self.extensions.after_build()

        sonar_helper = SonarHelper(sonar_scanner_type=SonarScannerType.FLUTTER)

        sonar_helper.scanner_analyze()

    def _validade_options(self):
        len_opts = len(self.opts)

        if len_opts == 0:
            exit_message("É preciso informar as opções.")

    def _configure_platform(self):
        str_opts = str(self.opts)
        if "android" in str_opts and "ios" in str_opts:
            exit_message("Somente uma definição de plataforma é necessária.")
        elif "android" in str_opts:
            self.platform = Platform.ANDROID
            self.opts.remove(tuple(("--android", "")))
        elif "ios" in str_opts:
            self.platform = Platform.IOS
            self.opts.remove(tuple(("--ios", "")))
        if "apk" in str_opts:
            self.build_apk = True
            self.opts.remove(tuple(("--apk", "")))

    def _configure_flavor(self):
        for opt, arg in self.opts:
            if opt == "--flavor":
                self.flavor = arg
            if opt == "--target":
                self.target = arg

        if self.flavor and not self.target:
            exit_message("Quando informado o flavor é necessário informar o target.")
        elif not self.flavor and self.target:
            exit_message("Quando informado o target é necessário informar o flavor.")
        elif self.flavor and self.target:
            self.opts.remove(tuple(("--flavor", self.flavor)))
            self.opts.remove(tuple(("--target", self.target)))

    @staticmethod
    def _install():
        fvm_config = Path(".fvm/fvm_config.json")
        fvmrc = Path(".fvmrc")
        if fvm_config.is_file() or fvmrc.is_file():
            exec_command("fvm install")
            exec_command("fvm flutter pub get")
        else:
            exit_message("Arquivo de configuração do FVM não encontrado.")
