from __future__ import annotations

from dataclasses import dataclass, asdict
from typing import Iterable


POLICY_VERSION = "2026-05-04.mais-humana-institutional-decisions.v1"


@dataclass(frozen=True)
class HumanImpact:
    profile_id: str
    expectation: str
    visible_signal: str
    risk_if_missing: str
    next_human_question: str


@dataclass(frozen=True)
class InstitutionalDecision:
    decision_id: str
    title: str
    human_summary: str
    operational_owner: str
    expected_mcp_evidence: tuple[str, ...]
    affected_profiles: tuple[HumanImpact, ...]
    ready_criteria: tuple[str, ...]
    blockers: tuple[str, ...]


@dataclass(frozen=True)
class PlatformAlias:
    current_name: str
    canonical_name: str
    alias_status: str
    is_platform: bool
    owner_platform_id: str
    human_note: str


@dataclass(frozen=True)
class DevelopmentActionAssessment:
    action: str
    environment: str
    allowed: bool
    decision: str
    required_controls: tuple[str, ...]
    human_explanation: str


@dataclass(frozen=True)
class HumanReadinessSignal:
    decision_id: str
    status: str
    visible_to_profile: str
    evidence_fields: tuple[str, ...]
    missing_for_human_confidence: tuple[str, ...]
    next_question: str


@dataclass(frozen=True)
class HumanPolicyPanel:
    policy_version: str
    ok: bool
    generated_at: str
    signals: tuple[HumanReadinessSignal, ...]
    summary: dict[str, int]


@dataclass(frozen=True)
class HumanEvidenceGap:
    decision_id: str
    status: str
    expected_evidence: tuple[str, ...]
    present_evidence: tuple[str, ...]
    missing_evidence: tuple[str, ...]
    visible_to_profile: str
    human_impact: str
    next_question: str


def _impact(profile_id: str, expectation: str, signal: str, risk: str, question: str) -> HumanImpact:
    return HumanImpact(
        profile_id=profile_id,
        expectation=expectation,
        visible_signal=signal,
        risk_if_missing=risk,
        next_human_question=question,
    )


INSTITUTIONAL_DECISIONS: tuple[InstitutionalDecision, ...] = (
    InstitutionalDecision(
        decision_id="docs_full_operational_platform",
        title="Docs pleno operacional",
        human_summary="Docs deixa de ser catalogo passivo e passa a responder suficiência documental com fonte, autoridade e lacuna.",
        operational_owner="tudo-para-ia-docs-plataform",
        expected_mcp_evidence=("documentId", "sourceHash", "authority", "truthState", "missingTopics"),
        affected_profiles=(
            _impact(
                "operador_gpt",
                "Encontrar resposta institucional sem adivinhar.",
                "status found/partial/not_found/conflicting",
                "GPT responde com base incompleta ou inventada.",
                "A resposta existe na Docs ou precisa virar pendencia?",
            ),
            _impact(
                "suporte_cliente",
                "Explicar regra ao cliente com base rastreavel.",
                "documento com authority e sourceHash",
                "Suporte passa orientacao sem lastro.",
                "Qual documento posso citar como fonte?",
            ),
        ),
        ready_criteria=(
            "Docs publica pelo menos uma tool responseReady.",
            "Consulta de suficiência gera pendencia quando nao encontra resposta.",
            "Resposta informa autoridade, fonte e hash.",
        ),
        blockers=("docs_catalog_only_sem_excecao", "lacuna_sem_pedido_documental"),
    ),
    InstitutionalDecision(
        decision_id="mcp_required_cross_platform_acceptance",
        title="Aceite externo via MCP",
        human_summary="Quando uma plataforma entrega dado, contrato, aceite ou evidencia para outra, o MCP precisa aparecer no caminho.",
        operational_owner="tudo-para-ia-mcps-internos-plataform",
        expected_mcp_evidence=("originPlatformId", "destinationPlatformId", "contractHash", "sourceRecordsHash", "permissionId", "evidenceId"),
        affected_profiles=(
            _impact(
                "admin_ecossistema",
                "Saber quem aceitou o que e com qual contrato.",
                "rota platform -> MCP -> platform",
                "Aceite bilateral fica sem auditoria cross-platform.",
                "O aceite passou pelo MCP e tem hash?",
            ),
            _impact(
                "auditor",
                "Comparar origem, destino, permissao e evidencia.",
                "traceId + auditId + evidenceId",
                "Nao ha prova de permissao do consumidor.",
                "Qual evidencia prova a circulacao?",
            ),
        ),
        ready_criteria=(
            "Envelope possui origem, destino, contrato, hashes e permissao.",
            "MCP gera etiqueta de entrada e saida.",
            "Evidencia de aceite fica acessivel ao GPT e ao painel.",
        ),
        blockers=("mcp_bypass", "hash_ausente", "permissao_ausente"),
    ),
    InstitutionalDecision(
        decision_id="ten_year_institutional_retention",
        title="Retencao institucional 10 anos",
        human_summary="Prova de decisao, execucao, aceite, contrato, evidencia e auditoria deve ser preservada por 10 anos.",
        operational_owner="nucleo-gestao-operacional",
        expected_mcp_evidence=("retentionClass", "retentionYears", "legalHold", "rollbackPlan", "beforeAfterEvidence"),
        affected_profiles=(
            _impact(
                "gestor_operacional",
                "Confiar que prova institucional nao desaparece.",
                "retentionClass institutional_10y",
                "Decisao futura perde contexto e responsabilidade.",
                "Esta evidencia esta retida por 10 anos?",
            ),
            _impact(
                "compliance",
                "Bloquear descarte sob legal hold.",
                "legalHoldBlocksDiscard=true",
                "Descarte indevido vira risco juridico.",
                "Ha legal hold ou rollback antes da operacao sensivel?",
            ),
        ),
        ready_criteria=(
            "Evidence, contrato, aceite e auditoria usam classe 10 anos.",
            "Operacao sensivel tem rollback.",
            "Evidencia antes/depois existe quando aplicavel.",
        ),
        blockers=("retencao_curta_para_prova_unica", "operacao_sensivel_sem_rollback"),
    ),
    InstitutionalDecision(
        decision_id="live_sensitive_data_purge_forbidden",
        title="Expurgo de dado vivo sensivel vetado",
        human_summary="Limpeza operacional e permitida; apagar dado vivo sensivel nao e politica atual.",
        operational_owner="nucleo-gestao-operacional",
        expected_mcp_evidence=("cleanupClassification", "allowedTerms", "forbiddenTerms", "sensitiveDataCheck"),
        affected_profiles=(
            _impact(
                "operador_codex",
                "Limpar cache/build sem chamar isso de expurgo.",
                "classification operational_cleanup",
                "Artefato vivo pode ser apagado por erro de nomenclatura.",
                "Isto e limpeza operacional ou dado vivo sensivel?",
            ),
            _impact(
                "responsavel_cliente",
                "Garantir que dados reais nao sejam apagados sem decisao.",
                "blocked_live_sensitive_purge",
                "Perda de dado sensivel ou evidencia.",
                "Existe rollback e inventario antes de qualquer descarte?",
            ),
        ),
        ready_criteria=(
            "Cache/build/dist/coverage sao classificados como limpeza operacional.",
            "Dado vivo sensivel fica bloqueado.",
            "Projeto inchado passa por inventario de peso.",
        ),
        blockers=("expurgo_dado_vivo", "limpeza_chamada_expurgo"),
    ),
    InstitutionalDecision(
        decision_id="canonical_platform_names",
        title="Nomes canonicos e Biblioteca Privada",
        human_summary="Plataformas comuns terminam em platform; Biblioteca Privada nao aparece como plataforma autonoma.",
        operational_owner="nucleo-gestao-operacional",
        expected_mcp_evidence=("canonicalName", "aliasStatus", "ownerPlatformId", "providerId"),
        affected_profiles=(
            _impact(
                "admin_ecossistema",
                "Ver nomes sem duplicidade entre plataforma real e alias legado.",
                "aliasStatus canonical/legacy_alias",
                "Matriz cria plataforma duplicada.",
                "Este nome e canonico ou alias legado?",
            ),
            _impact(
                "produto_integracoes",
                "Tratar Biblioteca Privada como produto/modulo de Integracoes.",
                "isPlatform=false",
                "Biblioteca ganha owner errado e contrato paralelo.",
                "Quem e o owner real deste modulo?",
            ),
        ),
        ready_criteria=(
            "Novos nomes terminam em platform.",
            "plataform/plataforma permanecem apenas como aliases legados.",
            "Biblioteca Privada nao recebe ownerPlatformId proprio.",
        ),
        blockers=("novo_nome_plataform", "biblioteca_como_plataforma_autonoma"),
    ),
    InstitutionalDecision(
        decision_id="development_execution_for_gpt_codex",
        title="Execucao ampla GPT/Codex em desenvolvimento",
        human_summary="GPT e Codex podem simular e operar em desenvolvimento quando houver rastreabilidade e sem efeito externo real.",
        operational_owner="nucleo-gestao-operacional",
        expected_mcp_evidence=("truthState", "actorId", "auditId", "dryRun", "environment"),
        affected_profiles=(
            _impact(
                "operador_gpt",
                "Executar fluxo de teste sem pedir autorizacao indevida.",
                "truthState simulated/test",
                "Rodada fica passiva e nao avanca.",
                "Esta acao e reversivel, auditavel e de desenvolvimento?",
            ),
            _impact(
                "gestor_risco",
                "Bloquear destruicao, segredo e efeito externo real.",
                "decision authorization_required/blocked_secret_exposure",
                "Acao real pode escapar sem aprovacao.",
                "Existe autorizacao explicita, rollback e evidencia antes/depois?",
            ),
        ),
        ready_criteria=(
            "Simulacao usa truthState, actorId e auditId.",
            "dryRun aparece quando aplicavel.",
            "Efeito externo real exige autorizacao explicita.",
        ),
        blockers=("segredo_exposto", "efeito_externo_sem_autorizacao", "acao_destrutiva_sem_rollback"),
    ),
)


PLATFORM_ALIASES: tuple[PlatformAlias, ...] = (
    PlatformAlias(
        current_name="tudo-para-ia-docs-plataform",
        canonical_name="tudo-para-ia-docs-platform",
        alias_status="legacy_alias",
        is_platform=True,
        owner_platform_id="tudo-para-ia-docs-platform",
        human_note="Alias legado deve permanecer rastreado ate decisao de rename.",
    ),
    PlatformAlias(
        current_name="tudo-para-ia-integracoes-plataform",
        canonical_name="tudo-para-ia-integracoes-platform",
        alias_status="legacy_alias",
        is_platform=True,
        owner_platform_id="tudo-para-ia-integracoes-platform",
        human_note="Alias legado nao deve virar novo providerId.",
    ),
    PlatformAlias(
        current_name="tudo-para-ia-mcps-internos-plataform",
        canonical_name="tudo-para-ia-mcps-internos-platform",
        alias_status="legacy_alias",
        is_platform=True,
        owner_platform_id="tudo-para-ia-mcps-internos-platform",
        human_note="MCP segue como nucleo administrativo mesmo enquanto o repo real usa alias legado.",
    ),
    PlatformAlias(
        current_name="tudo-para-ia-biblioteca-privada",
        canonical_name="tudo-para-ia-integracoes-platform",
        alias_status="module_not_platform",
        is_platform=False,
        owner_platform_id="tudo-para-ia-integracoes-platform",
        human_note="Biblioteca Privada deve ser produto/app/modulo de Integracoes.",
    ),
)


def list_institutional_decisions() -> tuple[InstitutionalDecision, ...]:
    return INSTITUTIONAL_DECISIONS


def summarize_decisions(decisions: Iterable[InstitutionalDecision] = INSTITUTIONAL_DECISIONS) -> dict[str, object]:
    items = tuple(decisions)
    profile_ids = sorted({impact.profile_id for decision in items for impact in decision.affected_profiles})
    return {
        "policy_version": POLICY_VERSION,
        "decisions": len(items),
        "profiles": profile_ids,
        "profile_count": len(profile_ids),
        "expected_mcp_evidence_fields": sorted({field for decision in items for field in decision.expected_mcp_evidence}),
        "blockers": sorted({blocker for decision in items for blocker in decision.blockers}),
        "ready_criteria_count": sum(len(decision.ready_criteria) for decision in items),
    }


def build_human_decision_matrix() -> dict[str, object]:
    decisions = list_institutional_decisions()
    return {
        "ok": True,
        "policy_version": POLICY_VERSION,
        "generated_at": "2026-05-04T00:00:00.000Z",
        "summary": summarize_decisions(decisions),
        "decisions": [asdict(decision) for decision in decisions],
        "platform_aliases": [asdict(alias) for alias in PLATFORM_ALIASES],
        "human_boundary": (
            "Mais Humana traduz impacto e pergunta humana; Docs/MCP continuam donos da fonte "
            "e a UI continua renderizadora same-source."
        ),
    }


def classify_development_action_for_humans(action: str, environment: str = "development") -> DevelopmentActionAssessment:
    normalized = action.casefold()
    secret_words = ("secret", "token", "cookie", "auth state", "segredo", "qr bruto")
    external_words = ("send real message", "charge customer", "production", "delete database", "drop table", "apagar banco")
    if any(word in normalized for word in secret_words):
        return DevelopmentActionAssessment(
            action=action,
            environment=environment,
            allowed=False,
            decision="blocked_secret_exposure",
            required_controls=("redaction", "credentialRef", "auditId"),
            human_explanation="Valor de segredo nunca deve aparecer para suporte, GPT, Codex, relatorio ou SQL.",
        )
    if environment != "development" or any(word in normalized for word in external_words):
        return DevelopmentActionAssessment(
            action=action,
            environment=environment,
            allowed=False,
            decision="explicit_authorization_required",
            required_controls=("authorization", "rollbackPlan", "beforeAfterEvidence", "auditId"),
            human_explanation="Efeito externo real, producao ou acao destrutiva exige autorizacao explicita.",
        )
    return DevelopmentActionAssessment(
        action=action,
        environment=environment,
        allowed=True,
        decision="development_simulation_allowed",
        required_controls=("truthState", "actorId", "auditId", "dryRunWhenApplicable"),
        human_explanation="Simulacao auditavel em desenvolvimento pode ser executada para avancar a rodada.",
    )


def build_human_policy_readiness_panel(
    available_evidence: dict[str, tuple[str, ...]] | None = None,
) -> HumanPolicyPanel:
    default_evidence_by_decision = {
        "docs_full_operational_platform": ("documentId", "sourceHash", "authority", "truthState", "missingTopics"),
        "mcp_required_cross_platform_acceptance": (
            "originPlatformId",
            "destinationPlatformId",
            "contractHash",
            "sourceRecordsHash",
            "permissionId",
            "evidenceId",
            "traceId",
            "auditId",
        ),
        "ten_year_institutional_retention": (
            "retentionClass",
            "retentionYears",
            "legalHold",
            "rollbackPlan",
            "beforeAfterEvidence",
        ),
        "live_sensitive_data_purge_forbidden": (
            "cleanupClassification",
            "allowedTerms",
            "forbiddenTerms",
            "sensitiveDataCheck",
        ),
        "canonical_platform_names": ("canonicalName", "aliasStatus", "ownerPlatformId", "providerId"),
        "development_execution_for_gpt_codex": ("truthState", "actorId", "auditId", "dryRun", "environment"),
    }
    evidence_by_decision = {
        **default_evidence_by_decision,
        **(available_evidence or {}),
    }
    signals: list[HumanReadinessSignal] = []
    for decision in INSTITUTIONAL_DECISIONS:
        evidence = tuple(evidence_by_decision.get(decision.decision_id, ()))
        expected = set(decision.expected_mcp_evidence)
        missing = tuple(sorted(expected.difference(evidence)))
        status = "ready" if not missing else "partial" if evidence else "blocked"
        profile = decision.affected_profiles[0]
        signals.append(
            HumanReadinessSignal(
                decision_id=decision.decision_id,
                status=status,
                visible_to_profile=profile.profile_id,
                evidence_fields=evidence,
                missing_for_human_confidence=missing,
                next_question=profile.next_human_question if missing else "A evidencia ja permite resposta humana equivalente?",
            )
        )
    summary = {
        "ready": sum(1 for signal in signals if signal.status == "ready"),
        "partial": sum(1 for signal in signals if signal.status == "partial"),
        "blocked": sum(1 for signal in signals if signal.status == "blocked"),
        "total": len(signals),
    }
    return HumanPolicyPanel(
        policy_version=POLICY_VERSION,
        ok=summary["blocked"] == 0,
        generated_at="2026-05-04T00:00:00.000Z",
        signals=tuple(signals),
        summary=summary,
    )


def export_human_policy_readiness_panel(
    available_evidence: dict[str, tuple[str, ...]] | None = None,
) -> dict[str, object]:
    panel = build_human_policy_readiness_panel(available_evidence)
    return {
        "ok": panel.ok,
        "policy_version": panel.policy_version,
        "generated_at": panel.generated_at,
        "summary": panel.summary,
        "signals": [asdict(signal) for signal in panel.signals],
    }


def build_human_institutional_evidence_gap_analysis(
    available_evidence: dict[str, tuple[str, ...]] | None = None,
) -> dict[str, object]:
    evidence_by_decision = available_evidence or {
        decision.decision_id: decision.expected_mcp_evidence
        for decision in INSTITUTIONAL_DECISIONS
    }
    gaps: list[HumanEvidenceGap] = []
    for decision in INSTITUTIONAL_DECISIONS:
        expected = tuple(decision.expected_mcp_evidence)
        present = tuple(evidence_by_decision.get(decision.decision_id, ()))
        present_set = set(present)
        missing = tuple(field for field in expected if field not in present_set)
        status = "ready" if not missing else "partial" if present else "blocked"
        profile = decision.affected_profiles[0]
        gaps.append(
            HumanEvidenceGap(
                decision_id=decision.decision_id,
                status=status,
                expected_evidence=expected,
                present_evidence=present,
                missing_evidence=missing,
                visible_to_profile=profile.profile_id,
                human_impact=profile.risk_if_missing if missing else profile.visible_signal,
                next_question=profile.next_human_question,
            )
        )
    summary = {
        "ready": sum(1 for gap in gaps if gap.status == "ready"),
        "partial": sum(1 for gap in gaps if gap.status == "partial"),
        "blocked": sum(1 for gap in gaps if gap.status == "blocked"),
        "missing_evidence": sum(len(gap.missing_evidence) for gap in gaps),
        "total": len(gaps),
    }
    return {
        "ok": summary["partial"] == 0 and summary["blocked"] == 0,
        "policy_version": POLICY_VERSION,
        "generated_at": "2026-05-04T00:00:00.000Z",
        "gaps": [asdict(gap) for gap in gaps],
        "summary": summary,
        "human_boundary": (
            "Mais Humana avalia confianca e pergunta humana; MCP/Docs continuam donos da prova "
            "e a UI continua responsavel por renderizar a mesma fonte."
        ),
    }
