"""Scripts for Flutter Libs CI"""

import getopt
from pathlib import Path

import yaml
from colorama import Fore

from common import (
    ExitCode,
    exec_command,
    exit_message,
    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 FlutterLibsCI:
    def __init__(self, argv):
        try:
            self.opts, self.args = getopt.getopt(argv, "vpr:x")
        except getopt.GetoptError as err:
            exit_message(err)

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

        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 == "-p":
                self._publish_pubdev()
            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()

        self.extensions.before_build()

        validate_changelog = ValidateChangelog()
        validate_changelog.validate()

        self._exec_flutter_analyze()

        if not self._is_package_private():
            self._publish_pubdev_dry_run()

        self.extensions.after_build()

    def _publish_pubdev(self):
        if self._is_package_private():
            print_message(
                "Biblioteca configurada como privada, não será possível publica-la dessa forma.",
                Fore.YELLOW,
            )
            exit_message(
                "Para mais informações consulte a documentação oficial: https://dart.dev/tools/pub/pubspec#publish_to"
            )

        print(" ")
        print_message(
            "Iniciando a publicação da nova versão da biblioteca",
            Fore.YELLOW,
        )
        print(" ")

        self._config_runtime_pubignore()

        exec_command("cp -r /root/.config/pub/ /root/.config/dart/")

        exec_command("fvm flutter pub publish --force")

        print(" ")
        print_message(
            "Publicação da biblioteca finalizada.",
            Fore.GREEN,
        )

    def _publish_pubdev_dry_run(self):
        print_message(
            "Iniciando a execução da validação da biblioteca",
            Fore.YELLOW,
        )
        print(" ")

        self._config_runtime_pubignore()

        command_result_analyze = exec_command("fvm flutter pub publish --dry-run")

        if command_result_analyze.exit_code == ExitCode.SUCCESS:
            print(" ")
            print_message(
                "Validação da biblioteca finalizada com sucesso.",
                Fore.GREEN,
            )
        else:
            exit_message("Validação da biblioteca finalizada com erro.")

    @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(" ")
        print_message(
            "Iniciando a execução dos testes unitários",
            Fore.YELLOW,
        )
        print(" ")

        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.SUCCESS:
            print(" ")
            print_message(
                "Execução dos testes finalizada com sucesso.",
                Fore.GREEN,
            )
        else:
            exit_message("Execução dos testes finalizada com erro.")

    @staticmethod
    def _release(versioning_release):
        check_and_validate_issues()

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

    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.")

    @staticmethod
    def _config_runtime_pubignore():
        exec_command("touch .pubignore")

        with open(".gitignore", "r", encoding="utf-8") as gitignore_file:
            gitignore_str = gitignore_file.read()

        with open(".pubignore", "a", encoding="utf-8") as pubignore_file:
            pubignore_file.write(f"\n{gitignore_str}\nsenior-ci/\nvenv/")

    @staticmethod
    def _is_package_private():
        """
        Regras segundo a doc

        https://dart.dev/tools/pub/pubspec#publish_to
        """

        publish_to = "https://pub.dev/"

        with open("pubspec.yaml", "r", encoding="utf-8") as stream:
            try:
                pubspec = yaml.safe_load(stream)

                publish_to = pubspec["publish_to"]
            except yaml.YAMLError as exc:
                exit_message(exc)
            except KeyError:
                pass

        return publish_to == "none"

    @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.")
