"""Centris Action API contract.

Stable runtime-facing schema for Electron/extension/CLI integration.
"""

from dataclasses import dataclass, field
from typing import Any, Dict, Literal, Optional, Union

from centris_sdk.action.kernel import (
    ACTION_KERNEL_SPEC_VERSION,
    KernelActRequest,
    KernelActResult,
    KernelObserveRequest,
    KernelObserveResult,
    KernelSuccessCheck,
    KernelVerifyRequest,
    KernelVerifyResult,
)

ACTION_API_SPEC_VERSION: str = ACTION_KERNEL_SPEC_VERSION

ActionApiMethod = Literal[
    "observe",
    "act",
    "verify",
    "desktop.snapshot",
    "desktop.find",
    "desktop.click",
    "desktop.type",
    "desktop.apps",
    "desktop.windows",
    "route.run",
    "route.record.start",
    "route.record.stop",
    "contract.discover",
    "contract.resolveAction",
    "contract.execute",
    "contract.verify",
    "contract.capabilities",
    "contract.attest",
    "retrieval.search",
    "web.memory.index",
    "web.memory.upsert_batch",
    "web.memory.validate",
    "web.memory.resolve",
    "web.memory.search",
    "web.memory.execute",
    "web.memory.invalidate",
    "web.memory.stats",
    "web.memory.context.pack",
    "web.memory.context.probe",
    "web.memory.policy.check",
    "web.route.tree.index",
    "web.route.tree.resolve",
]

RouteRecordOutcome = Literal["success", "failed", "cancelled"]
ActionAffordance = Literal["click", "type", "select", "submit", "navigate", "press", "read", "wait"]
ActionRegion = Literal["header", "nav", "main", "sidebar", "modal", "footer", "unknown"]
ActionAnchorType = Literal[
    "label",
    "aria_label",
    "placeholder",
    "near_text",
    "selector",
    "test_id",
    "business_id",
    "role",
    "url",
    "region",
]


@dataclass
class ActionArtifact:
    artifact_type: str
    schema: str
    producer_operation: str
    value: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionAnchor:
    anchor_type: ActionAnchorType
    value: str
    weight: Optional[float] = None


@dataclass
class ActionNodeHint:
    node_id: Optional[int] = None
    selector: Optional[str] = None
    role: Optional[str] = None
    name: Optional[str] = None


@dataclass
class ActionLandmark:
    role: str
    label: Optional[str] = None
    region: Optional[ActionRegion] = None
    selectors: list[str] = field(default_factory=list)
    text_hints: list[str] = field(default_factory=list)


@dataclass
class ActionPageFingerprint:
    fingerprint_id: Optional[str] = None
    url_pattern: Optional[str] = None
    title_hints: list[str] = field(default_factory=list)
    headings: list[str] = field(default_factory=list)
    nav_labels: list[str] = field(default_factory=list)
    primary_actions: list[str] = field(default_factory=list)
    landmarks: list[ActionLandmark] = field(default_factory=list)
    interactive_summary: Dict[str, int] = field(default_factory=dict)
    signature_hash: Optional[str] = None
    generated_at: Optional[str] = None
    confidence: Optional[float] = None


@dataclass
class ActionIndexEntry:
    action_id: str
    intent: str
    affordance: ActionAffordance
    semantic_label: Optional[str] = None
    region: Optional[ActionRegion] = None
    node_hints: list[ActionNodeHint] = field(default_factory=list)
    anchors: list[ActionAnchor] = field(default_factory=list)
    preconditions: list[str] = field(default_factory=list)
    success_checks: list[KernelSuccessCheck] = field(default_factory=list)
    fallback_action_ids: list[str] = field(default_factory=list)
    confidence: Optional[float] = None
    updated_at: Optional[str] = None


@dataclass
class ActionRouteMemoryStep:
    step_id: Optional[str] = None
    action_id: Optional[str] = None
    operation: Optional[str] = None
    params: Dict[str, str] = field(default_factory=dict)
    expected_page_fingerprint_id: Optional[str] = None
    success_checks: list[KernelSuccessCheck] = field(default_factory=list)


@dataclass
class ActionRouteMemory:
    route_id: str
    steps: list[ActionRouteMemoryStep] = field(default_factory=list)
    intent: Optional[str] = None
    site: Optional[str] = None
    page_fingerprint_id: Optional[str] = None
    preconditions: list[str] = field(default_factory=list)
    success_checks: list[KernelSuccessCheck] = field(default_factory=list)
    fallback_route_ids: list[str] = field(default_factory=list)
    confidence: Optional[float] = None
    version: Optional[str] = None
    updated_at: Optional[str] = None


@dataclass
class ActionRouteRunRequest:
    route_id: str
    url: Optional[str] = None
    params: Dict[str, str] = field(default_factory=dict)
    checks: list[KernelSuccessCheck] = field(default_factory=list)
    artifacts: list[ActionArtifact] = field(default_factory=list)
    page_fingerprint: Optional[ActionPageFingerprint] = None
    action_index: list[ActionIndexEntry] = field(default_factory=list)
    route_memory: Optional[ActionRouteMemory] = None


@dataclass
class ActionRouteRunResult:
    ok: bool
    executed: int
    verify: Optional[KernelVerifyResult] = None
    artifacts: list[ActionArtifact] = field(default_factory=list)
    source: Optional[Literal["memory", "manifest", "live"]] = None
    page_fingerprint: Optional[ActionPageFingerprint] = None
    action_index: list[ActionIndexEntry] = field(default_factory=list)
    route_memory: Optional[ActionRouteMemory] = None


@dataclass
class ActionRouteRecordStartRequest:
    intent: str
    url: Optional[str] = None
    params: Dict[str, str] = field(default_factory=dict)
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionRouteRecordStartResult:
    ok: bool
    session_id: str
    started_at: Optional[str] = None


@dataclass
class ActionRouteRecordStopRequest:
    session_id: str
    outcome: Optional[RouteRecordOutcome] = None
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionRouteRecordStopResult:
    ok: bool
    route_id: Optional[str] = None
    updated_at: Optional[str] = None


@dataclass
class ActionContractCapability:
    action_id: str
    safety_class: Literal["read", "write", "destructive"] = "read"
    description: Optional[str] = None
    scopes: list[str] = field(default_factory=list)
    plugins: list[str] = field(default_factory=list)
    skills: list[str] = field(default_factory=list)
    required_success_checks: list[KernelSuccessCheck] = field(default_factory=list)
    binding: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionContractSummary:
    app: str
    publisher: str
    url_patterns: list[str] = field(default_factory=list)
    version: Optional[str] = None
    app_version: Optional[str] = None
    contract_hash: Optional[str] = None
    source: Optional[str] = None
    source_kind: Optional[Literal["workspace", "registry", "global", "overlay", "external"]] = None
    trusted: Optional[bool] = None
    trust_reason: Optional[str] = None


@dataclass
class ActionContractDiscoverRequest:
    app: Optional[str] = None
    url: Optional[str] = None
    include_actions: Optional[bool] = None
    refresh: Optional[bool] = None


@dataclass
class ActionContractDiscoverResult:
    hit: bool
    contract: Optional[ActionContractSummary] = None
    actions: list[ActionContractCapability] = field(default_factory=list)


@dataclass
class ActionContractResolveActionRequest:
    action_id: str
    app: Optional[str] = None
    url: Optional[str] = None


@dataclass
class ActionContractResolveActionResult:
    hit: bool
    contract: Optional[ActionContractSummary] = None
    capability: Optional[ActionContractCapability] = None


@dataclass
class ActionContractExecuteRequest:
    action_id: str
    app: Optional[str] = None
    url: Optional[str] = None
    input: Dict[str, Any] = field(default_factory=dict)
    auth_scopes: list[str] = field(default_factory=list)
    allow_destructive: Optional[bool] = None
    approval_token: Optional[str] = None
    fallback: Optional[Literal["api", "browser", "desktop"]] = None


@dataclass
class ActionContractExecuteResult:
    ok: bool
    action_id: str
    executed_by: Literal["contract", "fallback"] = "contract"
    safety_class: Optional[Literal["read", "write", "destructive"]] = None
    contract_hash: Optional[str] = None
    source: Optional[str] = None
    fallback_method: Optional[Literal["api", "browser", "desktop"]] = None
    result: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionContractVerifyRequest:
    app: Optional[str] = None
    url: Optional[str] = None


@dataclass
class ActionContractVerifyResult:
    ok: bool
    trusted: bool
    reason: str
    publisher: Optional[str] = None
    contract_hash: Optional[str] = None
    signature_valid: Optional[bool] = None


@dataclass
class ActionContractCapabilitiesRequest:
    app: Optional[str] = None
    url: Optional[str] = None


@dataclass
class ActionContractCapabilitiesResult:
    hit: bool
    contract: Optional[ActionContractSummary] = None
    capabilities: list[ActionContractCapability] = field(default_factory=list)
    skills_graph: Dict[str, Any] = field(default_factory=dict)
    plugins: list[str] = field(default_factory=list)


@dataclass
class ActionContractAttestRequest:
    app: Optional[str] = None
    url: Optional[str] = None
    action_id: Optional[str] = None
    nonce: Optional[str] = None


@dataclass
class ActionContractAttestResult:
    ok: bool
    attestation: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionRetrievalSearchRequest:
    query: str
    provider: Optional[Literal["airweave"]] = None
    readable_id: Optional[str] = None
    collection: Optional[str] = None
    collection_id: Optional[str] = None
    base_url: Optional[str] = None
    api_key: Optional[str] = None
    organization_id: Optional[str] = None
    limit: Optional[int] = None
    timeout_ms: Optional[int] = None
    retrieval_strategy: Optional[Literal["hybrid", "neural", "keyword"]] = None
    expand_query: Optional[bool] = None
    interpret_filters: Optional[bool] = None
    rerank: Optional[bool] = None
    generate_answer: Optional[bool] = None
    filter: Dict[str, Any] = field(default_factory=dict)
    offset: Optional[int] = None


@dataclass
class ActionRetrievalSearchRecord:
    name: str
    source_name: str
    score: Optional[float] = None
    snippet: Optional[str] = None
    raw: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionRetrievalSearchResult:
    ok: bool
    provider: Literal["airweave"] = "airweave"
    readable_id: str = ""
    query: str = ""
    result_count: int = 0
    completion: Optional[str] = None
    results: list[ActionRetrievalSearchRecord] = field(default_factory=list)


@dataclass
class ActionWebMemoryIndexRequest:
    url: str
    intent: Optional[str] = None
    playbook: Dict[str, Any] = field(default_factory=dict)
    page_fingerprint: Optional[ActionPageFingerprint] = None
    action_index: list[ActionIndexEntry] = field(default_factory=list)
    route_memory: Optional[ActionRouteMemory] = None
    ttl_ms: Optional[int] = None
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionWebMemoryIndexResult:
    ok: bool
    cache_key: Optional[str] = None
    version: Optional[str] = None
    created_at: Optional[str] = None
    expires_at: Optional[str] = None
    page_fingerprint: Optional[ActionPageFingerprint] = None
    action_index: list[ActionIndexEntry] = field(default_factory=list)
    route_memory: Optional[ActionRouteMemory] = None
    artifact: Optional[ActionArtifact] = None


@dataclass
class ActionWebMemoryUpsertBatchRequest:
    entries: list[Dict[str, Any]] = field(default_factory=list)
    continue_on_error: Optional[bool] = None


@dataclass
class ActionWebMemoryBatchItemResult:
    index: int
    ok: bool
    cache_key: Optional[str] = None
    error: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionWebMemoryUpsertBatchResult:
    ok: bool
    processed: int = 0
    succeeded: int = 0
    failed: int = 0
    results: list[ActionWebMemoryBatchItemResult] = field(default_factory=list)


@dataclass
class ActionWebMemoryValidateRequest:
    payload: ActionWebMemoryIndexRequest
    strict: Optional[bool] = None


@dataclass
class ActionWebMemoryValidateResult:
    ok: bool
    errors: list[str] = field(default_factory=list)
    warnings: list[str] = field(default_factory=list)
    normalized: Optional[ActionWebMemoryIndexRequest] = None
    stats: Dict[str, int] = field(default_factory=dict)


@dataclass
class ActionWebMemoryResolveRequest:
    url: str
    intent: Optional[str] = None
    max_age_ms: Optional[int] = None
    semantic_fallback: Optional[bool] = None


@dataclass
class ActionWebMemoryResolveResult:
    hit: bool
    cache_key: Optional[str] = None
    playbook: Dict[str, Any] = field(default_factory=dict)
    generated_at: Optional[str] = None
    expires_at: Optional[str] = None
    source: Optional[Literal["cache", "live"]] = None
    confidence: Optional[float] = None
    page_fingerprint: Optional[ActionPageFingerprint] = None
    action_index: list[ActionIndexEntry] = field(default_factory=list)
    route_memory: Optional[ActionRouteMemory] = None
    artifact: Optional[ActionArtifact] = None


@dataclass
class ActionWebMemorySearchRequest:
    url: str
    intent: Optional[str] = None
    max_age_ms: Optional[int] = None
    semantic_fallback: Optional[bool] = None
    limit: Optional[int] = None
    include_payload: Optional[bool] = None


@dataclass
class ActionWebMemorySearchHit:
    cache_key: str
    url: str
    intent: Optional[str] = None
    confidence: float = 0.0
    generated_at: Optional[str] = None
    expires_at: Optional[str] = None
    retrieval: Dict[str, Any] = field(default_factory=dict)
    playbook: Dict[str, Any] = field(default_factory=dict)
    page_fingerprint: Optional[ActionPageFingerprint] = None
    action_index: list[ActionIndexEntry] = field(default_factory=list)
    route_memory: Optional[ActionRouteMemory] = None
    route_tree: list["ActionRouteTreeNode"] = field(default_factory=list)


@dataclass
class ActionWebMemorySearchResult:
    hits: list[ActionWebMemorySearchHit] = field(default_factory=list)
    total_candidates: int = 0


@dataclass
class ActionWebMemoryExecuteRequest:
    url: str
    intent: Optional[str] = None
    operation: Optional[str] = None
    page_fingerprint_id: Optional[str] = None
    route_id: Optional[str] = None
    params: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionWebMemoryExecuteResult:
    ok: bool
    source: Optional[Literal["cache", "live"]] = None
    executed: Optional[int] = None
    confidence: Optional[float] = None
    page_fingerprint: Optional[ActionPageFingerprint] = None
    action_index: list[ActionIndexEntry] = field(default_factory=list)
    route_memory: Optional[ActionRouteMemory] = None
    details: Dict[str, Any] = field(default_factory=dict)
    artifacts: list[ActionArtifact] = field(default_factory=list)


@dataclass
class ActionWebMemoryInvalidateRequest:
    url: Optional[str] = None
    playbook_id: Optional[str] = None
    scope: Optional[Literal["url", "domain", "all"]] = None
    reason: Optional[str] = None


@dataclass
class ActionWebMemoryInvalidateResult:
    ok: bool
    invalidated: int


@dataclass
class ActionWebMemoryStatsRequest:
    url: Optional[str] = None
    window: Optional[Literal["1h", "24h", "7d", "30d"]] = None


@dataclass
class ActionWebMemoryStatsResult:
    entries: int
    hits: int
    misses: int
    hit_rate: Optional[float] = None
    avg_resolve_ms: Optional[float] = None
    indexed_pages: Optional[int] = None
    indexed_actions: Optional[int] = None
    indexed_routes: Optional[int] = None
    avg_execute_ms: Optional[float] = None


@dataclass
class ActionContextPackRequest:
    url: str
    intent: Optional[str] = None
    max_chars: Optional[int] = None


@dataclass
class ActionContextPackResult:
    ok: bool
    source: Literal["cache", "live"] = "live"
    char_count: int = 0
    pack: Dict[str, Any] = field(default_factory=dict)
    warnings: list[str] = field(default_factory=list)


@dataclass
class ActionContextProbeRequest:
    url: str
    intent: Optional[str] = None
    max_chars: Optional[int] = None


@dataclass
class ActionContextProbeResult:
    ok: bool
    source: Literal["cache", "live"] = "live"
    coverage: Dict[str, Any] = field(default_factory=dict)
    budget: Dict[str, Any] = field(default_factory=dict)
    warnings: list[str] = field(default_factory=list)


@dataclass
class ActionRetrievalPolicyCheckRequest:
    estimated_entries: Optional[int] = None
    semantic_fallback: Optional[bool] = None
    routing_depth: Optional[Literal["shallow", "balanced", "deep"]] = None
    retrieval_mode: Optional[Literal["exact-only", "hybrid", "semantic"]] = None
    strict: Optional[bool] = None


@dataclass
class ActionRetrievalPolicyCheckResult:
    ok: bool
    blocked: bool = False
    errors: list[str] = field(default_factory=list)
    warnings: list[str] = field(default_factory=list)
    normalized: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionRouteTreeNode:
    id: str
    label: str
    action_id: Optional[str] = None
    affordance: Optional[str] = None
    confidence: Optional[float] = None
    children: list["ActionRouteTreeNode"] = field(default_factory=list)


@dataclass
class ActionRouteTreeIndexRequest:
    url: str
    intent: Optional[str] = None
    route_tree: list[ActionRouteTreeNode] = field(default_factory=list)
    action_index: list[ActionIndexEntry] = field(default_factory=list)
    route_memory: Optional[ActionRouteMemory] = None
    page_fingerprint: Optional[ActionPageFingerprint] = None
    ttl_ms: Optional[int] = None


@dataclass
class ActionRouteTreeIndexResult:
    ok: bool
    cache_key: Optional[str] = None
    route_tree: list[ActionRouteTreeNode] = field(default_factory=list)
    version: Optional[str] = None
    created_at: Optional[str] = None
    expires_at: Optional[str] = None


@dataclass
class ActionRouteTreeResolveRequest:
    url: str
    intent: Optional[str] = None
    max_age_ms: Optional[int] = None


@dataclass
class ActionRouteTreeResolveResult:
    hit: bool
    cache_key: Optional[str] = None
    route_tree: list[ActionRouteTreeNode] = field(default_factory=list)
    confidence: Optional[float] = None
    source: Optional[Literal["cache", "live"]] = None
    generated_at: Optional[str] = None
    expires_at: Optional[str] = None


@dataclass
class DesktopElement:
    id: int
    role: Optional[str] = None
    name: Optional[str] = None
    value: Optional[str] = None


@dataclass
class ActionDesktopSnapshotRequest:
    app_name: Optional[str] = None
    window_title: Optional[str] = None


@dataclass
class ActionDesktopSnapshotResult:
    app_name: Optional[str] = None
    window_title: Optional[str] = None
    element_count: int = 0
    elements: list[DesktopElement] = field(default_factory=list)
    note: Optional[str] = None


@dataclass
class ActionDesktopFindRequest:
    app_name: Optional[str] = None
    window_title: Optional[str] = None
    role: Optional[str] = None
    name: Optional[str] = None


@dataclass
class ActionDesktopFindResult:
    count: int = 0
    elements: list[DesktopElement] = field(default_factory=list)
    note: Optional[str] = None


@dataclass
class ActionDesktopClickRequest:
    element_id: int


@dataclass
class ActionDesktopClickResult:
    ok: bool
    details: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionDesktopTypeRequest:
    text: str
    element_id: Optional[int] = None


@dataclass
class ActionDesktopTypeResult:
    ok: bool
    details: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionDesktopWindowsRequest:
    app_name: Optional[str] = None


@dataclass
class ActionDesktopAppsResult:
    apps: list[Dict[str, Any]] = field(default_factory=list)


@dataclass
class ActionDesktopWindowsResult:
    windows: list[Dict[str, Any]] = field(default_factory=list)


ActionApiParams = Union[
    KernelObserveRequest,
    KernelActRequest,
    KernelVerifyRequest,
    ActionDesktopSnapshotRequest,
    ActionDesktopFindRequest,
    ActionDesktopClickRequest,
    ActionDesktopTypeRequest,
    ActionDesktopWindowsRequest,
    ActionRouteRunRequest,
    ActionRouteRecordStartRequest,
    ActionRouteRecordStopRequest,
    ActionContractDiscoverRequest,
    ActionContractResolveActionRequest,
    ActionContractExecuteRequest,
    ActionContractVerifyRequest,
    ActionContractCapabilitiesRequest,
    ActionContractAttestRequest,
    ActionRetrievalSearchRequest,
    ActionWebMemoryIndexRequest,
    ActionWebMemoryUpsertBatchRequest,
    ActionWebMemoryValidateRequest,
    ActionWebMemoryResolveRequest,
    ActionWebMemorySearchRequest,
    ActionWebMemoryExecuteRequest,
    ActionWebMemoryInvalidateRequest,
    ActionWebMemoryStatsRequest,
    ActionContextPackRequest,
    ActionContextProbeRequest,
    ActionRetrievalPolicyCheckRequest,
    ActionRouteTreeIndexRequest,
    ActionRouteTreeResolveRequest,
]

ActionApiResult = Union[
    KernelObserveResult,
    KernelActResult,
    KernelVerifyResult,
    ActionDesktopSnapshotResult,
    ActionDesktopFindResult,
    ActionDesktopClickResult,
    ActionDesktopTypeResult,
    ActionDesktopAppsResult,
    ActionDesktopWindowsResult,
    ActionRouteRunResult,
    ActionRouteRecordStartResult,
    ActionRouteRecordStopResult,
    ActionContractDiscoverResult,
    ActionContractResolveActionResult,
    ActionContractExecuteResult,
    ActionContractVerifyResult,
    ActionContractCapabilitiesResult,
    ActionContractAttestResult,
    ActionRetrievalSearchResult,
    ActionWebMemoryIndexResult,
    ActionWebMemoryUpsertBatchResult,
    ActionWebMemoryValidateResult,
    ActionWebMemoryResolveResult,
    ActionWebMemorySearchResult,
    ActionWebMemoryExecuteResult,
    ActionWebMemoryInvalidateResult,
    ActionWebMemoryStatsResult,
    ActionContextPackResult,
    ActionContextProbeResult,
    ActionRetrievalPolicyCheckResult,
    ActionRouteTreeIndexResult,
    ActionRouteTreeResolveResult,
]


@dataclass
class ActionApiError:
    code: str
    message: str
    details: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ActionApiRequestEnvelope:
    spec_version: str
    method: ActionApiMethod
    params: Dict[str, Any]
    id: Optional[str] = None


@dataclass
class ActionApiResponseEnvelope:
    spec_version: str
    method: ActionApiMethod
    ok: bool
    result: Optional[Dict[str, Any]] = None
    error: Optional[ActionApiError] = None
    id: Optional[str] = None
