# [DEF:backend.src.services.clean_release.stages.internal_sources_only:Module]
# @TIER: STANDARD
# @SEMANTICS: clean-release, compliance-stage, source-isolation, registry
# @PURPOSE: Verify manifest-declared sources belong to trusted internal registry allowlist.
# @LAYER: Domain
# @RELATION: IMPLEMENTS -> backend.src.services.clean_release.stages.base.ComplianceStage
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.stages.base
# @INVARIANT: Any source host outside allowed_hosts yields BLOCKED decision with at least one violation.

from __future__ import annotations

from ....core.logger import belief_scope, logger
from ..enums import ComplianceDecision, ComplianceStageName, ViolationSeverity
from .base import ComplianceStageContext, StageExecutionResult, build_violation


# [DEF:InternalSourcesOnlyStage:Class]
# @PURPOSE: Enforce internal-source-only policy from trusted registry snapshot.
# @PRE: context.registry.allowed_hosts is available.
# @POST: Returns PASSED when all hosts are allowed; otherwise BLOCKED and violations captured.
class InternalSourcesOnlyStage:
    stage_name = ComplianceStageName.INTERNAL_SOURCES_ONLY

    def execute(self, context: ComplianceStageContext) -> StageExecutionResult:
        with belief_scope("InternalSourcesOnlyStage.execute"):
            allowed_hosts = {str(host).strip().lower() for host in (context.registry.allowed_hosts or [])}
            sources = context.manifest.content_json.get("sources", [])
            violations = []

            logger.reason(
                f"Internal sources evaluation run={context.run.id} sources={len(sources)} allowlist={len(allowed_hosts)}"
            )

            for source in sources:
                host = str(source.get("host", "")).strip().lower() if isinstance(source, dict) else ""
                if not host or host in allowed_hosts:
                    continue

                violations.append(
                    build_violation(
                        run_id=context.run.id,
                        stage_name=self.stage_name,
                        code="SOURCE_HOST_NOT_ALLOWED",
                        message=f"Source host '{host}' is not in trusted internal registry",
                        artifact_path=str(source.get("path", "")) if isinstance(source, dict) else None,
                        severity=ViolationSeverity.CRITICAL,
                        evidence_json={
                            "host": host,
                            "allowed_hosts": sorted(allowed_hosts),
                            "manifest_id": context.manifest.id,
                        },
                        blocked_release=True,
                    )
                )

            if violations:
                return StageExecutionResult(
                    decision=ComplianceDecision.BLOCKED,
                    details_json={
                        "source_count": len(sources),
                        "violations_count": len(violations),
                    },
                    violations=violations,
                )

            return StageExecutionResult(
                decision=ComplianceDecision.PASSED,
                details_json={
                    "source_count": len(sources),
                    "violations_count": 0,
                },
                violations=[],
            )
# [/DEF:InternalSourcesOnlyStage:Class]

# [/DEF:backend.src.services.clean_release.stages.internal_sources_only:Module]