import re
from typing import List, Tuple

from colorama import Fore

from common import (
    end_collapsible_section,
    exec_command,
    exit_message,
    get_env_variable_required,
    print_message,
    print_messages,
    start_collapsible_section,
)

special_branches = ["master", "main", "develop", "beta", "alpha"]
RELEASE_PATTERN = r"^(\d+\.\d+|\d+)\.x$"


class ValidateCommitMessage(object):
    def __init__(self) -> None:
        self.ci_commit_ref_name = get_env_variable_required("CI_COMMIT_REF_NAME")

        if self.ci_commit_ref_name.lower() not in special_branches and not re.match(
            RELEASE_PATTERN, self.ci_commit_ref_name
        ):
            try:
                self.branch_type, self.issue_key = self.ci_commit_ref_name.split("/")
            except ValueError:
                exit_message(
                    f"Nome de branch inválido: {self.ci_commit_ref_name}. "
                    "O padrão de desenvolvimento é issue_type/issue_key. "
                    "Por exemplo: feature/devops-19872 ou bugfix/devops-19872."
                )
            self.issue_key = self.issue_key.upper()
            self.skip_validation = False
        else:
            self.branch_type = None
            self.issue_key = None
            self.skip_validation = True

        self.base_branch = ValidateCommitMessage._detect_base_branch()

    @staticmethod
    def _detect_base_branch() -> str:
        for branch in ["master", "main"]:
            try:
                exec_command(f"git rev-parse --verify origin/{branch}")
                return f"origin/{branch}"
            except Exception:
                continue
        exit_message(
            "Não foi possível detectar a branch base (origin/master ou origin/main)."
        )

    @staticmethod
    def get_commit_messages() -> List[Tuple[str, str]]:
        try:
            exec_command("git fetch origin")
        except Exception as e:
            exit_message(f"Erro ao fazer fetch: {str(e)}")

        try:
            result = exec_command(
                "git log origin/master..HEAD --pretty=format:"
                '"COMMIT:%n  SHA: %H%n  MSG:%n%B%n-------------------------------"',
            )

        except Exception as e:
            exit_message(f"Erro ao obter mensagens de commit: {str(e)}")

        output = result.output
        commits_raw = output.split("===COMMIT===")
        commits = []

        for commit in commits_raw:
            lines = commit.strip().split("\n")
            if len(lines) >= 2:
                sha = lines[0]
                message = "\n".join(lines[1:])
                commits.append((sha, message))

        return commits

    def validate(self):
        section_id = start_collapsible_section("Validando commits da branch", Fore.CYAN)

        if self.branch_type not in ["feature", "bugfix", "feat", "fix"]:
            print_messages(
                [
                    "Validação de issue não é necessária.",
                ],
                Fore.YELLOW,
            )

            end_collapsible_section(section_id)
            return

        commits = self.get_commit_messages()
        if not commits:
            print_message(
                "Nenhum commit encontrado na branch. Nada para validar.", Fore.YELLOW
            )
            end_collapsible_section(section_id)
            return

        pattern = re.compile(
            rf"AFFECTED ISSUES:.*#{re.escape(self.issue_key)}(\b|[^0-9A-Z-])",
            re.IGNORECASE,
        )

        for sha, message in commits:
            if pattern.search(message):
                print_message(
                    f"Commit {sha[:7]} contém a referência 'AFFECTED ISSUES: #{self.issue_key}'. Validação concluída.",
                    Fore.GREEN,
                )
                end_collapsible_section(section_id)
                return

        print_message(
            "Nenhum commit contém a referência obrigatória à issue:", Fore.RED
        )
        print_message(
            "É necessário que ao menos um commit contenha a referência da branch. "
            "Use o prompt do Lefthook ao realizar commits e garantir que o padrão seja seguido corretamente."
            "Saiba mais: https://wiki.senior.com.br/pt-br/DevSecOps/Semantic-Release#onde-devo-referencias-as-issues",
            Fore.RED,
        )
        exit_message(
            f"Certifique-se de que a chave da issue usada nos commits corresponda à usada no nome da branch.\n"
            f"Issue esperada: #{self.issue_key}."
        )
