# #region no_external_endpoints [C:5] [TYPE Module] [SEMANTICS clean-release, endpoint, validate, manifest, compliance]
# @BRIEF Block manifest payloads that expose external endpoints outside trusted schemes and hosts.
# @LAYER Domain
# @RELATION IMPLEMENTS -> [ComplianceStage]
# @RELATION DEPENDS_ON -> [ComplianceStageBase]
# @INVARIANT  Endpoint outside allowed scheme/host always yields BLOCKED stage decision.
# @SIDE_EFFECT  None (read-only validation)
# @DATA_CONTRACT  Endpoints -> EndpointViolationReport

from __future__ import annotations

from urllib.parse import urlparse

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


# #region NoExternalEndpointsStage [TYPE Class]
# @BRIEF Validate endpoint references from manifest against trusted registry.
# @PRE  context.registry includes allowed hosts and schemes.
# @POST  Returns PASSED when all endpoints are trusted, otherwise BLOCKED with endpoint violations.
class NoExternalEndpointsStage:
    stage_name = ComplianceStageName.NO_EXTERNAL_ENDPOINTS

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

            logger.reason(
                f"Endpoint isolation evaluation run={context.run.id} endpoints={len(endpoints)} "
                f"allowed_hosts={len(allowed_hosts)} allowed_schemes={len(allowed_schemes)}"
            )

            for endpoint in endpoints:
                raw = str(endpoint).strip()
                if not raw:
                    continue
                parsed = urlparse(raw)
                host = (parsed.hostname or "").lower()
                scheme = (parsed.scheme or "").lower()

                if host in allowed_hosts and scheme in allowed_schemes:
                    continue

                violations.append(
                    build_violation(
                        run_id=context.run.id,
                        stage_name=self.stage_name,
                        code="EXTERNAL_ENDPOINT_DETECTED",
                        message=f"Endpoint '{raw}' is outside trusted internal network boundary",
                        artifact_path=None,
                        severity=ViolationSeverity.CRITICAL,
                        evidence_json={
                            "endpoint": raw,
                            "host": host,
                            "scheme": scheme,
                            "allowed_hosts": sorted(allowed_hosts),
                            "allowed_schemes": sorted(allowed_schemes),
                        },
                        blocked_release=True,
                    )
                )

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

            return StageExecutionResult(
                decision=ComplianceDecision.PASSED,
                details_json={"endpoint_count": len(endpoints), "violations_count": 0},
                violations=[],
            )


# #endregion NoExternalEndpointsStage

# #endregion no_external_endpoints
