"""Build and render the operational dossier for a service-order round."""

from __future__ import annotations

from pathlib import Path
from typing import Iterable, Sequence

from .blocker_catalog import build_operational_signals, summarize_blockers, summarize_capabilities
from .models import OrderType, PlatformHumanReport, Recommendation, ServiceOrder, as_plain_data, merge_unique, utc_now
from .operational_models import (
    ExecutionRoundDossier,
    GateDomain,
    GateOutcome,
    HumanReadinessStage,
    OrderClosureStatus,
    OrderJustification,
    OperationalSignal,
    PlatformOperationalDossier,
    ReadinessGate,
    SignalKind,
    SignalSeverity,
    SourceConfidence,
    SourceReference,
    code_line_count,
    find_service_order_platform,
    next_actions_from_signals,
    severity_rank,
    source_refs_from_strings,
    stage_rank,
    stable_digest,
)


ACTIVE_ORDER_IDS: tuple[str, ...] = (
    "0001_EXECUTIVA__resolver-ou-formalizar-bloqueios-conhecidos",
    "0002_EXECUTIVA__resolver-ou-formalizar-bloqueios-conhecidos",
    "0003_EXECUTIVA__resolver-ou-formalizar-bloqueios-conhecidos",
    "0004_EXECUTIVA__elevar-maturidade-humana-de-business-platform",
    "0005_EXECUTIVA__elevar-maturidade-humana-de-compliance-platform",
    "0012_GERENCIAL__resolver-ou-formalizar-bloqueios-conhecidos",
    "0013_GERENCIAL__resolver-ou-formalizar-bloqueios-conhecidos",
    "0014_GERENCIAL__resolver-ou-formalizar-bloqueios-conhecidos",
    "0015_GERENCIAL__elevar-maturidade-humana-de-business-platform",
    "0016_GERENCIAL__elevar-maturidade-humana-de-compliance-platform",
)


ORDER_PLATFORM_MAP: dict[str, str] = {
    "0001_EXECUTIVA__resolver-ou-formalizar-bloqueios-conhecidos": "docs",
    "0002_EXECUTIVA__resolver-ou-formalizar-bloqueios-conhecidos": "integracoes",
    "0003_EXECUTIVA__resolver-ou-formalizar-bloqueios-conhecidos": "intelligence",
    "0004_EXECUTIVA__elevar-maturidade-humana-de-business-platform": "business",
    "0005_EXECUTIVA__elevar-maturidade-humana-de-compliance-platform": "compliance",
    "0012_GERENCIAL__resolver-ou-formalizar-bloqueios-conhecidos": "docs",
    "0013_GERENCIAL__resolver-ou-formalizar-bloqueios-conhecidos": "integracoes",
    "0014_GERENCIAL__resolver-ou-formalizar-bloqueios-conhecidos": "intelligence",
    "0015_GERENCIAL__elevar-maturidade-humana-de-business-platform": "business",
    "0016_GERENCIAL__elevar-maturidade-humana-de-compliance-platform": "compliance",
}


def gate(
    platform_id: str,
    gate_id: str,
    domain: GateDomain,
    title: str,
    outcome: GateOutcome,
    severity: SignalSeverity,
    reason: str,
    next_action: str,
    evidence: Iterable[SourceReference] = (),
    linked_signals: Iterable[str] = (),
) -> ReadinessGate:
    return ReadinessGate(
        gate_id=f"{platform_id}.{gate_id}",
        platform_id=platform_id,
        domain=domain,
        title=title,
        outcome=outcome,
        severity=severity,
        reason=reason,
        next_action=next_action,
        evidence=tuple(evidence),
        linked_signals=tuple(linked_signals),
    )


def signal_refs(signals: Sequence[OperationalSignal], domain: GateDomain | None = None, limit: int = 6) -> tuple[SourceReference, ...]:
    refs: list[SourceReference] = []
    for signal in signals:
        if domain is not None and signal.domain != domain:
            continue
        refs.extend(signal.sources)
        if len(refs) >= limit:
            break
    return tuple(refs[:limit])


def linked_signal_ids(signals: Sequence[OperationalSignal], domain: GateDomain | None = None, kind: SignalKind | None = None) -> tuple[str, ...]:
    ids: list[str] = []
    for signal in signals:
        if domain is not None and signal.domain != domain:
            continue
        if kind is not None and signal.kind != kind:
            continue
        ids.append(signal.signal_id)
    return tuple(ids)


def build_repository_gate(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> ReadinessGate:
    scan = report.scan
    if not scan.exists:
        return gate(
            report.platform.platform_id,
            "repository",
            GateDomain.REPOSITORY,
            "Repositorio real existe",
            GateOutcome.BLOCKED,
            SignalSeverity.CRITICAL,
            "Repositorio real nao encontrado no espelho local.",
            "criar ou clonar repositorio real antes de nova promocao",
            signal_refs(signals, GateDomain.REPOSITORY),
            linked_signal_ids(signals, GateDomain.REPOSITORY),
        )
    return gate(
        report.platform.platform_id,
        "repository",
        GateDomain.REPOSITORY,
        "Repositorio real existe",
        GateOutcome.PASS,
        SignalSeverity.INFO,
        "Repositorio real encontrado e analisado.",
        "manter espelho local sincronizado",
        (SourceReference(path=scan.repo_path, summary="Repositorio analisado.", confidence=SourceConfidence.DIRECT),),
        linked_signal_ids(signals, GateDomain.REPOSITORY),
    )


def build_git_gate(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> ReadinessGate:
    scan = report.scan
    if scan.git_present:
        evidence = source_refs_from_strings(
            tuple(value for value in (scan.branch, scan.head, scan.remote_origin) if value) or (scan.repo_path,),
            "Metadado Git detectado.",
        )
        return gate(
            report.platform.platform_id,
            "git",
            GateDomain.REPOSITORY,
            "Git local e rastreabilidade",
            GateOutcome.PASS,
            SignalSeverity.INFO,
            "Git local detectado.",
            "registrar status e hash no fechamento",
            evidence,
            linked_signal_ids(signals, GateDomain.REPOSITORY),
        )
    severity = SignalSeverity.HIGH if scan.exists else SignalSeverity.CRITICAL
    outcome = GateOutcome.FAIL if scan.exists else GateOutcome.BLOCKED
    return gate(
        report.platform.platform_id,
        "git",
        GateDomain.REPOSITORY,
        "Git local e rastreabilidade",
        outcome,
        severity,
        "Git local nao esta disponivel para hash e sincronizacao.",
        "corrigir permissao ou inicializar Git com origin correto",
        signal_refs(signals, GateDomain.REPOSITORY),
        linked_signal_ids(signals, GateDomain.REPOSITORY),
    )


def build_documentation_gate(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> ReadinessGate:
    scan = report.scan
    if scan.readme_excerpt:
        return gate(
            report.platform.platform_id,
            "documentation",
            GateDomain.DOCUMENTATION,
            "Documentacao humana minima",
            GateOutcome.PASS,
            SignalSeverity.INFO,
            "README ou documentacao principal foi encontrada.",
            "manter documentacao reconciliada com gates humanos",
            signal_refs(signals, GateDomain.DOCUMENTATION),
            linked_signal_ids(signals, GateDomain.DOCUMENTATION),
        )
    return gate(
        report.platform.platform_id,
        "documentation",
        GateDomain.DOCUMENTATION,
        "Documentacao humana minima",
        GateOutcome.ATTENTION,
        SignalSeverity.MEDIUM,
        "README/documentacao inicial nao foi encontrada.",
        "criar README tecnico e humano com comandos, surfaces e criterios",
        signal_refs(signals, GateDomain.DOCUMENTATION),
        linked_signal_ids(signals, GateDomain.DOCUMENTATION),
    )


def build_tests_gate(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> ReadinessGate:
    if report.scan.has_tests:
        return gate(
            report.platform.platform_id,
            "tests",
            GateDomain.TESTS,
            "Teste ou smoke detectavel",
            GateOutcome.PASS,
            SignalSeverity.INFO,
            "Testes ou specs foram detectados pela varredura.",
            "executar suite aplicavel antes de fechar OS",
            signal_refs(signals, GateDomain.TESTS),
            linked_signal_ids(signals, GateDomain.TESTS),
        )
    return gate(
        report.platform.platform_id,
        "tests",
        GateDomain.TESTS,
        "Teste ou smoke detectavel",
        GateOutcome.ATTENTION,
        SignalSeverity.MEDIUM,
        "Nenhum teste foi encontrado pela varredura local.",
        "criar smoke ou teste canonico do contrato humano",
        signal_refs(signals, GateDomain.TESTS),
        linked_signal_ids(signals, GateDomain.TESTS),
    )


def build_openapi_gate(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> ReadinessGate:
    if report.scan.has_openapi:
        return gate(
            report.platform.platform_id,
            "contract",
            GateDomain.CONTRACT,
            "Contrato de API ou surface auditavel",
            GateOutcome.PASS,
            SignalSeverity.INFO,
            "OpenAPI ou contrato equivalente foi detectado.",
            "manter contrato sincronizado com rotas e tools",
            signal_refs(signals, GateDomain.CONTRACT),
            linked_signal_ids(signals, GateDomain.CONTRACT),
        )
    severity = SignalSeverity.MEDIUM
    if report.platform.platform_id in {"docs", "public", "ui"}:
        severity = SignalSeverity.HIGH
    return gate(
        report.platform.platform_id,
        "contract",
        GateDomain.CONTRACT,
        "Contrato de API ou surface auditavel",
        GateOutcome.ATTENTION,
        severity,
        "OpenAPI nao foi detectada por varredura local.",
        "publicar OpenAPI minima ou declarar contrato alternativo versionado",
        signal_refs(signals, GateDomain.CONTRACT),
        linked_signal_ids(signals, GateDomain.CONTRACT),
    )


def build_panel_gate(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> ReadinessGate:
    panel_signals = [signal for signal in signals if signal.domain == GateDomain.PANEL]
    same_source = any("samesource" in " ".join(signal.tags).lower() or "mesma fonte" in signal.summary.lower() for signal in panel_signals)
    panel_ready = any("panelready" in " ".join(signal.tags).lower() or "painel" in signal.summary.lower() for signal in panel_signals)
    if same_source and panel_ready:
        outcome = GateOutcome.PASS
        severity = SignalSeverity.INFO
        reason = "Sinais de panelReady e sameSource foram detectados."
        action = "executar regressao de hashes de fonte e registros"
    elif panel_signals:
        outcome = GateOutcome.ATTENTION
        severity = SignalSeverity.MEDIUM
        reason = "Ha sinais de painel, mas a mesma fonte precisa ficar explicita."
        action = "formalizar sourceEndpoint, sourceToolId, sourcePayloadHash e sourceRecordsHash"
    else:
        outcome = GateOutcome.ATTENTION
        severity = SignalSeverity.MEDIUM
        reason = "Nenhum contrato de painel humano foi detectado."
        action = "criar contrato de tela ou declarar que a plataforma nao entrega painel diretamente"
    return gate(
        report.platform.platform_id,
        "panel",
        GateDomain.PANEL,
        "Painel humano e mesma fonte",
        outcome,
        severity,
        reason,
        action,
        signal_refs(signals, GateDomain.PANEL),
        linked_signal_ids(signals, GateDomain.PANEL),
    )


def build_docs_gate(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> ReadinessGate:
    docs_related = report.platform.platform_id == "docs" or "docs" in report.platform.related_platforms
    if not docs_related:
        return gate(
            report.platform.platform_id,
            "docs",
            GateDomain.DOCS,
            "Relacao com Docs",
            GateOutcome.NOT_APPLICABLE,
            SignalSeverity.INFO,
            "Docs nao e dependencia primaria declarada para esta plataforma.",
            "manter referencia documental quando houver contrato publico",
            (),
            linked_signal_ids(signals, GateDomain.DOCS),
        )
    docs_blockers = [signal for signal in signals if signal.domain == GateDomain.DOCS and signal.is_blocking]
    if docs_blockers:
        return gate(
            report.platform.platform_id,
            "docs",
            GateDomain.DOCS,
            "Relacao com Docs",
            GateOutcome.BLOCKED,
            SignalSeverity.HIGH,
            "Docs possui decisao catalogOnly ou leitura canonica pendente.",
            "promover leitura Docs responseReady minima ou registrar excecao formal",
            signal_refs(docs_blockers, GateDomain.DOCS),
            linked_signal_ids(docs_blockers, GateDomain.DOCS),
        )
    return gate(
        report.platform.platform_id,
        "docs",
        GateDomain.DOCS,
        "Relacao com Docs",
        GateOutcome.PASS,
        SignalSeverity.INFO,
        "A relacao documental nao apresenta blocker formal nesta varredura.",
        "manter evidencias e contratos documentais reconciliados",
        signal_refs(signals, GateDomain.DOCS),
        linked_signal_ids(signals, GateDomain.DOCS),
    )


def build_integration_gate(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> ReadinessGate:
    integration_related = report.platform.platform_id == "integracoes" or "integracoes" in report.platform.related_platforms
    if not integration_related:
        return gate(
            report.platform.platform_id,
            "integration",
            GateDomain.INTEGRATION,
            "Integracao externa e credenciais",
            GateOutcome.NOT_APPLICABLE,
            SignalSeverity.INFO,
            "Integracoes nao e dependencia primaria declarada.",
            "registrar dependencia quando produto exigir provider externo",
            (),
            linked_signal_ids(signals, GateDomain.INTEGRATION),
        )
    blockers = [signal for signal in signals if signal.domain == GateDomain.INTEGRATION and signal.is_blocking]
    capabilities = [signal for signal in signals if signal.domain == GateDomain.INTEGRATION and signal.kind == SignalKind.CAPABILITY]
    if blockers:
        return gate(
            report.platform.platform_id,
            "integration",
            GateDomain.INTEGRATION,
            "Integracao externa e credenciais",
            GateOutcome.BLOCKED,
            SignalSeverity.HIGH,
            "Ha bloqueio de BYOK, credencial live, provider ou smoke por tenant.",
            "provar credentialRef, smoke readonly e nao vazamento por tenant",
            signal_refs(blockers, GateDomain.INTEGRATION),
            linked_signal_ids(blockers, GateDomain.INTEGRATION),
        )
    if capabilities:
        return gate(
            report.platform.platform_id,
            "integration",
            GateDomain.INTEGRATION,
            "Integracao externa e credenciais",
            GateOutcome.PASS,
            SignalSeverity.INFO,
            "A plataforma possui sinais de integracao controlada.",
            "manter readiness e redaction dos providers",
            signal_refs(capabilities, GateDomain.INTEGRATION),
            linked_signal_ids(capabilities, GateDomain.INTEGRATION),
        )
    return gate(
        report.platform.platform_id,
        "integration",
        GateDomain.INTEGRATION,
        "Integracao externa e credenciais",
        GateOutcome.ATTENTION,
        SignalSeverity.MEDIUM,
        "Integracoes e dependencia declarada, mas nao apareceram sinais fortes.",
        "mapear provider, credencial, smoke e ownerPlatformId",
        signal_refs(signals, GateDomain.INTEGRATION),
        linked_signal_ids(signals, GateDomain.INTEGRATION),
    )


def build_business_gate(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> ReadinessGate:
    business_related = report.platform.platform_id == "business" or "business" in report.platform.related_platforms
    if not business_related:
        return gate(
            report.platform.platform_id,
            "business",
            GateDomain.BUSINESS,
            "Fonte comercial e entitlement",
            GateOutcome.NOT_APPLICABLE,
            SignalSeverity.INFO,
            "Business nao e dependencia primaria declarada.",
            "registrar contrato comercial quando houver produto vendavel",
            (),
            linked_signal_ids(signals, GateDomain.BUSINESS),
        )
    business_caps = [signal for signal in signals if signal.domain == GateDomain.BUSINESS]
    blockers = [signal for signal in business_caps if signal.is_blocking]
    if blockers:
        return gate(
            report.platform.platform_id,
            "business",
            GateDomain.BUSINESS,
            "Fonte comercial e entitlement",
            GateOutcome.BLOCKED,
            SignalSeverity.HIGH,
            "Ha blocker comercial ou de isolamento de produto.",
            "isolar readiness por productId e validar regra de venda/piloto",
            signal_refs(blockers, GateDomain.BUSINESS),
            linked_signal_ids(blockers, GateDomain.BUSINESS),
        )
    if business_caps:
        return gate(
            report.platform.platform_id,
            "business",
            GateDomain.BUSINESS,
            "Fonte comercial e entitlement",
            GateOutcome.PASS,
            SignalSeverity.INFO,
            "Ha sinais de entitlement, cobranca, produto ou blocker comercial isolado.",
            "manter Business como fonte unica para plano, franquia e bloqueio",
            signal_refs(business_caps, GateDomain.BUSINESS),
            linked_signal_ids(business_caps, GateDomain.BUSINESS),
        )
    return gate(
        report.platform.platform_id,
        "business",
        GateDomain.BUSINESS,
        "Fonte comercial e entitlement",
        GateOutcome.ATTENTION,
        SignalSeverity.MEDIUM,
        "Business e relacao declarada, mas sinais comerciais nao apareceram nesta varredura.",
        "mapear plano, entitlement, consumo, bloqueio e decisao comercial",
        signal_refs(signals, GateDomain.BUSINESS),
        linked_signal_ids(signals, GateDomain.BUSINESS),
    )


def build_cloud_gate(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> ReadinessGate:
    cloud_signals = [signal for signal in signals if signal.domain == GateDomain.CLOUD]
    plugin_exception = [signal for signal in cloud_signals if "cloudflare-plugin" in signal.tags]
    wrangler_or_worker = [signal for signal in cloud_signals if "wrangler" in signal.tags or "cloudflare" in signal.tags]
    cloud_blockers = [signal for signal in cloud_signals if signal.is_blocking and "cloudflare-plugin" not in signal.tags]
    if cloud_blockers:
        return gate(
            report.platform.platform_id,
            "cloud",
            GateDomain.CLOUD,
            "Cloudflare e runtime externo",
            GateOutcome.ATTENTION,
            SignalSeverity.MEDIUM,
            "Ha pendencia de binding, token live ou runtime externo; negativa do plugin nao e blocker.",
            "usar wrangler para diagnostico real de routes, secrets, deploy e smoke",
            signal_refs(cloud_blockers, GateDomain.CLOUD),
            linked_signal_ids(cloud_blockers, GateDomain.CLOUD),
        )
    if wrangler_or_worker:
        return gate(
            report.platform.platform_id,
            "cloud",
            GateDomain.CLOUD,
            "Cloudflare e runtime externo",
            GateOutcome.PASS,
            SignalSeverity.INFO,
            "Ha evidencia de Worker, Cloudflare ou wrangler.",
            "validar runtime com wrangler quando a ordem exigir deploy/health",
            signal_refs(wrangler_or_worker, GateDomain.CLOUD),
            linked_signal_ids(wrangler_or_worker, GateDomain.CLOUD),
        )
    if plugin_exception:
        return gate(
            report.platform.platform_id,
            "cloud",
            GateDomain.CLOUD,
            "Cloudflare e runtime externo",
            GateOutcome.NOT_APPLICABLE,
            SignalSeverity.INFO,
            "Apenas a tentativa do plugin foi registrada; isso nao bloqueia a OS.",
            "seguir trabalho operacional por wrangler quando houver acao Cloudflare real",
            signal_refs(plugin_exception, GateDomain.CLOUD),
            linked_signal_ids(plugin_exception, GateDomain.CLOUD),
        )
    return gate(
        report.platform.platform_id,
        "cloud",
        GateDomain.CLOUD,
        "Cloudflare e runtime externo",
        GateOutcome.NOT_APPLICABLE,
        SignalSeverity.INFO,
        "Nenhuma dependencia Cloudflare direta foi detectada para esta leitura.",
        "registrar dependencia Cloudflare apenas quando houver Worker, rota ou deploy",
        (),
        linked_signal_ids(signals, GateDomain.CLOUD),
    )


def build_gates(report: PlatformHumanReport, signals: Sequence[OperationalSignal]) -> tuple[ReadinessGate, ...]:
    return (
        build_repository_gate(report, signals),
        build_git_gate(report, signals),
        build_documentation_gate(report, signals),
        build_tests_gate(report, signals),
        build_openapi_gate(report, signals),
        build_panel_gate(report, signals),
        build_docs_gate(report, signals),
        build_integration_gate(report, signals),
        build_business_gate(report, signals),
        build_cloud_gate(report, signals),
    )


def compute_stage(report: PlatformHumanReport, gates: Sequence[ReadinessGate], signals: Sequence[OperationalSignal]) -> HumanReadinessStage:
    if not report.scan.exists:
        return HumanReadinessStage.NOT_FOUND
    critical = any(signal.severity == SignalSeverity.CRITICAL for signal in signals)
    high_blocker = any(signal.is_blocking and signal.severity == SignalSeverity.HIGH for signal in signals)
    blocked_gate = any(gate.outcome == GateOutcome.BLOCKED for gate in gates)
    if critical or blocked_gate:
        return HumanReadinessStage.BLOCKED
    if high_blocker:
        return HumanReadinessStage.CATALOG_ONLY
    candidate_stages = [signal.stage for signal in signals]
    if report.scan.has_tests and report.scan.has_openapi and report.scan.git_present:
        candidate_stages.append(HumanReadinessStage.TECHNICAL_READY)
    if any(gate.domain == GateDomain.PANEL and gate.passed for gate in gates):
        candidate_stages.append(HumanReadinessStage.PANEL_READY)
    if report.average_score >= 85:
        candidate_stages.append(HumanReadinessStage.HUMAN_EXPLAINABLE)
    return max(candidate_stages or [HumanReadinessStage.LOCAL_ONLY], key=stage_rank)


def compute_panel_ready(gates: Sequence[ReadinessGate], signals: Sequence[OperationalSignal]) -> bool:
    panel_gate = next((gate for gate in gates if gate.domain == GateDomain.PANEL), None)
    if panel_gate and panel_gate.passed:
        return True
    panel_signals = [signal for signal in signals if signal.domain == GateDomain.PANEL]
    return any(signal.stage == HumanReadinessStage.PANEL_READY for signal in panel_signals) and not any(signal.is_blocking for signal in panel_signals)


def compute_same_source_ready(signals: Sequence[OperationalSignal]) -> bool:
    return any(
        signal.domain == GateDomain.PANEL
        and ("samesource" in " ".join(signal.tags).lower() or "sameSource" in signal.title or "sameSource" in signal.summary)
        and not signal.is_blocking
        for signal in signals
    )


def build_platform_dossier(report: PlatformHumanReport, recommendations: Sequence[Recommendation]) -> PlatformOperationalDossier:
    signals = build_operational_signals(report, recommendations)
    gates = build_gates(report, signals)
    blocker_count = sum(1 for signal in signals if signal.is_blocking)
    warning_count = sum(1 for gate_item in gates if gate_item.outcome == GateOutcome.ATTENTION)
    stage = compute_stage(report, gates, signals)
    panel_ready = compute_panel_ready(gates, signals)
    same_source_ready = compute_same_source_ready(signals)
    technical_ready = report.scan.exists and report.scan.git_present and report.scan.has_tests
    next_actions = next_actions_from_signals(signals, gates, limit=8)
    order_targets = merge_unique(recommendation.recommendation_id for recommendation in recommendations if recommendation.platform_id == report.platform.platform_id)
    return PlatformOperationalDossier(
        platform_id=report.platform.platform_id,
        title=report.platform.title,
        repo_path=report.scan.repo_path,
        stage=stage,
        human_score=report.average_score,
        technical_ready=technical_ready,
        panel_ready=panel_ready,
        same_source_ready=same_source_ready,
        blocker_count=blocker_count,
        warning_count=warning_count,
        gates=gates,
        signals=signals,
        top_next_actions=next_actions,
        order_targets=order_targets,
    )


def order_title_from_id(order_id: str) -> str:
    if "__" not in order_id:
        return order_id
    return order_id.split("__", 1)[1].replace("-", " ").strip().capitalize()


def order_type_from_id(order_id: str) -> OrderType:
    return OrderType.EXECUTIVE if "_EXECUTIVA__" in order_id else OrderType.MANAGERIAL


def closure_status_for_order(platform_dossier: PlatformOperationalDossier | None, order_id: str) -> OrderClosureStatus:
    if platform_dossier is None:
        return OrderClosureStatus.BLOCKED
    if platform_dossier.critical_signals:
        return OrderClosureStatus.BLOCKED
    if platform_dossier.blocker_count:
        return OrderClosureStatus.PARTIAL
    if "resolver-ou-formalizar-bloqueios" in order_id and platform_dossier.blocker_count == 0:
        return OrderClosureStatus.COMPLETED
    return OrderClosureStatus.COMPLETED


def pending_for_order(platform_dossier: PlatformOperationalDossier | None) -> tuple[str, ...]:
    if platform_dossier is None:
        return ("plataforma relacionada nao encontrada no dossie operacional",)
    pending: list[str] = []
    for signal in platform_dossier.blocking_signals[:6]:
        pending.append(f"{signal.title}: {signal.next_action}")
    for gate_item in platform_dossier.gates:
        if gate_item.outcome in {GateOutcome.FAIL, GateOutcome.BLOCKED}:
            pending.append(f"{gate_item.title}: {gate_item.next_action}")
    return merge_unique(pending)


def evidence_for_order(platform_dossier: PlatformOperationalDossier | None, limit: int = 8) -> tuple[SourceReference, ...]:
    if platform_dossier is None:
        return ()
    refs: list[SourceReference] = []
    for signal in platform_dossier.signals:
        refs.extend(signal.sources)
        if len(refs) >= limit:
            break
    for gate_item in platform_dossier.gates:
        refs.extend(gate_item.evidence)
        if len(refs) >= limit:
            break
    return tuple(refs[:limit])


def link_output_orders(platform_id: str, output_orders: Sequence[ServiceOrder]) -> tuple[str, ...]:
    linked: list[str] = []
    for order in output_orders:
        if find_service_order_platform(order) == platform_id or platform_id in order.object_scope.lower():
            linked.append(order.order_id)
    return tuple(linked)


def build_order_justifications(
    platform_dossiers: Sequence[PlatformOperationalDossier],
    active_order_ids: Sequence[str],
    output_orders: Sequence[ServiceOrder],
) -> tuple[OrderJustification, ...]:
    by_platform = {dossier.platform_id: dossier for dossier in platform_dossiers}
    justifications: list[OrderJustification] = []
    for order_id in active_order_ids:
        platform_id = ORDER_PLATFORM_MAP.get(order_id, find_service_order_platform(order_id))
        dossier = by_platform.get(platform_id)
        status = closure_status_for_order(dossier, order_id)
        linked_signals = tuple(signal.signal_id for signal in dossier.signals[:12]) if dossier else ()
        linked_gates = tuple(gate_item.gate_id for gate_item in dossier.gates if gate_item.outcome != GateOutcome.NOT_APPLICABLE) if dossier else ()
        pending = pending_for_order(dossier)
        if dossier is None:
            reason = "A plataforma relacionada nao foi encontrada no relatorio da rodada."
            execution = "Ordem bloqueada por ausencia de dossie material."
        elif pending:
            reason = "A ordem foi executada como formalizacao e reducao de ambiguidade; ainda ha pendencias reais."
            execution = f"Dossie atualizado para {platform_id}; status {dossier.status_label}; pendencias: {len(pending)}."
        else:
            reason = "A ordem foi executada com evidencia suficiente para fechar a lacuna mapeada."
            execution = f"Dossie atualizado para {platform_id}; nenhum blocker ativo ficou associado a ordem."
        justifications.append(
            OrderJustification(
                order_id=order_id,
                order_type=order_type_from_id(order_id),
                platform_id=platform_id,
                title=order_title_from_id(order_id),
                closure_status=status,
                reason=reason,
                execution_summary=execution,
                evidence=evidence_for_order(dossier),
                linked_signals=linked_signals,
                linked_gates=linked_gates,
                resulting_orders=link_output_orders(platform_id, output_orders),
                pending_items=pending,
                validation_steps=(
                    "regenerar dossie operacional",
                    "validar JSON e Markdown gerados",
                    "atualizar SQL semantico com arquivos alterados",
                    "registrar ordens de saida somente para pendencias reais",
                ),
            )
        )
    return tuple(justifications)


def executive_summary(platform_dossiers: Sequence[PlatformOperationalDossier]) -> tuple[str, ...]:
    total = len(platform_dossiers)
    blocked = sum(1 for item in platform_dossiers if item.blocker_count > 0)
    panel_ready = sum(1 for item in platform_dossiers if item.panel_ready)
    same_source = sum(1 for item in platform_dossiers if item.same_source_ready)
    critical = sum(1 for item in platform_dossiers if item.critical_signals)
    avg_score = round(sum(item.human_score for item in platform_dossiers) / total) if total else 0
    lines = [
        f"Plataformas avaliadas: {total}",
        f"Score humano medio: {avg_score}",
        f"Plataformas com blocker formalizado: {blocked}",
        f"Plataformas com panelReady detectado: {panel_ready}",
        f"Plataformas com sameSource detectado: {same_source}",
        f"Plataformas com sinal critico: {critical}",
    ]
    blockers = summarize_blockers(tuple(signal for dossier in platform_dossiers for signal in dossier.signals), limit=6)
    if blockers:
        lines.append("Blockers prioritarios: " + " | ".join(blockers[:4]))
    capabilities = summarize_capabilities(tuple(signal for dossier in platform_dossiers for signal in dossier.signals), limit=6)
    if capabilities:
        lines.append("Capacidades confirmadas: " + " | ".join(capabilities[:4]))
    return tuple(lines)


def managerial_summary(platform_dossiers: Sequence[PlatformOperationalDossier]) -> tuple[str, ...]:
    lines: list[str] = []
    for dossier in sorted(platform_dossiers, key=lambda item: (-item.blocker_count, item.platform_id)):
        gates_failed = [gate_item for gate_item in dossier.gates if gate_item.outcome in {GateOutcome.ATTENTION, GateOutcome.FAIL, GateOutcome.BLOCKED}]
        gates_text = ", ".join(gate_item.title for gate_item in gates_failed[:3]) or "sem gate em atencao"
        lines.append(
            f"{dossier.platform_id}: stage={dossier.stage.value}; status={dossier.status_label}; "
            f"blockers={dossier.blocker_count}; gates={gates_text}; acao={dossier.primary_action}"
        )
    return tuple(lines)


def pending_items_from_justifications(justifications: Sequence[OrderJustification], platform_dossiers: Sequence[PlatformOperationalDossier]) -> tuple[str, ...]:
    items: list[str] = []
    for justification in justifications:
        for pending in justification.pending_items:
            items.append(f"{justification.order_id}: {pending}")
    for dossier in platform_dossiers:
        if not dossier.technical_ready:
            items.append(f"{dossier.platform_id}: technical_ready=false; {dossier.primary_action}")
        if dossier.blocker_count and not dossier.top_next_actions:
            items.append(f"{dossier.platform_id}: blockers sem proxima acao detalhada")
    return merge_unique(items)


def build_execution_round_dossier(
    project_root: Path,
    platform_reports: Sequence[PlatformHumanReport],
    recommendations: Sequence[Recommendation],
    output_orders: Sequence[ServiceOrder],
    active_order_ids: Sequence[str] = ACTIVE_ORDER_IDS,
    total_code_lines_analyzed: int = 0,
) -> ExecutionRoundDossier:
    platform_dossiers = tuple(build_platform_dossier(report, recommendations) for report in platform_reports)
    justifications = build_order_justifications(platform_dossiers, active_order_ids, output_orders)
    pending = pending_items_from_justifications(justifications, platform_dossiers)
    generated_at = utc_now()
    return ExecutionRoundDossier(
        round_id=f"mais-humana-round-{stable_digest((generated_at, active_order_ids, [order.order_id for order in output_orders]), length=10)}",
        project_id="tudo-para-ia-mais-humana",
        generated_at=generated_at,
        platform_dossiers=platform_dossiers,
        order_justifications=justifications,
        active_input_orders=tuple(active_order_ids),
        output_orders=tuple(order.order_id for order in output_orders),
        executive_summary=executive_summary(platform_dossiers),
        managerial_summary=managerial_summary(platform_dossiers),
        pending_items=pending,
        total_code_lines_analyzed=total_code_lines_analyzed,
        code_lines_available_in_project=code_line_count(project_root),
    )


def dossier_to_markdown(dossier: ExecutionRoundDossier) -> str:
    lines = [
        "# Dossie operacional humano da rodada",
        "",
        f"- round_id: `{dossier.round_id}`",
        f"- project_id: `{dossier.project_id}`",
        f"- generated_at: `{dossier.generated_at}`",
        f"- linhas de codigo analisadas no ecossistema: `{dossier.total_code_lines_analyzed}`",
        f"- linhas de codigo disponiveis no projeto Mais Humana: `{dossier.code_lines_available_in_project}`",
        "",
        "## Sumario executivo",
        "",
    ]
    lines.extend(f"- {item}" for item in dossier.executive_summary)
    lines.extend(["", "## Sumario gerencial", ""])
    lines.extend(f"- {item}" for item in dossier.managerial_summary)
    lines.extend(["", "## Plataformas", ""])
    for item in sorted(dossier.platform_dossiers, key=lambda value: value.platform_id):
        lines.append(f"### {item.platform_id}")
        lines.append("")
        lines.append(f"- stage: `{item.stage.value}`")
        lines.append(f"- status: `{item.status_label}`")
        lines.append(f"- score_humano: `{item.human_score}`")
        lines.append(f"- technical_ready: `{item.technical_ready}`")
        lines.append(f"- panel_ready: `{item.panel_ready}`")
        lines.append(f"- same_source_ready: `{item.same_source_ready}`")
        lines.append(f"- blockers: `{item.blocker_count}`")
        lines.append(f"- warnings: `{item.warning_count}`")
        lines.append(f"- proxima_acao: {item.primary_action}")
        lines.append("")
        lines.append("Gates:")
        for gate_item in item.gates:
            if gate_item.outcome == GateOutcome.NOT_APPLICABLE:
                continue
            lines.append(f"- `{gate_item.domain.value}` {gate_item.title}: `{gate_item.outcome.value}` - {gate_item.reason}")
        lines.append("")
        lines.append("Sinais prioritarios:")
        for signal in sorted(item.signals, key=lambda value: (-severity_rank(value.severity), value.title))[:10]:
            lines.append(f"- `{signal.severity.value}` `{signal.kind.value}` {signal.title}: {signal.next_action}")
        lines.append("")
    lines.extend(["", "## Ordens executadas/formalizadas", ""])
    for item in dossier.order_justifications:
        lines.append(f"### {item.order_id}")
        lines.append("")
        lines.append(f"- tipo: `{item.order_type.value}`")
        lines.append(f"- plataforma: `{item.platform_id}`")
        lines.append(f"- status: `{item.closure_status.value}`")
        lines.append(f"- razao: {item.reason}")
        lines.append(f"- executado: {item.execution_summary}")
        if item.pending_items:
            lines.append("- pendencias:")
            lines.extend(f"  - {pending}" for pending in item.pending_items[:8])
        else:
            lines.append("- pendencias: nenhuma pendencia material associada")
        if item.resulting_orders:
            lines.append("- ordens de saida vinculadas: " + ", ".join(f"`{order}`" for order in item.resulting_orders))
        lines.append("")
    lines.extend(["", "## Pendencias consolidadas", ""])
    if dossier.pending_items:
        lines.extend(f"- {item}" for item in dossier.pending_items)
    else:
        lines.append("- Nenhuma pendencia material consolidada.")
    return "\n".join(lines).strip() + "\n"


def order_justifications_markdown(dossier: ExecutionRoundDossier) -> str:
    lines = [
        "# Justificativa das ordens de servico da rodada",
        "",
        "Este arquivo liga cada ordem tratada ao dossie operacional, sinais, gates, evidencias e ordens de saida.",
        "",
    ]
    for item in dossier.order_justifications:
        lines.append(f"## {item.order_id}")
        lines.append("")
        lines.append(f"- tipo: `{item.order_type.value}`")
        lines.append(f"- plataforma: `{item.platform_id}`")
        lines.append(f"- fechamento: `{item.closure_status.value}`")
        lines.append(f"- status compacto: `{item.compact_status}`")
        lines.append(f"- motivo: {item.reason}")
        lines.append(f"- resumo da execucao: {item.execution_summary}")
        lines.append(f"- sinais vinculados: `{len(item.linked_signals)}`")
        lines.append(f"- gates vinculados: `{len(item.linked_gates)}`")
        lines.append("")
        lines.append("Evidencias:")
        if item.evidence:
            for ref in item.evidence[:10]:
                lines.append(f"- `{ref.reference}` - {ref.summary}")
        else:
            lines.append("- nenhuma evidencia local direta encontrada")
        lines.append("")
        lines.append("Validacoes:")
        for validation in item.validation_steps:
            lines.append(f"- {validation}")
        lines.append("")
    return "\n".join(lines).strip() + "\n"


def dossier_compact_rows(dossier: ExecutionRoundDossier) -> list[list[str]]:
    rows = [["platform", "stage", "status", "score", "blockers", "panel_ready", "same_source", "primary_action"]]
    for item in sorted(dossier.platform_dossiers, key=lambda value: value.platform_id):
        rows.append(
            [
                item.platform_id,
                item.stage.value,
                item.status_label,
                str(item.human_score),
                str(item.blocker_count),
                "yes" if item.panel_ready else "no",
                "yes" if item.same_source_ready else "no",
                item.primary_action,
            ]
        )
    return rows


def write_csv_lines(rows: Sequence[Sequence[str]]) -> str:
    output: list[str] = []
    for row in rows:
        escaped: list[str] = []
        for value in row:
            text = str(value).replace('"', '""')
            if "," in text or "\n" in text or '"' in text:
                text = f'"{text}"'
            escaped.append(text)
        output.append(",".join(escaped))
    return "\n".join(output) + "\n"


def dossier_to_dict(dossier: ExecutionRoundDossier) -> dict[str, object]:
    return as_plain_data(dossier)
