from __future__ import annotations

from typing import Any, Mapping, Optional

from app.core.data_factory.n8n_client import N8nClient

from .base import EngineCapability, EngineCapabilityUnsupported, WorkflowEngine


class N8nAdapter(WorkflowEngine):
    engine_name = "n8n"
    capabilities = frozenset(
        {
            EngineCapability.VALIDATE,
            EngineCapability.DEPLOY_DISABLED,
            EngineCapability.ACTIVATE,
            EngineCapability.DEACTIVATE,
            EngineCapability.EXECUTE,
            EngineCapability.GET_EXECUTION,
        }
    )

    def __init__(self, client: Optional[N8nClient] = None):
        self.client = client or N8nClient()

    def _unsupported(self, operation: str, reason: str = ""):
        raise EngineCapabilityUnsupported(self.engine_name, operation, reason)

    def validate(self, definition: Any) -> Mapping[str, Any]:
        if not isinstance(definition, dict):
            raise ValueError("n8n workflow definition must be an object")
        for field in ("name", "nodes", "connections"):
            if field not in definition:
                raise ValueError(f"n8n workflow definition requires {field}")
        if not isinstance(definition["nodes"], list):
            raise ValueError("n8n workflow nodes must be an array")
        if not isinstance(definition["connections"], dict):
            raise ValueError("n8n workflow connections must be an object")
        return {"valid": True}

    def deploy_disabled(self, definition: Any) -> Mapping[str, Any]:
        self.validate(definition)
        result = self.client.create_workflow(definition)
        if result.get("active"):
            return self.client.deactivate_workflow(str(result["id"]))
        return result

    def activate(self, namespace: str, definition_id: str) -> Mapping[str, Any]:
        return self.client.activate_workflow(definition_id)

    def deactivate(self, namespace: str, definition_id: str) -> Mapping[str, Any]:
        return self.client.deactivate_workflow(definition_id)

    def execute(
        self,
        namespace: str,
        definition_id: str,
        inputs: Optional[Mapping[str, Any]] = None,
        **options: Any,
    ) -> Mapping[str, Any]:
        webhook_path = options.get("webhook_path")
        if not isinstance(webhook_path, str) or not webhook_path.strip():
            self._unsupported(
                "execute",
                "a governed webhook_path is required for n8n execution",
            )
        return self.client.execute_workflow_webhook(
            webhook_path.strip(), data=dict(inputs or {}), method="POST"
        )

    def pause(self, execution_id: str) -> Mapping[str, Any]:
        self._unsupported("pause")

    def kill(self, execution_id: str) -> Mapping[str, Any]:
        self._unsupported("kill")

    def replay(self, execution_id: str) -> Mapping[str, Any]:
        self._unsupported("replay")

    def backfill(
        self,
        namespace: str,
        definition_id: str,
        trigger_id: str,
        start: str,
        end: Optional[str],
    ) -> Mapping[str, Any]:
        self._unsupported("backfill")

    def get_execution(self, execution_id: str) -> Mapping[str, Any]:
        return self.client.get_execution(execution_id, include_data=True)

    def get_logs(self, execution_id: str) -> Any:
        self._unsupported("get_logs")
