"""Publication gate for the Mais Humana MCP provider.

This module closes the local part of the service orders that depend on the
MCPs Internos gateway.  It does not deploy the Worker; deployment remains a
Wrangler-only operation.  Instead, it gives the round a deterministic,
secret-safe acceptance package that answers four practical questions:

* is the provider implemented in the local MCP gateway code?
* did Wrangler authenticate and can this runner deploy?
* do the live /v1/execute probes still return tool_not_found?
* is the canonical name policy explicit enough to avoid unsafe renames?
"""

from __future__ import annotations

import csv
import io
import json
import re
import urllib.error
import urllib.request
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import Any, Mapping, Sequence

from .mcp_contract import (
    CANONICAL_PROJECT_ID,
    CURRENT_PROJECT_ID,
    MCP_CONTROL_PLANE_ID,
    MCP_EXECUTE_ENDPOINT,
    MCP_TRANSIT_FIELDS,
    PROVIDER_ID,
    PROVIDER_TOOL_ID,
    stable_hash,
)
from .identity_policy import (
    CANONICAL_COMPATIBILITY_RULE,
    CANONICAL_DECISION_STATUS,
    LEGACY_PROJECT_IDS,
)
from .models import GeneratedFile, as_plain_data, merge_unique, slugify, utc_now
from .redaction import redact_sensitive_text


DEFAULT_GATEWAY_TOOLS = (
    "mais_humana.rulebook.compact",
    "mais_humana.admin_ui.same_source",
    "mais_humana.mcp_transit.ledger",
    "mais_humana.admin_routes.acceptance",
)

EXPECTED_GATEWAY_SNIPPETS = (
    "const MAIS_HUMANA_PROVIDER_ID",
    "MAIS_HUMANA_CATALOG",
    "executeMaisHumanaGatewayTool",
    "mais_humana.rulebook.compact",
    "mais_humana.admin_ui.same_source",
    "mais_humana.mcp_transit.ledger",
    "mais_humana.admin_routes.acceptance",
)

DEFAULT_OWNER_PLATFORM_ID = "tudo-para-ia-mais-humana-platform"
LEGACY_ALIAS_PLATFORM_ID = "tudo-para-ia-mais-humana-plataform"


class GateStatus(str, Enum):
    """Status of one publication gate."""

    PASSED = "passed"
    PARTIAL = "partial"
    BLOCKED = "blocked"
    NOT_RUN = "not_run"


class ProbeStatus(str, Enum):
    """HTTP probe result class."""

    OK = "ok"
    TOOL_NOT_FOUND = "tool_not_found"
    HTTP_ERROR = "http_error"
    NETWORK_ERROR = "network_error"
    NOT_RUN = "not_run"


@dataclass(frozen=True, slots=True)
class GatewaySourceEvidence:
    """Evidence extracted from the local MCP gateway source tree."""

    mcp_repo_root: str
    gateway_manager_path: str
    managed_catalog_path: str
    managed_provider_manager_path: str
    present_snippets: tuple[str, ...]
    missing_snippets: tuple[str, ...]
    provider_id: str
    owner_platform_id: str
    tool_ids: tuple[str, ...]
    tests_path: str
    tests_present: bool
    source_hash: str

    @property
    def ready(self) -> bool:
        return not self.missing_snippets and self.tests_present

    def to_dict(self) -> dict[str, Any]:
        return as_plain_data(self)


@dataclass(frozen=True, slots=True)
class WranglerRunnerEvidence:
    """Evidence from direct Wrangler execution."""

    attempted: bool
    version: str
    account_name: str
    account_id: str
    authenticated: bool
    deploy_dry_run_ok: bool
    command_status: Mapping[str, str]
    blockers: tuple[str, ...]
    raw_summary: str

    @property
    def status(self) -> GateStatus:
        if not self.attempted:
            return GateStatus.NOT_RUN
        if self.authenticated and self.deploy_dry_run_ok:
            return GateStatus.PASSED
        if self.authenticated:
            return GateStatus.PARTIAL
        return GateStatus.BLOCKED

    def to_dict(self) -> dict[str, Any]:
        return as_plain_data(self)


@dataclass(frozen=True, slots=True)
class LiveToolProbe:
    """One safe HTTP probe against /v1/execute."""

    tool_id: str
    endpoint: str
    http_status: int | None
    status: ProbeStatus
    ok: bool
    error_code: str
    trace_id: str
    audit_id: str
    evidence_id: str
    source_payload_hash: str
    source_records_hash: str
    transit_fields_present: tuple[str, ...]
    missing_transit_fields: tuple[str, ...]
    response_excerpt: Mapping[str, Any]
    observed_at: str

    @property
    def live_ready(self) -> bool:
        return self.status == ProbeStatus.OK and self.ok and not self.missing_transit_fields

    def to_dict(self) -> dict[str, Any]:
        return as_plain_data(self)


@dataclass(frozen=True, slots=True)
class AliasPolicyEvidence:
    """Canonical name and alias policy for the platform."""

    current_project_id: str
    canonical_project_id: str
    owner_platform_id: str
    legacy_aliases: tuple[str, ...]
    central_folder: str
    repo_remote: str
    decision_status: str
    migration_safe_now: bool
    compatibility_rule: str
    blockers: tuple[str, ...]

    @property
    def status(self) -> GateStatus:
        if self.decision_status == "approved":
            return GateStatus.PASSED
        if self.migration_safe_now:
            return GateStatus.PARTIAL
        return GateStatus.BLOCKED

    def to_dict(self) -> dict[str, Any]:
        return as_plain_data(self)


@dataclass(frozen=True, slots=True)
class PublicationGateDecision:
    """Decision for one active service order."""

    order_id: str
    status: GateStatus
    reason: str
    evidence_refs: tuple[str, ...]
    next_action: str

    def to_dict(self) -> dict[str, Any]:
        return as_plain_data(self)


@dataclass(frozen=True, slots=True)
class McpPublicationGateReport:
    """Full publication gate report for Mais Humana."""

    report_id: str
    generated_at: str
    provider_id: str
    current_project_id: str
    canonical_project_id: str
    control_plane_id: str
    gateway_source: GatewaySourceEvidence
    wrangler_runner: WranglerRunnerEvidence
    live_probes: tuple[LiveToolProbe, ...]
    alias_policy: AliasPolicyEvidence
    decisions: tuple[PublicationGateDecision, ...]
    summary: tuple[str, ...]
    blockers: tuple[str, ...]

    @property
    def live_ready(self) -> bool:
        return bool(self.live_probes) and all(item.live_ready for item in self.live_probes)

    @property
    def local_ready(self) -> bool:
        return self.gateway_source.ready

    @property
    def deploy_ready(self) -> bool:
        return self.wrangler_runner.deploy_dry_run_ok

    @property
    def status(self) -> GateStatus:
        if self.local_ready and self.live_ready and self.deploy_ready and not self.blockers:
            return GateStatus.PASSED
        if self.local_ready:
            return GateStatus.PARTIAL
        return GateStatus.BLOCKED

    def to_dict(self) -> dict[str, Any]:
        data = as_plain_data(self)
        data["status"] = self.status.value
        data["liveReady"] = self.live_ready
        data["localReady"] = self.local_ready
        data["deployReady"] = self.deploy_ready
        return data


def _read_text(path: Path, limit_chars: int = 2_000_000) -> str:
    try:
        text = path.read_text(encoding="utf-8", errors="ignore")
    except OSError:
        return ""
    if len(text) <= limit_chars:
        return text
    return text[:limit_chars]


def _extract_first(pattern: str, text: str, default: str = "") -> str:
    match = re.search(pattern, text, flags=re.I | re.M)
    if not match:
        return default
    return match.group(1).strip()


def _bounded_limit(value: int, maximum: int) -> int:
    return max(1, min(maximum, int(value)))


def _safe_excerpt(value: object, *, max_items: int = 16) -> dict[str, Any]:
    """Return a compact response excerpt without leaking raw secret-shaped text."""

    if isinstance(value, Mapping):
        output: dict[str, Any] = {}
        for index, (key, inner) in enumerate(value.items()):
            if index >= max_items:
                output["__truncated__"] = True
                break
            output[str(key)] = _safe_excerpt(inner, max_items=max_items) if isinstance(inner, Mapping) else redact_sensitive_text(str(inner))[:600]
        return output
    if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
        return {str(index): _safe_excerpt(item, max_items=max_items) for index, item in enumerate(value[:max_items])}
    return {"value": redact_sensitive_text(str(value))[:600]}


def _transit_fields(payload: Mapping[str, Any]) -> tuple[tuple[str, ...], tuple[str, ...]]:
    result = payload.get("result")
    result_map = result if isinstance(result, Mapping) else {}
    transit = result_map.get("transit")
    transit_map = transit if isinstance(transit, Mapping) else {}
    present = tuple(
        field
        for field in MCP_TRANSIT_FIELDS
        if field in transit_map and transit_map[field] not in ("", None, {}, [])
    )
    missing = tuple(field for field in MCP_TRANSIT_FIELDS if field not in present)
    return present, missing


def _status_from_http(status: int | None, payload: Mapping[str, Any]) -> ProbeStatus:
    if status is None:
        return ProbeStatus.NETWORK_ERROR
    error_code = str(payload.get("error") or payload.get("code") or "").strip()
    nested_error = payload.get("error")
    if isinstance(nested_error, Mapping):
        error_code = str(nested_error.get("code") or nested_error.get("error") or error_code).strip()
    if error_code == "tool_not_found":
        return ProbeStatus.TOOL_NOT_FOUND
    if 200 <= status < 300 and payload.get("ok") is not False:
        return ProbeStatus.OK
    return ProbeStatus.HTTP_ERROR


def scan_gateway_source(mcp_repo_root: Path) -> GatewaySourceEvidence:
    """Scan local MCP gateway source for Mais Humana publication hooks."""

    manager = mcp_repo_root / "deploy" / "mcps-gateway" / "src" / "managers" / "gpt-tooling-manager.ts"
    catalog = mcp_repo_root / "deploy" / "mcps-gateway" / "src" / "providers" / "managed-catalog.ts"
    managed = mcp_repo_root / "deploy" / "mcps-gateway" / "src" / "managers" / "managed-provider-manager.ts"
    tests = mcp_repo_root / "tests" / "platform" / "mcps-gateway-mais-humana-provider.test.ts"
    combined = "\n".join(_read_text(path) for path in (manager, catalog, managed, tests))
    present = tuple(snippet for snippet in EXPECTED_GATEWAY_SNIPPETS if snippet in combined)
    missing = tuple(snippet for snippet in EXPECTED_GATEWAY_SNIPPETS if snippet not in combined)
    owner = _extract_first(r"maisHumana:\s*\"([^\"]+)\"", _read_text(manager), DEFAULT_OWNER_PLATFORM_ID)
    tool_ids = tuple(tool_id for tool_id in DEFAULT_GATEWAY_TOOLS if tool_id in combined)
    source_hash = stable_hash(
        {
            "manager": str(manager),
            "catalog": str(catalog),
            "managed": str(managed),
            "tests": str(tests),
            "present": present,
            "missing": missing,
            "toolIds": tool_ids,
            "owner": owner,
        }
    )
    return GatewaySourceEvidence(
        mcp_repo_root=str(mcp_repo_root),
        gateway_manager_path=str(manager),
        managed_catalog_path=str(catalog),
        managed_provider_manager_path=str(managed),
        present_snippets=present,
        missing_snippets=missing,
        provider_id=PROVIDER_ID,
        owner_platform_id=owner,
        tool_ids=tool_ids,
        tests_path=str(tests),
        tests_present=tests.exists() and "Mais Humana tools are published" in _read_text(tests),
        source_hash=source_hash,
    )


def build_wrangler_runner_evidence(
    raw_summary: str,
    *,
    attempted: bool = True,
) -> WranglerRunnerEvidence:
    """Parse safe Wrangler output captured by the caller."""

    clean = redact_sensitive_text(raw_summary or "")
    version = _extract_first(r"wrangler\s+([0-9]+\.[0-9]+\.[0-9]+)", clean)
    if not version:
        version = _extract_first(r"^([0-9]+\.[0-9]+\.[0-9]+)$", clean)
    account_id = _extract_first(r"([0-9a-f]{32})", clean)
    account_name = "PARA-IA" if "PARA-IA" in clean else ""
    lower_clean = clean.lower()
    authenticated = (
        "You are logged in" in clean
        or "Account API Token" in clean
        or bool(account_id)
        or ("whoami ok" in lower_clean and "para-ia" in lower_clean)
        or "wrangler direto autenticado" in lower_clean
        or "wrangler autenticado" in lower_clean
    )
    spawn_blocked = "spawn EPERM" in clean or "Assertion failed" in clean
    deploy_dry_run_ok = "Total Upload" in clean or "Uploaded" in clean or ("Successfully" in clean and "dry" in lower_clean)
    command_status = {
        "wrangler_version": "observed" if version else ("blocked" if spawn_blocked else "unknown"),
        "wrangler_whoami": "authenticated" if authenticated else ("blocked" if spawn_blocked else "unknown"),
        "wrangler_deploy_dry_run": "ok" if deploy_dry_run_ok else ("blocked_spawn_eperm" if spawn_blocked else "not_confirmed"),
    }
    blockers: list[str] = []
    if spawn_blocked:
        blockers.append("runner_node_esbuild_spawn_eperm")
    if attempted and not authenticated:
        blockers.append("wrangler_auth_not_confirmed")
    if authenticated and not deploy_dry_run_ok:
        blockers.append("wrangler_deploy_dry_run_not_confirmed")
    return WranglerRunnerEvidence(
        attempted=attempted,
        version=version,
        account_name=account_name,
        account_id=account_id,
        authenticated=authenticated,
        deploy_dry_run_ok=deploy_dry_run_ok,
        command_status=command_status,
        blockers=tuple(blockers),
        raw_summary=clean[-4000:],
    )


def execute_live_tool_probe(
    tool_id: str,
    *,
    endpoint: str = MCP_EXECUTE_ENDPOINT,
    bearer: str = "",
    timeout_seconds: int = 20,
    limit: int = 3,
) -> LiveToolProbe:
    """Execute one safe /v1/execute probe and redact the result."""

    body = {
        "toolId": tool_id,
        "input": {
            "limit": _bounded_limit(limit, 20),
            "actorId": "codex.service-order-round",
            "permission": "mcp.admin.readonly",
        },
    }
    encoded = json.dumps(body, ensure_ascii=False).encode("utf-8")
    headers = {
        "Content-Type": "application/json",
        "User-Agent": "Codex-Mais-Humana-MCP-Publication-Gate/1.0",
    }
    if bearer:
        headers["Authorization"] = f"Bearer {bearer}"
    request = urllib.request.Request(endpoint, data=encoded, headers=headers, method="POST")
    status: int | None = None
    payload: Mapping[str, Any] = {}
    try:
        with urllib.request.urlopen(request, timeout=timeout_seconds) as response:
            status = int(response.status)
            text = response.read().decode("utf-8", errors="replace")
    except urllib.error.HTTPError as exc:
        status = int(exc.code)
        text = exc.read().decode("utf-8", errors="replace")
    except OSError as exc:
        text = json.dumps({"error": "network_error", "message": str(exc)}, ensure_ascii=False)
    try:
        parsed = json.loads(text) if text.strip() else {}
        if isinstance(parsed, Mapping):
            payload = parsed
        else:
            payload = {"value": parsed}
    except json.JSONDecodeError:
        payload = {"raw": redact_sensitive_text(text[:1200])}
    probe_status = _status_from_http(status, payload)
    result = payload.get("result")
    result_map = result if isinstance(result, Mapping) else {}
    error_value = payload.get("error")
    if isinstance(error_value, Mapping):
        error_code = str(error_value.get("code") or error_value.get("error") or "").strip()
    else:
        error_code = str(error_value or payload.get("code") or "").strip()
    source_payload_hash = stable_hash({"endpoint": endpoint, "request": body, "status": status})
    source_records_hash = stable_hash({"toolId": tool_id, "payload": _safe_excerpt(payload), "status": probe_status.value})
    transit_present, transit_missing = _transit_fields(payload)
    return LiveToolProbe(
        tool_id=tool_id,
        endpoint=endpoint,
        http_status=status,
        status=probe_status,
        ok=bool(payload.get("ok") is True or result_map.get("ok") is True),
        error_code=error_code,
        trace_id=str(result_map.get("traceId") or result_map.get("trace_id") or f"trace-{source_payload_hash[:24]}"),
        audit_id=str(result_map.get("auditId") or result_map.get("audit_id") or f"audit-{source_records_hash[:24]}"),
        evidence_id=f"evidence-{source_records_hash[:24]}",
        source_payload_hash=source_payload_hash,
        source_records_hash=source_records_hash,
        transit_fields_present=transit_present,
        missing_transit_fields=transit_missing,
        response_excerpt=_safe_excerpt(payload),
        observed_at=utc_now(),
    )


def build_not_run_probe(tool_id: str, reason: str, *, endpoint: str = MCP_EXECUTE_ENDPOINT) -> LiveToolProbe:
    """Build a not-run probe when live HTTP is intentionally skipped."""

    source_payload_hash = stable_hash({"endpoint": endpoint, "toolId": tool_id, "notRun": reason})
    source_records_hash = stable_hash({"status": "not_run", "reason": reason, "toolId": tool_id})
    return LiveToolProbe(
        tool_id=tool_id,
        endpoint=endpoint,
        http_status=None,
        status=ProbeStatus.NOT_RUN,
        ok=False,
        error_code="not_run",
        trace_id=f"trace-{source_payload_hash[:24]}",
        audit_id=f"audit-{source_records_hash[:24]}",
        evidence_id=f"evidence-{source_records_hash[:24]}",
        source_payload_hash=source_payload_hash,
        source_records_hash=source_records_hash,
        transit_fields_present=(),
        missing_transit_fields=MCP_TRANSIT_FIELDS,
        response_excerpt={"reason": reason},
        observed_at=utc_now(),
    )


def build_alias_policy(
    *,
    central_platform_folder: Path | None = None,
    repo_remote: str = "",
    decision_status: str = CANONICAL_DECISION_STATUS,
) -> AliasPolicyEvidence:
    """Build the canonical-name policy without renaming files or remotes."""

    central_folder = str(central_platform_folder or "")
    blockers: list[str] = []
    if decision_status != "approved":
        blockers.append("canonical_name_requires_institutional_decision")
    if not repo_remote:
        blockers.append("repo_remote_not_confirmed")
    migration_safe_now = decision_status == "approved" and bool(repo_remote) and CANONICAL_PROJECT_ID in repo_remote
    return AliasPolicyEvidence(
        current_project_id=CURRENT_PROJECT_ID,
        canonical_project_id=CANONICAL_PROJECT_ID,
        owner_platform_id=DEFAULT_OWNER_PLATFORM_ID,
        legacy_aliases=tuple(LEGACY_PROJECT_IDS),
        central_folder=central_folder,
        repo_remote=repo_remote,
        decision_status=decision_status,
        migration_safe_now=migration_safe_now,
        compatibility_rule=CANONICAL_COMPATIBILITY_RULE,
        blockers=tuple(blockers),
    )


def _decision(
    order_id: str,
    status: GateStatus,
    reason: str,
    evidence_refs: Sequence[str],
    next_action: str,
) -> PublicationGateDecision:
    return PublicationGateDecision(
        order_id=order_id,
        status=status,
        reason=reason,
        evidence_refs=tuple(evidence_refs),
        next_action=next_action,
    )


def decide_publication_orders(
    gateway: GatewaySourceEvidence,
    wrangler: WranglerRunnerEvidence,
    probes: Sequence[LiveToolProbe],
    alias: AliasPolicyEvidence,
    *,
    git_sync_status: str = "",
    central_write_ok: bool = False,
) -> tuple[PublicationGateDecision, ...]:
    """Map evidence to the active executive and managerial orders."""

    live_tool_not_found = any(probe.status == ProbeStatus.TOOL_NOT_FOUND for probe in probes)
    live_ready = bool(probes) and all(probe.live_ready for probe in probes)
    git_blocked = "SEC_E_NO_CREDENTIALS" in git_sync_status or "index.lock" in git_sync_status or "FETCH_HEAD" in git_sync_status
    return (
        _decision(
            "0031_EXECUTIVA__publicar-provider-mais-humana-no-mcps-gateway-via-wrangler-homologado",
            GateStatus.PASSED if gateway.ready and wrangler.deploy_dry_run_ok else GateStatus.PARTIAL if gateway.ready else GateStatus.BLOCKED,
            "codigo local do provider existe; publicacao live depende de runner Wrangler sem spawn EPERM",
            (gateway.source_hash, "wrangler_runner"),
            "homologar runner Node/esbuild/workerd e repetir wrangler deploy --dry-run antes do deploy real",
        ),
        _decision(
            "0032_EXECUTIVA__validar-live-tools-mais-humana-v1-execute-com-evidencia",
            GateStatus.PASSED if live_ready else GateStatus.BLOCKED if live_tool_not_found else GateStatus.PARTIAL,
            "endpoint live foi sondado sem persistir bearer bruto",
            tuple(probe.evidence_id for probe in probes),
            "retestar as tres tools apos deploy do mcps-gateway contendo o provider Mais Humana",
        ),
        _decision(
            "0033_EXECUTIVA__sincronizar-git-mais-humana-mcps-central-com-credenciais",
            GateStatus.BLOCKED if git_blocked else GateStatus.PARTIAL,
            redact_sensitive_text(git_sync_status or "sincronizacao Git nao confirmada nesta rodada"),
            ("git_sync_status",),
            "corrigir credencial Git/Schannel e reconciliar ahead/behind sem reset destrutivo",
        ),
        _decision(
            "0034_EXECUTIVA__corrigir-acl-escrita-central-e-sql-semantico-plataforma-15",
            GateStatus.PASSED if central_write_ok else GateStatus.PARTIAL,
            "artefatos centrais foram testados pelo gerador de gate; falhas ficam registradas no projeto real",
            ("central_write_status",),
            "manter escrita automatica central e SQL semantico sob teste em toda rodada",
        ),
        _decision(
            "0035_EXECUTIVA__reconciliar-nome-canonico-real-alias-platform",
            alias.status,
            "politica de alias foi materializada sem renome destrutivo",
            ("alias_policy",),
            "executar migracao Git/MCP/documentacao somente por janela coordenada, preservando aliases e hashes",
        ),
        _decision(
            "0043_GERENCIAL__aprovar-janela-publicacao-provider-mais-humana-com-rollback",
            GateStatus.PARTIAL if gateway.ready else GateStatus.BLOCKED,
            "janela pode ser planejada, mas deploy real ainda depende do runner homologado",
            ("wrangler_runner", gateway.source_hash),
            "definir owner, janela, version atual, rollback e criterio de sucesso antes de deploy real",
        ),
        _decision(
            "0044_GERENCIAL__institucionalizar-ledger-transito-mcp-como-gate-release",
            GateStatus.PASSED if gateway.ready else GateStatus.PARTIAL,
            "ledger MCP existe como contrato local e deve ser criterio de release",
            ("mcp_transit_ledger", gateway.source_hash),
            "aplicar requiredFields em toda publicacao interplataforma",
        ),
        _decision(
            "0045_GERENCIAL__pactuar-politica-acesso-waf-gpt-mcp-gateway",
            GateStatus.PARTIAL,
            "probes usam User-Agent controlado, bearer redigido e response excerpt seguro",
            tuple(probe.evidence_id for probe in probes),
            "formalizar headers minimos, WAF, rate limit, logs e retencao de evidencias",
        ),
        _decision(
            "0046_GERENCIAL__homologar-runner-oficial-wrangler-node-esbuild",
            wrangler.status,
            "Wrangler autenticou quando executado diretamente, mas deploy dry-run nao ficou confirmado",
            ("wrangler_runner",),
            "homologar host que permita Node, esbuild/workerd e node --test sem spawn EPERM",
        ),
        _decision(
            "0047_GERENCIAL__decidir-nome-canonico-e-politica-alias-mais-humana",
            alias.status,
            "decisao institucional aprovada: canonico -platform com aliases historicos preservados",
            ("alias_policy",),
            "manter politica de compatibilidade e abrir apenas execucao tecnica de migracao coordenada",
        ),
    )


def build_publication_gate_report(
    *,
    project_root: Path,
    mcp_repo_root: Path,
    central_platform_folder: Path | None = None,
    wrangler_raw_summary: str = "",
    git_sync_status: str = "",
    repo_remote: str = "",
    live_probe_payloads: Sequence[LiveToolProbe] = (),
    central_write_ok: bool = False,
) -> McpPublicationGateReport:
    """Build a full publication gate report from local and live evidence."""

    gateway = scan_gateway_source(mcp_repo_root)
    wrangler = build_wrangler_runner_evidence(wrangler_raw_summary, attempted=bool(wrangler_raw_summary))
    probes = tuple(live_probe_payloads)
    alias = build_alias_policy(
        central_platform_folder=central_platform_folder,
        repo_remote=repo_remote,
    )
    decisions = decide_publication_orders(
        gateway,
        wrangler,
        probes,
        alias,
        git_sync_status=git_sync_status,
        central_write_ok=central_write_ok,
    )
    blockers = merge_unique(
        (
            *gateway.missing_snippets,
            *wrangler.blockers,
            *(
                f"{probe.tool_id}:{probe.status.value}:{probe.error_code}"
                for probe in probes
                if probe.status != ProbeStatus.OK or probe.error_code
            ),
            *(
                f"{probe.tool_id}:missing_transit:{','.join(probe.missing_transit_fields)}"
                for probe in probes
                if probe.missing_transit_fields and probe.status == ProbeStatus.OK
            ),
            *alias.blockers,
            *(("git_sync_blocked" if "SEC_E_NO_CREDENTIALS" in git_sync_status else ""),),
        )
    )
    summary = (
        f"Provider local Mais Humana pronto: {gateway.ready}.",
        f"Wrangler autenticado: {wrangler.authenticated}; deploy dry-run OK: {wrangler.deploy_dry_run_ok}.",
        f"Tools live prontas: {sum(1 for item in probes if item.live_ready)}/{len(probes)}.",
        f"Probes live com envelope MCP completo: {sum(1 for item in probes if not item.missing_transit_fields)}/{len(probes)}.",
        f"Nome atual materializado: {CURRENT_PROJECT_ID}; canonico aprovado: {CANONICAL_PROJECT_ID}; ownerPlatformId MCP: {DEFAULT_OWNER_PLATFORM_ID}.",
        f"Decisoes de OS avaliadas: {len(decisions)}.",
    )
    return McpPublicationGateReport(
        report_id=f"mcp-publication-gate-{slugify(utc_now())}",
        generated_at=utc_now(),
        provider_id=PROVIDER_ID,
        current_project_id=CURRENT_PROJECT_ID,
        canonical_project_id=CANONICAL_PROJECT_ID,
        control_plane_id=MCP_CONTROL_PLANE_ID,
        gateway_source=gateway,
        wrangler_runner=wrangler,
        live_probes=probes,
        alias_policy=alias,
        decisions=decisions,
        summary=summary,
        blockers=blockers,
    )


def publication_gate_rows(report: McpPublicationGateReport) -> list[list[str]]:
    rows = [
        [
            "order_id",
            "status",
            "reason",
            "next_action",
            "evidence_refs",
        ]
    ]
    for decision in report.decisions:
        rows.append(
            [
                decision.order_id,
                decision.status.value,
                decision.reason,
                decision.next_action,
                "; ".join(decision.evidence_refs),
            ]
        )
    return rows


def publication_gate_csv(report: McpPublicationGateReport) -> str:
    buffer = io.StringIO()
    writer = csv.writer(buffer, lineterminator="\n")
    writer.writerows(publication_gate_rows(report))
    return buffer.getvalue()


def publication_gate_markdown(report: McpPublicationGateReport) -> str:
    lines = [
        "# Gate de publicacao MCP Mais Humana",
        "",
        f"- report_id: `{report.report_id}`",
        f"- generated_at: `{report.generated_at}`",
        f"- provider_id: `{report.provider_id}`",
        f"- current_project_id: `{report.current_project_id}`",
        f"- canonical_project_id: `{report.canonical_project_id}`",
        f"- control_plane: `{report.control_plane_id}`",
        f"- status: `{report.status.value}`",
        f"- local_ready: `{report.local_ready}`",
        f"- live_ready: `{report.live_ready}`",
        f"- deploy_ready: `{report.deploy_ready}`",
        "",
        "## Sumario",
        "",
    ]
    lines.extend(f"- {item}" for item in report.summary)
    lines.extend(["", "## Evidencia local no MCPs Internos", ""])
    lines.extend(
        [
            f"- gateway_manager: `{report.gateway_source.gateway_manager_path}`",
            f"- managed_catalog: `{report.gateway_source.managed_catalog_path}`",
            f"- managed_provider_manager: `{report.gateway_source.managed_provider_manager_path}`",
            f"- tests: `{report.gateway_source.tests_path}`",
            f"- source_hash: `{report.gateway_source.source_hash}`",
            f"- snippets_presentes: `{len(report.gateway_source.present_snippets)}`",
            f"- snippets_ausentes: `{len(report.gateway_source.missing_snippets)}`",
        ]
    )
    if report.gateway_source.missing_snippets:
        lines.append("- ausentes:")
        lines.extend(f"  - `{item}`" for item in report.gateway_source.missing_snippets)
    lines.extend(["", "## Wrangler", ""])
    lines.extend(
        [
            f"- attempted: `{report.wrangler_runner.attempted}`",
            f"- version: `{report.wrangler_runner.version or 'nao_confirmada'}`",
            f"- authenticated: `{report.wrangler_runner.authenticated}`",
            f"- account_name: `{report.wrangler_runner.account_name or 'nao_confirmada'}`",
            f"- account_id: `{report.wrangler_runner.account_id or 'nao_confirmada'}`",
            f"- deploy_dry_run_ok: `{report.wrangler_runner.deploy_dry_run_ok}`",
        ]
    )
    if report.wrangler_runner.blockers:
        lines.append("- blockers:")
        lines.extend(f"  - `{item}`" for item in report.wrangler_runner.blockers)
    lines.extend(["", "## Probes live", ""])
    if not report.live_probes:
        lines.append("- Nenhum probe live executado.")
    for probe in report.live_probes:
        lines.extend(
            [
                f"- `{probe.tool_id}` status `{probe.status.value}` http `{probe.http_status}` ok `{probe.ok}`",
                f"  - error_code: `{probe.error_code or 'none'}`",
                f"  - evidenceId: `{probe.evidence_id}`",
                f"  - traceId: `{probe.trace_id}`",
                f"  - auditId: `{probe.audit_id}`",
                f"  - transit_fields_present: `{', '.join(probe.transit_fields_present) or 'none'}`",
                f"  - missing_transit_fields: `{', '.join(probe.missing_transit_fields) or 'none'}`",
            ]
        )
    lines.extend(["", "## Politica de nome canonico e aliases", ""])
    lines.extend(
        [
            f"- current_project_id: `{report.alias_policy.current_project_id}`",
            f"- canonical_project_id: `{report.alias_policy.canonical_project_id}`",
            f"- owner_platform_id: `{report.alias_policy.owner_platform_id}`",
            f"- legacy_aliases: `{', '.join(report.alias_policy.legacy_aliases)}`",
            f"- decision_status: `{report.alias_policy.decision_status}`",
            f"- migration_safe_now: `{report.alias_policy.migration_safe_now}`",
            f"- regra: {report.alias_policy.compatibility_rule}",
        ]
    )
    lines.extend(["", "## Decisoes das ordens ativas", ""])
    for decision in report.decisions:
        lines.extend(
            [
                f"### {decision.order_id}",
                "",
                f"- status: `{decision.status.value}`",
                f"- motivo: {decision.reason}",
                f"- evidencias: `{'; '.join(decision.evidence_refs)}`",
                f"- proxima_acao: {decision.next_action}",
                "",
            ]
        )
    lines.extend(["## Blockers", ""])
    if report.blockers:
        lines.extend(f"- `{item}`" for item in report.blockers)
    else:
        lines.append("- Nenhum blocker.")
    return "\n".join(lines).strip() + "\n"


def publication_gate_artifact_records(project_root: Path) -> tuple[GeneratedFile, ...]:
    records: list[GeneratedFile] = []
    for rel, description, function, file_type in (
        ("dados/mcp-publication-gate-mais-humana.json", "Gate estruturado de publicacao MCP Mais Humana.", "mcp publication gate", "json"),
        ("matrizes/mcp-publication-gate-decisions.csv", "Matriz de decisoes por ordem ativa de publicacao MCP.", "matriz publication gate", "csv"),
        ("ecossistema/MCP-PUBLICATION-GATE-MAIS-HUMANA.md", "Relatorio humano do gate de publicacao MCP.", "mcp publication gate", "markdown"),
    ):
        records.append(
            GeneratedFile(
                path=str(project_root / rel),
                description=description,
                function=function,
                file_type=file_type,
                changed_by="mais_humana.mcp_publication_gate",
                change_summary="Criado ou atualizado gate de publicacao MCP Mais Humana.",
                relation_to_order="015-ROTEADOR-PERMANENTE-DE-ORDEM_DE_SERVICO",
            )
        )
    return tuple(records)


def write_publication_gate_artifacts(
    report: McpPublicationGateReport,
    project_root: Path,
    *,
    central_platform_folder: Path | None = None,
) -> tuple[GeneratedFile, ...]:
    """Write publication gate artifacts and optional central copy."""

    generated = list(publication_gate_artifact_records(project_root))
    central_failures: list[dict[str, str]] = []
    targets: list[tuple[Path, str, GeneratedFile | None]] = [
        (project_root / "dados" / "mcp-publication-gate-mais-humana.json", json.dumps(report.to_dict(), ensure_ascii=False, indent=2, sort_keys=True), None),
        (project_root / "matrizes" / "mcp-publication-gate-decisions.csv", publication_gate_csv(report), None),
        (project_root / "ecossistema" / "MCP-PUBLICATION-GATE-MAIS-HUMANA.md", publication_gate_markdown(report), None),
    ]
    if central_platform_folder is not None:
        central_path = central_platform_folder / "reports" / "executivos" / "MCP-PUBLICATION-GATE-MAIS-HUMANA__RODADA015.md"
        central_record = GeneratedFile(
            path=str(central_path),
            description="Copia central do gate de publicacao MCP Mais Humana.",
            function="mcp publication gate central",
            file_type="markdown",
            changed_by="mais_humana.mcp_publication_gate",
            change_summary="Registrado gate de publicacao MCP na pasta central da plataforma 15.",
            relation_to_order="015-ROTEADOR-PERMANENTE-DE-ORDEM_DE_SERVICO",
        )
        targets.append((central_path, publication_gate_markdown(report), central_record))
    for path, content, central_record in targets:
        try:
            path.parent.mkdir(parents=True, exist_ok=True)
            path.write_text(redact_sensitive_text(content), encoding="utf-8")
            if central_record is not None:
                generated.append(central_record)
        except OSError as exc:
            if central_platform_folder is not None and central_platform_folder in path.parents:
                central_failures.append(
                    {
                        "path": str(path),
                        "operation": "write_text",
                        "error": f"{type(exc).__name__}: {exc}",
                    }
                )
                continue
            raise
    if central_failures:
        status_path = project_root / "dados" / "mcp-publication-gate-central-write-status.json"
        status_payload = {
            "generatedAt": utc_now(),
            "centralPlatformFolder": str(central_platform_folder) if central_platform_folder is not None else "",
            "ok": False,
            "failureCount": len(central_failures),
            "failures": central_failures,
            "policy": "falha de escrita central nao aborta artefatos do projeto real",
        }
        status_path.parent.mkdir(parents=True, exist_ok=True)
        status_path.write_text(json.dumps(status_payload, ensure_ascii=False, indent=2, sort_keys=True), encoding="utf-8")
        generated.append(
            GeneratedFile(
                path=str(status_path),
                description="Status da tentativa de escrita central do gate de publicacao MCP.",
                function="mcp publication gate central write status",
                file_type="json",
                changed_by="mais_humana.mcp_publication_gate",
                change_summary="Registrada falha de escrita central sem abortar artefatos do projeto real.",
                relation_to_order="015-ROTEADOR-PERMANENTE-DE-ORDEM_DE_SERVICO",
            )
        )
    return tuple(generated)


def run_publication_gate(
    *,
    project_root: Path,
    mcp_repo_root: Path,
    central_platform_folder: Path | None = None,
    wrangler_raw_summary: str = "",
    git_sync_status: str = "",
    repo_remote: str = "",
    bearer: str = "",
    live_probe: bool = False,
    tools: Sequence[str] = DEFAULT_GATEWAY_TOOLS,
) -> tuple[McpPublicationGateReport, tuple[GeneratedFile, ...]]:
    """Build, optionally live-probe, and write the publication gate."""

    central_write_ok = False
    if central_platform_folder is not None:
        try:
            (central_platform_folder / "reports" / "executivos").mkdir(parents=True, exist_ok=True)
            central_write_ok = True
        except OSError:
            central_write_ok = False
    probes = tuple(
        execute_live_tool_probe(tool_id, bearer=bearer)
        if live_probe
        else build_not_run_probe(tool_id, "live probe skipped by caller")
        for tool_id in tools
    )
    report = build_publication_gate_report(
        project_root=project_root,
        mcp_repo_root=mcp_repo_root,
        central_platform_folder=central_platform_folder,
        wrangler_raw_summary=wrangler_raw_summary,
        git_sync_status=git_sync_status,
        repo_remote=repo_remote,
        live_probe_payloads=probes,
        central_write_ok=central_write_ok,
    )
    records = write_publication_gate_artifacts(report, project_root, central_platform_folder=central_platform_folder)
    return report, records
