from datetime import datetime
from typing import List

from colorama import Fore

from common import (
    end_collapsible_section,
    exit_message,
    get_env_variable,
    get_env_variable_required,
    print_message,
    start_collapsible_section,
)
from common.graphql_client import GraphqlClient
from common.validations.issues.check_deprecated_primitives import (
    check_deprecated_primitives,
)
from common.validations.issues.check_platform_version import check_platform_version
from common.validations.issues.check_project_health import check_project_health
from common.validations.issues.check_project_path import check_project_path
from common.validations.issues.check_sonar_config_files import check_config_files
from common.validations.issues.check_tags import check_tags
from common.validations.issues.custom_issue import CustomIssue


class ProjectIssuesStatus(object):
    """Entity of a Project Issues Status"""

    should_break_job: bool
    issues: List[CustomIssue]

    def __init__(self, client_graphql: GraphqlClient = GraphqlClient()) -> None:
        self.client_graphql = client_graphql
        self._get_project_issue_status()

    def show_status(self):
        """Print the status of a job and/or project"""

        section_id = start_collapsible_section(
            "Validando existência de alguma pendência no projeto.", Fore.CYAN
        )

        if self.issues:
            print_message(
                "Existem algumas pendências a serem resolvidas, segue relação:",
            )

            for issue in self._get_required_issues():
                must_be_fixed_until = issue.must_be_fixed_until
                description = issue.description

                date_to_fix_message = None

                if must_be_fixed_until:
                    date_to_fix = issue.must_be_fixed_until.strftime("%d/%m/%Y")

                    date_to_fix_message = (
                        f"Você tem até o dia {date_to_fix} para corrigir as inconsistências, "
                        "após essa data as pipelines serão bloqueadas até a correção."
                    )

                print_message(
                    f"{description} {date_to_fix_message if date_to_fix_message else ''}",
                    Fore.RED if self.should_break_job else Fore.LIGHTYELLOW_EX,
                )

            if self.should_break_job:
                exit_message(
                    "O job foi impedido de continuar devido as pendências acima. :x"
                )

            print(" ")

            print_message(
                "Nenhuma das pendências acima quebrará este job, "
                "porém é importante ajustá-las antes que se tornem impeditivas ;)",
                Fore.YELLOW,
            )

        else:
            print_message("Nenhuma pendência encontrada, parabéns!", Fore.GREEN)

        end_collapsible_section(section_id)

    def _get_required_issues(self):
        self.issues.sort(
            key=lambda issue: (
                issue.must_be_fixed_until
                if issue.must_be_fixed_until
                else datetime.now()
            )
        )

        required_issues = self.issues

        if self.should_break_job:
            required_issues = list(
                filter(lambda issue: issue.must_be_fixed_until is None, self.issues)
            )

            if not required_issues:
                required_issues = list(
                    filter(
                        lambda issue: issue.must_be_fixed_until < datetime.now(),
                        self.issues,
                    )
                )

        return required_issues

    def _get_project_issue_status(self):
        query = """
            query ($projectId: Int!, $jobStage: String, $commitSha: String) {
                projectIssuesStatus(
                    projectId: $projectId,
                    jobStage: $jobStage,
                    commitSha: $commitSha
                ) {
                    shouldBreakJob
                    issues {
                        rule
                        mustBeFixedUntil
                        description
                        notificationText
                    }
                }
            }
        """

        params = {
            "projectId": int(get_env_variable_required("CI_PROJECT_ID")),
            "jobStage": get_env_variable_required("CI_JOB_STAGE"),
            "commitSha": get_env_variable_required("CI_COMMIT_SHA"),
        }

        result = self.client_graphql.call(query, params)

        project_issues_status_result = result["projectIssuesStatus"]

        self.convert_from_json(project_issues_status_result)

    def convert_from_json(self, payload):
        """Convert a payload to this class"""

        self.should_break_job = payload["shouldBreakJob"]
        self.issues = list(
            map(
                lambda issueResult: CustomIssue().convert_from_json(issueResult),
                payload["issues"],
            )
        )

        return self


def check_and_validate_issues():
    skip_check_custom_issue = get_env_variable("SKIP_CHECK_CUSTOM_ISSUE")
    if not skip_check_custom_issue:
        check_project_path()

        check_project_health()

        check_config_files()

        check_tags()

        check_deprecated_primitives()

        check_platform_version()

        skip_issue_validate = get_env_variable("SKIP_ISSUE_VALIDATE")

        if not skip_issue_validate:
            graphql_client = GraphqlClient()
            project_issues_status = ProjectIssuesStatus(graphql_client)
            project_issues_status.show_status()
