# [DEF:backend.src.services.clean_release.stages:Module]
# @TIER: STANDARD
# @SEMANTICS: clean-release, compliance, stages, state-machine
# @PURPOSE: Define compliance stage order and helper functions for deterministic run-state evaluation.
# @LAYER: Domain
# @RELATION: DEPENDS_ON -> backend.src.models.clean_release
# @INVARIANT: Stage order remains deterministic for all compliance runs.

from __future__ import annotations

from typing import Dict, Iterable, List

from ..enums import ComplianceDecision, ComplianceStageName
from ....models.clean_release import ComplianceStageRun
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,
]


# [DEF:build_default_stages:Function]
# @PURPOSE: 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(),
    ]
# [/DEF:build_default_stages:Function]


# [DEF:stage_result_map:Function]
# @PURPOSE: 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]) -> Dict[ComplianceStageName, ComplianceDecision]:
    return {ComplianceStageName(result.stage_name): ComplianceDecision(result.decision) for result in stage_results if result.decision}
# [/DEF:stage_result_map:Function]


# [DEF:missing_mandatory_stages:Function]
# @PURPOSE: 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, ComplianceDecision]) -> List[ComplianceStageName]:
    return [stage for stage in MANDATORY_STAGE_ORDER if stage not in stage_status_map]
# [/DEF:missing_mandatory_stages:Function]


# [DEF:derive_final_status:Function]
# @PURPOSE: 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]) -> ComplianceDecision:
    status_map = stage_result_map(stage_results)
    missing = missing_mandatory_stages(status_map)
    if missing:
        return ComplianceDecision.ERROR

    for stage in MANDATORY_STAGE_ORDER:
        decision = status_map.get(stage)
        if decision == ComplianceDecision.ERROR:
            return ComplianceDecision.ERROR
        if decision == ComplianceDecision.BLOCKED:
            return ComplianceDecision.BLOCKED

    return ComplianceDecision.PASSED
# [/DEF:derive_final_status:Function]
# [/DEF:backend.src.services.clean_release.stages:Module]