"""Admin review helpers for close-certification runs and reconciliation packets."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field, computed_field

from g_gremlin.autopilot.ai_provider import (
    AIProviderConfig,
    AIProviderStatus,
    AIProviderStore,
    ModelCapabilityProfileV1,
    WorkflowModelRequirementV1,
    build_model_capability_profile,
    model_satisfies_workflow,
    workflow_model_requirement,
)

from .capability_promotion import (
    DealDeskCapabilityDraftPromotionPreviewV1,
    DealDeskCapabilityDraftPromotionReceiptSummaryV1,
    build_capability_draft_promotion_preview,
    summarize_capability_draft_promotion_receipt,
)
from .config import DealDeskConfig, DealDeskConfigError
from .capability_refresh import DealDeskCapabilityRefreshReceiptSummaryV1, summarize_capability_refresh_receipt
from .extraction import load_document_vision_runtime_capability_profile
from .history import CloseCertificationRunV1
from .profiles import QuoteEnvironmentProfileV1
from .reconciliation import ReconciliationPacketV1
from .store import DealDeskStore
from .triggers import PostCloseTriggerRunV1, PostCloseTriggerV1, build_post_close_trigger_registry


class _ReviewModel(BaseModel):
    model_config = ConfigDict(extra="ignore")

    def to_dict(self) -> dict[str, Any]:
        return self.model_dump(exclude_none=False, by_alias=True)


class CloseReviewArtifactPathsV1(_ReviewModel):
    close_certification_run_path: str | None = None
    reconciliation_packet_path: str | None = None


class CloseReviewListItemV1(_ReviewModel):
    version: int = 1
    run_id: str
    opportunity_id: str
    crm_org_id: str
    org_alias: str | None = None
    entrypoint: str
    triggered_at: str
    close_submitted_at: str | None = None
    close_workflow_state: str
    overall_disposition: str
    reconciliation_packet_id: str
    packet_generated_at: str | None = None
    packet_present: bool = False
    blocker_count: int = 0
    approval_required_count: int = 0
    auto_fixable_count: int = 0
    discrepancy_count: int = 0
    document_evidence_status: str = "not_required"
    document_extraction_status: str = "not_attempted"
    document_discrepancy_count: int = 0
    document_evidence_count: int = 0
    document_candidate_count: int = 0
    document_contract_candidate_count: int = 0
    document_summary: str = ""
    document_evidence_summary: str = ""
    document_method_counts: dict[str, int] = Field(default_factory=dict)
    quote_integrity_status: str = "not_available"
    quote_integrity_discrepancy_count: int = 0
    quote_integrity_summary: str = ""
    progression_status: str = "not_attempted"
    post_close_status: str = "not_attempted"
    review_summary: str = ""

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "CloseReviewListItem/v1"


class CloseReviewListResponseV1(_ReviewModel):
    version: int = 1
    status: str
    count: int = 0
    items: list[CloseReviewListItemV1] = Field(default_factory=list)

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "CloseReviewListResponse/v1"


class CloseReviewDetailV1(_ReviewModel):
    version: int = 1
    run: CloseCertificationRunV1
    packet: ReconciliationPacketV1 | None = None
    packet_present: bool = False
    review_summary: str = ""
    discrepancy_count: int = 0
    evidence_ref_count: int = 0
    post_close_trigger_count: int = 0
    post_close_trigger_runs: list[PostCloseTriggerRunV1] = Field(default_factory=list)
    artifact_paths: CloseReviewArtifactPathsV1 = Field(default_factory=CloseReviewArtifactPathsV1)

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "CloseReviewDetail/v1"


class PostCloseTriggerRegistryResponseV1(_ReviewModel):
    version: int = 1
    status: str
    config_source: str = "missing"
    config_path: str | None = None
    crm_org_id: str | None = None
    org_alias: str | None = None
    certification_steps: list["CertificationStepPolicyV1"] = Field(default_factory=list)
    count: int = 0
    items: list[PostCloseTriggerV1] = Field(default_factory=list)

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "PostCloseTriggerRegistryResponse/v1"


class CertificationStepPolicyV1(_ReviewModel):
    version: int = 1
    step_key: str
    label: str
    description: str
    mode: str = "auto"
    notes: list[str] = Field(default_factory=list)

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "CertificationStepPolicy/v1"


class DealDeskCapabilityArtifactPathsV1(_ReviewModel):
    quote_environment_profile_path: str | None = None


class DealDeskWorkflowCapabilityCheckV1(_ReviewModel):
    version: int = 1
    workflow: str
    label: str
    description: str
    status: str
    requirement: WorkflowModelRequirementV1
    capability_profile: ModelCapabilityProfileV1 | None = None
    unmet_requirements: list[str] = Field(default_factory=list)
    notes: list[str] = Field(default_factory=list)

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "DealDeskWorkflowCapabilityCheck/v1"


class DealDeskCapabilitySummaryResponseV1(_ReviewModel):
    version: int = 1
    status: str
    crm_org_id: str | None = None
    org_alias: str | None = None
    environment_profile_present: bool = False
    adapter_profile_present: bool = False
    adapter_execution_verification_state: Literal[
        "not_available",
        "inferred_only",
        "partially_verified",
        "verified",
    ] = "not_available"
    adapter_execution_verified_capability_count: int = 0
    adapter_inferred_only_capability_count: int = 0
    environment_profile: QuoteEnvironmentProfileV1 | None = None
    ai_status: AIProviderStatus | None = None
    workflow_checks: list[DealDeskWorkflowCapabilityCheckV1] = Field(default_factory=list)
    latest_refresh_receipt: DealDeskCapabilityRefreshReceiptSummaryV1 | None = None
    latest_promotion_receipt: DealDeskCapabilityDraftPromotionReceiptSummaryV1 | None = None
    draft_promotion_preview: DealDeskCapabilityDraftPromotionPreviewV1 | None = None
    artifact_paths: DealDeskCapabilityArtifactPathsV1 = Field(default_factory=DealDeskCapabilityArtifactPathsV1)

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "DealDeskCapabilitySummaryResponse/v1"


def build_close_review_index(
    state_dir: str | Path | None,
    *,
    limit: int = 25,
    overall_disposition: str | None = None,
    entrypoint: str | None = None,
    query: str | None = None,
    org_alias: str | None = None,
) -> CloseReviewListResponseV1:
    store = DealDeskStore(state_dir)
    normalized_disposition = _normalize_token(overall_disposition)
    normalized_entrypoint = _normalize_token(entrypoint)
    normalized_query = _normalize_token(query)
    items: list[CloseReviewListItemV1] = []
    runs = sorted(
        store.list_close_certification_runs(org_alias=org_alias),
        key=lambda item: str(item.triggered_at or ""),
        reverse=True,
    )
    for run in runs:
        if normalized_disposition and _normalize_token(run.overall_disposition) != normalized_disposition:
            continue
        if normalized_entrypoint and _normalize_token(run.entrypoint) != normalized_entrypoint:
            continue
        packet = _load_packet_for_run(store=store, run=run)
        if normalized_query and not _matches_query(run=run, packet=packet, query=normalized_query):
            continue
        items.append(_list_item_from_run(run=run, packet=packet))
        if len(items) >= max(1, min(int(limit), 200)):
            break
    return CloseReviewListResponseV1(
        status="ok" if items else "empty",
        count=len(items),
        items=items,
    )


def load_close_review_detail(
    state_dir: str | Path | None,
    *,
    run_id: str,
    org_alias: str | None = None,
) -> CloseReviewDetailV1:
    store = DealDeskStore(state_dir)
    run = store.load_close_certification_run(run_id=run_id, org_alias=org_alias)
    if run is None:
        raise KeyError(f"Close review run {run_id} was not found.")
    packet = _load_packet_for_run(store=store, run=run)
    post_close_trigger_runs = _load_post_close_trigger_runs(store=store, run=run)
    discrepancies = list(packet.discrepancies) if packet is not None else []
    evidence_ref_count = sum(len(item.evidence) for item in discrepancies)
    return CloseReviewDetailV1(
        run=run,
        packet=packet,
        packet_present=packet is not None,
        review_summary=_review_summary(run=run, packet=packet),
        discrepancy_count=len(discrepancies),
        evidence_ref_count=evidence_ref_count,
        post_close_trigger_count=len(post_close_trigger_runs),
        post_close_trigger_runs=post_close_trigger_runs,
        artifact_paths=CloseReviewArtifactPathsV1(
            close_certification_run_path=str(
                store.close_certification_run_path(
                    opportunity_id=run.opportunity_id,
                    run_id=run.run_id,
                    crm_org_id=run.crm_org_id,
                    org_alias=run.org_alias,
                )
            ),
            reconciliation_packet_path=str(run.reconciliation_packet_path or "").strip() or None,
        ),
    )


def load_dealdesk_capability_summary(
    state_dir: str | Path | None,
    *,
    crm_org_id: str | None = None,
    org_alias: str | None = None,
) -> DealDeskCapabilitySummaryResponseV1:
    store = DealDeskStore(state_dir)
    environment_profile = store.load_quote_environment_profile(
        crm_org_id=crm_org_id,
        org_alias=org_alias,
    )
    ai_store = AIProviderStore(store.state_dir)
    ai_status = ai_store.load_status()
    ai_config = ai_store.load_config()
    latest_refresh_receipt_model = store.find_latest_capability_refresh_receipt(
        crm_org_id=crm_org_id,
        org_alias=org_alias,
    )
    latest_refresh_receipt = summarize_capability_refresh_receipt(
        latest_refresh_receipt_model
    )
    latest_promotion_receipt = summarize_capability_draft_promotion_receipt(
        store.find_latest_capability_draft_promotion_receipt(
            crm_org_id=crm_org_id,
            org_alias=org_alias,
        )
    )
    draft_promotion_preview = build_capability_draft_promotion_preview(
        store.state_dir,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
        refresh_receipt=latest_refresh_receipt_model,
    )
    workflow_checks = _build_workflow_capability_checks(
        state_dir=store.state_dir,
        ai_config=ai_config,
    )
    if environment_profile is not None and ai_status.configured:
        status = "ok"
    elif environment_profile is not None or ai_status.configured:
        status = "partial"
    else:
        status = "empty"
    adapter_profile = (
        environment_profile.adapter_capability_profile
        if environment_profile is not None
        else None
    )
    return DealDeskCapabilitySummaryResponseV1(
        status=status,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
        environment_profile_present=environment_profile is not None,
        adapter_profile_present=adapter_profile is not None,
        adapter_execution_verification_state=(
            adapter_profile.execution_verification_state
            if adapter_profile is not None
            else "not_available"
        ),
        adapter_execution_verified_capability_count=(
            len(adapter_profile.execution_verified_capabilities or [])
            if adapter_profile is not None
            else 0
        ),
        adapter_inferred_only_capability_count=(
            len(adapter_profile.inferred_only_capabilities or [])
            if adapter_profile is not None
            else 0
        ),
        environment_profile=environment_profile,
        ai_status=ai_status,
        workflow_checks=workflow_checks,
        latest_refresh_receipt=latest_refresh_receipt,
        latest_promotion_receipt=latest_promotion_receipt,
        draft_promotion_preview=draft_promotion_preview,
        artifact_paths=DealDeskCapabilityArtifactPathsV1(
            quote_environment_profile_path=str(
                store.quote_environment_profile_path(
                    crm_org_id=crm_org_id,
                    org_alias=org_alias,
                )
            ),
        ),
    )


def load_post_close_trigger_registry(
    state_dir: str | Path | None,
    *,
    crm_org_id: str | None = None,
    org_alias: str | None = None,
) -> PostCloseTriggerRegistryResponseV1:
    store = DealDeskStore(state_dir)
    try:
        config, config_source, config_path = store.load_effective_config_info(
            crm_org_id=crm_org_id,
            org_alias=org_alias,
        )
    except Exception as exc:
        if "not found" in str(exc).lower():
            return PostCloseTriggerRegistryResponseV1(
                status="missing",
                config_source="missing",
                config_path=None,
                crm_org_id=crm_org_id,
                org_alias=org_alias,
                count=0,
                items=[],
            )
        raise DealDeskConfigError(f"Failed to load effective Deal Desk config: {exc}") from exc

    items = _enrich_trigger_registry_with_pending_previews(
        items=build_post_close_trigger_registry(config),
        store=store,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
    )
    return PostCloseTriggerRegistryResponseV1(
        status="ok",
        config_source=config_source,
        config_path=config_path,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
        certification_steps=_build_certification_step_registry(config),
        count=len(items),
        items=items,
    )


def _list_item_from_run(
    *,
    run: CloseCertificationRunV1,
    packet: ReconciliationPacketV1 | None,
) -> CloseReviewListItemV1:
    return CloseReviewListItemV1(
        run_id=run.run_id,
        opportunity_id=run.opportunity_id,
        crm_org_id=run.crm_org_id,
        org_alias=run.org_alias,
        entrypoint=run.entrypoint,
        triggered_at=run.triggered_at,
        close_submitted_at=run.close_submitted_at,
        close_workflow_state=run.close_workflow_state,
        overall_disposition=run.overall_disposition,
        reconciliation_packet_id=run.reconciliation_packet_id,
        packet_generated_at=(packet.generated_at if packet is not None else None),
        packet_present=packet is not None,
        blocker_count=int(run.blocker_count or 0),
        approval_required_count=int(run.approval_required_count or 0),
        auto_fixable_count=int(run.auto_fixable_count or 0),
        discrepancy_count=(len(packet.discrepancies) if packet is not None else 0),
        document_evidence_status=run.document_evidence_status,
        document_extraction_status=run.document_extraction_status,
        document_discrepancy_count=int(run.document_discrepancy_count or 0),
        document_evidence_count=int(run.document_evidence_count or 0),
        document_candidate_count=(
            int(packet.document_candidate_count or 0) if packet is not None else 0
        ),
        document_contract_candidate_count=(
            int(packet.document_contract_candidate_count or 0) if packet is not None else 0
        ),
        document_summary=(packet.document_summary if packet is not None else run.document_summary),
        document_evidence_summary=(
            packet.document_evidence_summary if packet is not None else run.document_evidence_summary
        ),
        document_method_counts=dict(run.document_extraction_method_counts or {}),
        quote_integrity_status=run.quote_integrity_status,
        quote_integrity_discrepancy_count=int(run.quote_integrity_discrepancy_count or 0),
        quote_integrity_summary=(
            packet.quote_integrity_summary if packet is not None else run.quote_integrity_summary
        ),
        progression_status=run.progression_status,
        post_close_status=run.post_close_status,
        review_summary=_review_summary(run=run, packet=packet),
    )


def _load_packet_for_run(
    *,
    store: DealDeskStore,
    run: CloseCertificationRunV1,
) -> ReconciliationPacketV1 | None:
    packet = store.load_reconciliation_packet(
        opportunity_id=run.opportunity_id,
        packet_id=run.reconciliation_packet_id,
        crm_org_id=run.crm_org_id,
        org_alias=run.org_alias,
    )
    if packet is not None:
        return packet
    raw_path = str(run.reconciliation_packet_path or "").strip()
    if not raw_path:
        return None
    path = Path(raw_path)
    if not path.exists():
        return None
    try:
        payload = json.loads(path.read_text(encoding="utf-8"))
    except Exception:
        return None
    return ReconciliationPacketV1.model_validate(payload)


def _load_post_close_trigger_runs(
    *,
    store: DealDeskStore,
    run: CloseCertificationRunV1,
) -> list[PostCloseTriggerRunV1]:
    trigger_runs: list[PostCloseTriggerRunV1] = []
    seen: set[str] = set()
    for trigger_run_id in run.post_close_trigger_run_ids:
        normalized_run_id = str(trigger_run_id or "").strip()
        if not normalized_run_id or normalized_run_id in seen:
            continue
        item = store.load_post_close_trigger_run(
            run_id=normalized_run_id,
            opportunity_id=run.opportunity_id,
            crm_org_id=run.crm_org_id,
            org_alias=run.org_alias,
        )
        if item is None:
            continue
        trigger_runs.append(item)
        seen.add(item.run_id)
    return trigger_runs


def _enrich_trigger_registry_with_pending_previews(
    *,
    items: list[PostCloseTriggerV1],
    store: DealDeskStore,
    crm_org_id: str | None,
    org_alias: str | None,
) -> list[PostCloseTriggerV1]:
    summaries = _pending_preview_summary_by_trigger(
        store=store,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
    )
    enriched: list[PostCloseTriggerV1] = []
    for item in items:
        summary = summaries.get(item.trigger_key, {})
        enriched.append(
            item.model_copy(
                update={
                    "pending_preview_count": int(summary.get("count") or 0),
                    "latest_pending_preview_run_id": summary.get("run_id"),
                    "latest_pending_opportunity_id": summary.get("opportunity_id"),
                    "latest_pending_plan_path": summary.get("pending_plan_path"),
                    "latest_pending_observed_at": summary.get("observed_at"),
                }
            )
        )
    return enriched


def _pending_preview_summary_by_trigger(
    *,
    store: DealDeskStore,
    crm_org_id: str | None,
    org_alias: str | None,
) -> dict[str, dict[str, Any]]:
    grouped: dict[str, dict[str, Any]] = {}
    for run in store.list_post_close_trigger_runs(crm_org_id=crm_org_id, org_alias=org_alias):
        if run.mode != "preview":
            continue
        if run.status != "preview_generated":
            continue
        if not str(run.pending_run_id or "").strip() and not str(run.pending_plan_path or "").strip():
            continue
        key = str(run.trigger_key or "").strip()
        if not key:
            continue
        summary = grouped.setdefault(key, {"count": 0, "latest_sort": "", "run_id": None, "opportunity_id": None, "pending_plan_path": None, "observed_at": None})
        summary["count"] = int(summary.get("count") or 0) + 1
        observed_at = str(run.observed_at or "")
        if observed_at >= str(summary.get("latest_sort") or ""):
            summary["latest_sort"] = observed_at
            summary["run_id"] = run.run_id
            summary["opportunity_id"] = run.opportunity_id
            summary["pending_plan_path"] = run.pending_plan_path
            summary["observed_at"] = run.observed_at
    return grouped


def _build_certification_step_registry(config: DealDeskConfig) -> list[CertificationStepPolicyV1]:
    return [
        CertificationStepPolicyV1(
            step_key="close_progression",
            label="Close progression",
            description="Advance the opportunity to Closed Won after certification passes.",
            mode=str(config.certification_steps.close_progression.mode or "auto").strip().lower() or "auto",
            notes=[
                "Auto executes final close progression subject to autonomy policy.",
                "Approve stages a pending plan for operator approval.",
                "Skip leaves the opportunity certified without moving the stage.",
            ],
        ),
        CertificationStepPolicyV1(
            step_key="post_close_execution",
            label="Post-close execution",
            description="Run post-close trigger work after close progression completes.",
            mode=str(config.certification_steps.post_close_execution.mode or "auto").strip().lower() or "auto",
            notes=[
                "Auto executes post-close work while honoring trigger preview and failure policies.",
                "Approve stages live post-close work for operator approval.",
                "Skip prevents post-close execution in this Deal Desk scope.",
            ],
        ),
    ]


def _build_workflow_capability_checks(
    *,
    state_dir: Path,
    ai_config: AIProviderConfig | None,
) -> list[DealDeskWorkflowCapabilityCheckV1]:
    return [
        _build_primary_workflow_check(
            workflow="close_certification",
            label="Close certification",
            description="Primary AI connection used for structured close-certification reasoning without document inputs.",
            ai_config=ai_config,
        ),
        _build_document_workflow_check(state_dir=state_dir),
    ]


def _build_primary_workflow_check(
    *,
    workflow: str,
    label: str,
    description: str,
    ai_config: AIProviderConfig | None,
) -> DealDeskWorkflowCapabilityCheckV1:
    requirement = workflow_model_requirement(workflow)
    profile = None
    if ai_config is not None:
        profile = build_model_capability_profile(
            ai_config,
            connection_id="primary",
            supports_structured_output=True,
            supports_document_inputs=False,
            capability_source="declared",
        )
    unmet_requirements = _workflow_unmet_requirements(profile=profile, requirement=requirement)
    if profile is None:
        status = "not_configured"
    elif model_satisfies_workflow(profile, requirement):
        status = "compatible"
    else:
        status = "incompatible"
    return DealDeskWorkflowCapabilityCheckV1(
        workflow=workflow,
        label=label,
        description=description,
        status=status,
        requirement=requirement,
        capability_profile=profile,
        unmet_requirements=unmet_requirements,
        notes=[
            "Compatibility is derived from the current primary AI provider/model and the workflow requirement contract.",
        ],
    )


def _build_document_workflow_check(*, state_dir: Path) -> DealDeskWorkflowCapabilityCheckV1:
    requirement = workflow_model_requirement("document_reconciliation")
    profile = load_document_vision_runtime_capability_profile(state_dir)
    unmet_requirements = _workflow_unmet_requirements(profile=profile, requirement=requirement)
    if profile is None:
        status = "not_configured"
    elif model_satisfies_workflow(profile, requirement):
        status = "compatible"
    else:
        status = "incompatible"
    return DealDeskWorkflowCapabilityCheckV1(
        workflow="document_reconciliation",
        label="Document reconciliation",
        description="Document-heavy reconciliation path used by structured document verification and vision fallback.",
        status=status,
        requirement=requirement,
        capability_profile=profile,
        unmet_requirements=unmet_requirements,
        notes=[
            "This check uses the same document-vision runtime compatibility gate that structured document verification uses at execution time.",
        ],
    )


def _workflow_unmet_requirements(
    *,
    profile: ModelCapabilityProfileV1 | None,
    requirement: WorkflowModelRequirementV1,
) -> list[str]:
    connection_label = "Document AI connection" if requirement.preferred_connection_id == "document" else "Primary AI connection"
    unmet: list[str] = []
    if profile is None:
        unmet.append(f"{connection_label} is not configured.")
        if requirement.requires_document_inputs:
            unmet.append("Document inputs are required for this workflow.")
        elif requirement.requires_structured_output:
            unmet.append("Structured output is required for this workflow.")
        return unmet
    if requirement.requires_enabled_connection and not profile.enabled:
        unmet.append(f"{connection_label} is disabled.")
    if requirement.requires_structured_output and not profile.supports_structured_output:
        unmet.append("Structured output is required for this workflow.")
    if requirement.requires_document_inputs and not profile.supports_document_inputs:
        unmet.append("Document inputs are required for this workflow.")
    return unmet


def _review_summary(
    *,
    run: CloseCertificationRunV1,
    packet: ReconciliationPacketV1 | None,
) -> str:
    if packet is not None and str(packet.summary or "").strip():
        return str(packet.summary).strip()
    if run.overall_disposition == "block_close":
        return (
            f"Close review blocked with {int(run.blocker_count or 0)} blocker"
            f"{'' if int(run.blocker_count or 0) == 1 else 's'}."
        )
    if run.overall_disposition == "approval_required":
        actionable = int(run.approval_required_count or 0) + int(run.auto_fixable_count or 0)
        return (
            f"Close review surfaced {actionable} approval or guided-fix discrepancy"
            f"{'' if actionable == 1 else 'ies'}."
        )
    return "Close review completed without blocking discrepancies."


def _matches_query(
    *,
    run: CloseCertificationRunV1,
    packet: ReconciliationPacketV1 | None,
    query: str,
) -> bool:
    haystacks = [
        run.run_id,
        run.opportunity_id,
        run.crm_org_id,
        run.org_alias,
        run.entrypoint,
        run.close_workflow_state,
        run.overall_disposition,
        run.reconciliation_packet_id,
        run.document_summary,
        run.document_evidence_summary,
        run.quote_integrity_summary,
        packet.packet_id if packet is not None else None,
        packet.summary if packet is not None else None,
        packet.document_summary if packet is not None else None,
        packet.document_evidence_summary if packet is not None else None,
        packet.quote_integrity_summary if packet is not None else None,
    ]
    for value in haystacks:
        if query in _normalize_token(value):
            return True
    return False


def _normalize_token(value: Any) -> str:
    return str(value or "").strip().lower()


__all__ = [
    "DealDeskCapabilityArtifactPathsV1",
    "DealDeskCapabilityRefreshReceiptSummaryV1",
    "DealDeskCapabilitySummaryResponseV1",
    "DealDeskWorkflowCapabilityCheckV1",
    "CertificationStepPolicyV1",
    "CloseReviewArtifactPathsV1",
    "CloseReviewDetailV1",
    "CloseReviewListItemV1",
    "CloseReviewListResponseV1",
    "PostCloseTriggerRegistryResponseV1",
    "build_close_review_index",
    "load_dealdesk_capability_summary",
    "load_close_review_detail",
    "load_post_close_trigger_registry",
]
