# #region ComplianceStages [C:5] [TYPE Module] [SEMANTICS clean-release, compliance, stage, package]
# @BRIEF Define compliance stage order and helper functions for deterministic run-state evaluation.
# @LAYER Domain
# @RELATION DEPENDS_ON -> [CleanReleaseModels]
# @RELATION DEPENDS_ON -> [ComplianceStageBase]
# @INVARIANT  Stage order remains deterministic for all compliance runs.
# @SIDE_EFFECT  Registers compliance stages
# @DATA_CONTRACT  StagePipeline -> ComplianceResult

from __future__ import annotations

from collections.abc import Iterable
from typing import Dict, List

from ....models.clean_release import (
    CheckFinalStatus,
    CheckStageResult,
    CheckStageStatus,
    ComplianceStageRun,
)
from ..enums import ComplianceDecision, ComplianceStageName
from .base import ComplianceStage
from .data_purity import DataPurityStage
from .internal_sources_only import InternalSourcesOnlyStage
from .manifest_consistency import ManifestConsistencyStage
from .no_external_endpoints import NoExternalEndpointsStage

MANDATORY_STAGE_ORDER: list[ComplianceStageName] = [
    ComplianceStageName.DATA_PURITY,
    ComplianceStageName.INTERNAL_SOURCES_ONLY,
    ComplianceStageName.NO_EXTERNAL_ENDPOINTS,
    ComplianceStageName.MANIFEST_CONSISTENCY,
]


# #region build_default_stages [TYPE Function]
# @BRIEF Build default deterministic stage pipeline implementation order.
# @PRE  None.
# @POST  Returns stage instances in mandatory execution order.
def build_default_stages() -> list[ComplianceStage]:
    return [
        DataPurityStage(),
        InternalSourcesOnlyStage(),
        NoExternalEndpointsStage(),
        ManifestConsistencyStage(),
    ]


# #endregion build_default_stages


# #region stage_result_map [TYPE Function]
# @BRIEF Convert stage result list to dictionary by stage name.
# @PRE  stage_results may be empty or contain unique stage names.
# @POST  Returns stage->status dictionary for downstream evaluation.
def stage_result_map(
    stage_results: Iterable[ComplianceStageRun | CheckStageResult],
) -> dict[ComplianceStageName, CheckStageStatus]:
    normalized: dict[ComplianceStageName, CheckStageStatus] = {}
    for result in stage_results:
        if isinstance(result, CheckStageResult):
            normalized[ComplianceStageName(result.stage.value)] = CheckStageStatus(
                result.status.value
            )
            continue

        stage_name = getattr(result, "stage_name", None)
        decision = getattr(result, "decision", None)
        status = getattr(result, "status", None)

        if not stage_name:
            continue

        normalized_stage = ComplianceStageName(stage_name)
        if decision == ComplianceDecision.BLOCKED:
            normalized[normalized_stage] = CheckStageStatus.FAIL
        elif decision == ComplianceDecision.ERROR:
            normalized[normalized_stage] = CheckStageStatus.SKIPPED
        elif decision == ComplianceDecision.PASSED:
            normalized[normalized_stage] = CheckStageStatus.PASS
        elif decision:
            normalized[normalized_stage] = CheckStageStatus(str(decision))
        elif status:
            normalized[normalized_stage] = CheckStageStatus(str(status))
    return normalized


# #endregion stage_result_map


# #region missing_mandatory_stages [TYPE Function]
# @BRIEF Identify mandatory stages that are absent from run results.
# @PRE  stage_status_map contains zero or more known stage statuses.
# @POST  Returns ordered list of missing mandatory stages.
def missing_mandatory_stages(
    stage_status_map: dict[ComplianceStageName, CheckStageStatus],
) -> list[ComplianceStageName]:
    return [stage for stage in MANDATORY_STAGE_ORDER if stage not in stage_status_map]


# #endregion missing_mandatory_stages


# #region derive_final_status [TYPE Function]
# @BRIEF Derive final run status from stage results with deterministic blocking behavior.
# @PRE  Stage statuses correspond to compliance checks.
# @POST  Returns one of PASSED/BLOCKED/ERROR according to mandatory stage outcomes.
def derive_final_status(
    stage_results: Iterable[ComplianceStageRun | CheckStageResult],
) -> CheckFinalStatus:
    status_map = stage_result_map(stage_results)
    missing = missing_mandatory_stages(status_map)
    if missing:
        return CheckFinalStatus.FAILED

    for stage in MANDATORY_STAGE_ORDER:
        decision = status_map.get(stage)
        if decision == CheckStageStatus.SKIPPED:
            return CheckFinalStatus.FAILED
        if decision == CheckStageStatus.FAIL:
            return CheckFinalStatus.BLOCKED

    return CheckFinalStatus.COMPLIANT


# #endregion derive_final_status
# #endregion ComplianceStages
