from __future__ import annotations

from collections.abc import Mapping, Sequence
from dataclasses import dataclass, field
from datetime import datetime
import hashlib
import json
from typing import Protocol, runtime_checkable

from govoplan_core.core.events import PlatformEvent


CAPABILITY_WORKFLOW_RUNTIME_WORKER = "workflow.runtimeWorker"
CAPABILITY_WORKFLOW_DEFINITION_CONTRIBUTIONS = "workflow.definitionContributions"
CAPABILITY_WORKFLOW_ORCHESTRATION = "workflow.orchestration"
CAPABILITY_WORKFLOW_TRIGGER_DISPATCHER = "workflow.triggerDispatcher"


@dataclass(frozen=True, slots=True)
class WorkflowDefinitionContribution:
    """Versioned, module-owned Workflow Engine baseline definition."""

    origin_module_id: str
    origin_module_version: str
    definition_key: str
    name: str
    graph: Mapping[str, object]
    contribution_schema_version: str = "1"
    description: str | None = None
    definition_kind: str = "flow"
    scope_type: str = "system"
    inherit_to_lower_scopes: bool = True
    allow_start: bool = True
    allow_reuse: bool = True
    allow_automation: bool = False
    execution_mode: str = "hybrid"
    view_id: str | None = None
    view_revision_id: str | None = None
    bpmn_xml: str | None = None
    bpmn_adapter_id: str = "govoplan.native.bpmn"
    bpmn_adapter_version: str | None = None
    activate_on_install: bool = True
    required_capabilities: tuple[str, ...] = ()
    required_interfaces: tuple[str, ...] = ()
    metadata: Mapping[str, object] = field(default_factory=dict)
    policy_metadata: Mapping[str, object] = field(default_factory=dict)
    content_hash: str | None = None


@dataclass(frozen=True, slots=True)
class WorkflowStandardStartRequest:
    tenant_id: str
    origin_module_id: str
    definition_key: str
    idempotency_key: str
    input: Mapping[str, object] = field(default_factory=dict)
    actor_id: str | None = None
    correlation_id: str | None = None
    start_origin: str = "user"


@dataclass(frozen=True, slots=True)
class WorkflowCurrentStepResolution:
    action: str
    expected_step_id: str | None = None
    actor_id: str | None = None
    output: Mapping[str, object] = field(default_factory=dict)
    evidence: tuple[str, ...] = ()
    comment: str | None = None


@dataclass(frozen=True, slots=True)
class WorkflowInstanceRef:
    id: str
    tenant_id: str
    definition_id: str
    definition_revision_id: str
    definition_revision: int
    definition_hash: str
    status: str
    current_step_id: str | None = None
    current_node_id: str | None = None
    replayed: bool = False


def workflow_definition_contribution_hash(
    contribution: WorkflowDefinitionContribution,
) -> str:
    """Return the stable content hash, excluding package release provenance."""

    payload = {
        "schema_version": contribution.contribution_schema_version,
        "definition_key": contribution.definition_key,
        "name": contribution.name,
        "description": contribution.description,
        "graph": contribution.graph,
        "definition_kind": contribution.definition_kind,
        "scope_type": contribution.scope_type,
        "inherit_to_lower_scopes": contribution.inherit_to_lower_scopes,
        "allow_start": contribution.allow_start,
        "allow_reuse": contribution.allow_reuse,
        "allow_automation": contribution.allow_automation,
        "execution_mode": contribution.execution_mode,
        "view_id": contribution.view_id,
        "view_revision_id": contribution.view_revision_id,
        "bpmn_xml": contribution.bpmn_xml,
        "bpmn_adapter_id": contribution.bpmn_adapter_id,
        "bpmn_adapter_version": contribution.bpmn_adapter_version,
        "activate_on_install": contribution.activate_on_install,
        "required_capabilities": contribution.required_capabilities,
        "required_interfaces": contribution.required_interfaces,
        "metadata": contribution.metadata,
        "policy_metadata": contribution.policy_metadata,
    }
    encoded = json.dumps(
        payload,
        sort_keys=True,
        separators=(",", ":"),
        ensure_ascii=True,
    )
    return hashlib.sha256(encoded.encode("utf-8")).hexdigest()


@runtime_checkable
class WorkflowRuntimeWorker(Protocol):
    def reconcile_pending(
        self,
        session: object,
        *,
        now: datetime | None = None,
        limit: int = 50,
    ) -> Mapping[str, object]: ...


@runtime_checkable
class WorkflowTriggerDispatcher(Protocol):
    """Durable schedule/event start and wait-resumption boundary."""

    def dispatch_due(
        self,
        session: object,
        *,
        now: datetime | None = None,
        limit: int = 50,
    ) -> Mapping[str, object]: ...

    def ingest_event(
        self,
        session: object,
        *,
        event: PlatformEvent,
    ) -> Mapping[str, object]: ...


@runtime_checkable
class WorkflowDefinitionContributionProvider(Protocol):
    def reconcile(
        self,
        session: object,
        *,
        tenant_ids: Sequence[str] = (),
    ) -> Mapping[str, object]: ...


@runtime_checkable
class WorkflowOrchestrationProvider(Protocol):
    def start_standard(
        self,
        session: object,
        principal: object,
        *,
        request: WorkflowStandardStartRequest,
    ) -> WorkflowInstanceRef: ...

    def resolve_current_step(
        self,
        session: object,
        principal: object,
        *,
        tenant_id: str,
        instance_id: str,
        resolution: WorkflowCurrentStepResolution,
    ) -> WorkflowInstanceRef: ...

    def get_instance(
        self,
        session: object,
        *,
        tenant_id: str,
        instance_id: str,
    ) -> WorkflowInstanceRef: ...


def workflow_runtime_worker(
    registry: object | None,
) -> WorkflowRuntimeWorker | None:
    if (
        registry is None
        or not hasattr(registry, "has_capability")
        or not registry.has_capability(CAPABILITY_WORKFLOW_RUNTIME_WORKER)
    ):
        return None
    capability = registry.capability(CAPABILITY_WORKFLOW_RUNTIME_WORKER)
    return capability if isinstance(capability, WorkflowRuntimeWorker) else None


def workflow_trigger_dispatcher(
    registry: object | None,
) -> WorkflowTriggerDispatcher | None:
    if (
        registry is None
        or not hasattr(registry, "has_capability")
        or not registry.has_capability(CAPABILITY_WORKFLOW_TRIGGER_DISPATCHER)
    ):
        return None
    capability = registry.capability(CAPABILITY_WORKFLOW_TRIGGER_DISPATCHER)
    return capability if isinstance(capability, WorkflowTriggerDispatcher) else None


def workflow_definition_contribution_provider(
    registry: object | None,
) -> WorkflowDefinitionContributionProvider | None:
    if (
        registry is None
        or not hasattr(registry, "has_capability")
        or not registry.has_capability(CAPABILITY_WORKFLOW_DEFINITION_CONTRIBUTIONS)
    ):
        return None
    capability = registry.capability(CAPABILITY_WORKFLOW_DEFINITION_CONTRIBUTIONS)
    return (
        capability
        if isinstance(capability, WorkflowDefinitionContributionProvider)
        else None
    )


def workflow_orchestration_provider(
    registry: object | None,
) -> WorkflowOrchestrationProvider | None:
    if (
        registry is None
        or not hasattr(registry, "has_capability")
        or not registry.has_capability(CAPABILITY_WORKFLOW_ORCHESTRATION)
    ):
        return None
    capability = registry.capability(CAPABILITY_WORKFLOW_ORCHESTRATION)
    return capability if isinstance(capability, WorkflowOrchestrationProvider) else None


__all__ = [
    "CAPABILITY_WORKFLOW_DEFINITION_CONTRIBUTIONS",
    "CAPABILITY_WORKFLOW_ORCHESTRATION",
    "CAPABILITY_WORKFLOW_RUNTIME_WORKER",
    "CAPABILITY_WORKFLOW_TRIGGER_DISPATCHER",
    "WorkflowCurrentStepResolution",
    "WorkflowDefinitionContribution",
    "WorkflowDefinitionContributionProvider",
    "WorkflowInstanceRef",
    "WorkflowOrchestrationProvider",
    "WorkflowRuntimeWorker",
    "WorkflowTriggerDispatcher",
    "WorkflowStandardStartRequest",
    "workflow_definition_contribution_hash",
    "workflow_definition_contribution_provider",
    "workflow_orchestration_provider",
    "workflow_runtime_worker",
    "workflow_trigger_dispatcher",
]
