"""
OpenClaw Adapter for OpenMemo.

Provides a memory backend for OpenClaw agent sessions.
Hooks into agent lifecycle events (thought, action, observation).

PRD §12 — Inspector V3: All 6 Feedback Loop events are wired:
  memory_stored    → write() / on_action() / on_observation() / on_agent_step()
  memory_recalled  → search_memory() / search()
  memory_injected  → build_orchestrated_prompt()
  memory_adopted   → on_agent_step(memory_id=...)
  workflow_used    → build_orchestrated_prompt() (via orchestrator result)
  task_completed   → on_task_complete()

Usage (local):
    from openmemo.adapters.openclaw import OpenClawMemoryBackend
    backend = OpenClawMemoryBackend(agent_id="claw_agent")

Usage (remote):
    from openmemo.adapters.openclaw import OpenClawMemoryBackend
    backend = OpenClawMemoryBackend(
        agent_id="claw_agent",
        base_url="https://api.openmemo.ai",
    )
"""

from typing import List, Dict, Optional
from openmemo.adapters.base_adapter import BaseMemoryAdapter


class OpenClawMemoryBackend(BaseMemoryAdapter):
    adapter_name = "openclaw"

    # ── Internal helper ────────────────────────────────────────────────────

    def _task_id(self, scene: str = "", session_id: str = "") -> str:
        """Derive a task_id from available context."""
        return self.conversation_id or session_id or scene or ""

    def _extract_ids(self, results: List[Dict]) -> List[str]:
        """Extract memory IDs from recall results."""
        ids = []
        for m in results:
            mid = m.get("id") or m.get("memory_id") or ""
            if mid:
                ids.append(str(mid))
        return ids

    # ── Core memory operations ─────────────────────────────────────────────

    def search_memory(self, query: str, scene: str = "",
                      limit: int = 10) -> List[Dict]:
        """Recall + fire memory_recalled feedback event (PRD §12)."""
        results = self.recall_memory(query=query, scene=scene, limit=limit)
        if results:
            ids = self._extract_ids(results)
            if ids:
                self.feedback_on_recall(
                    memory_ids=ids,
                    task_id=self._task_id(scene),
                    memory_layer=scene or self.default_scene,
                )
        return results

    def memory_governance(self, operation: str = "cleanup") -> dict:
        try:
            return self._memory.memory_governance(operation=operation)
        except Exception:
            return {"error": "governance failed"}

    def write(self, content: str, scene: str = "", cell_type: str = "fact") -> str:
        """Write memory + fire memory_stored tracking event (PRD §12)."""
        memory_id = self.write_memory(content=content, scene=scene, memory_type=cell_type)
        if memory_id:
            self.feedback_on_recall(
                memory_ids=[memory_id],
                task_id=self._task_id(scene),
                memory_layer=scene or self.default_scene,
            )
        return memory_id

    def recall(self, query: str, scene: str = "", mode: str = "kv",
               limit: int = 10) -> dict:
        return self.recall_context(query=query, scene=scene, mode=mode, limit=limit)

    def search(self, query: str, limit: int = 10) -> List[Dict]:
        """Search + fire memory_recalled event (PRD §12)."""
        results = self.recall_memory(query=query, limit=limit)
        if results:
            ids = self._extract_ids(results)
            if ids:
                self.feedback_on_recall(
                    memory_ids=ids,
                    task_id=self._task_id(),
                    memory_layer=self.default_scene,
                )
        return results

    # ── Lifecycle hooks ────────────────────────────────────────────────────

    def on_thought(self, thought: str, scene: str = ""):
        pass

    def on_action(self, action: str, scene: str = ""):
        """PRD §12 — memory_stored event via action write."""
        memory_id = self.write_memory(content=action, scene=scene, memory_type="observation")
        if memory_id:
            self.feedback_on_recall(
                memory_ids=[memory_id],
                task_id=self._task_id(scene),
                memory_layer=scene or self.default_scene,
            )

    def on_observation(self, observation: str, scene: str = ""):
        """PRD §12 — memory_stored event via observation write."""
        memory_id = self.write_memory(content=observation, scene=scene, memory_type="observation")
        if memory_id:
            self.feedback_on_recall(
                memory_ids=[memory_id],
                task_id=self._task_id(scene),
                memory_layer=scene or self.default_scene,
            )

    def on_agent_step(self, step_text: str, scene: str = "",
                      session_id: str = "", memory_id: str = ""):
        """
        User Memory PRD §7.1 + Context Memory PRD §8.1
        + Orchestrator PRD §14 + Inspector PRD §12 — Agent step hook.

        - Writes step to memory (memory_stored)
        - Fires memory_adopted event when memory_id is provided
        - Extracts user memory
        - Records timeline event in context memory
        """
        written_id = self.write_memory(content=step_text, scene=scene, memory_type="observation")
        self.user_memory_store(step_text)
        sid = session_id or self.conversation_id
        if sid:
            self.context_on_agent_step(sid, step_text)
        # PRD §12 — memory_adopted event
        adopted_id = memory_id or written_id or ""
        task_id = self._task_id(scene, session_id)
        if adopted_id and task_id:
            self.feedback_on_adopt(
                task_id=task_id,
                memory_id=adopted_id,
                adopted=True,
            )

    def build_orchestrated_prompt(self, task: str,
                                   session_id: str = "", **kwargs) -> dict:
        """
        PRD §14 + Inspector PRD §12 — Orchestrator integration.

        Fires memory_injected event for each memory injected into the prompt.
        Returns full orchestration result; use result["context"] as system prompt.
        """
        result = self.orchestrate_prompt(
            task=task,
            session_id=session_id,
            agent_role="openclaw",
            **kwargs
        )
        # PRD §12 — memory_injected event for each orchestrated memory
        task_id = self._task_id(session_id=session_id) or task[:32]
        injected = result.get("memories_used", []) or result.get("recalled", [])
        for mem in injected:
            if isinstance(mem, dict):
                mid = mem.get("id") or mem.get("memory_id", "")
            else:
                mid = str(mem)
            if mid:
                self.feedback_on_inject(
                    memory_id=mid,
                    task_id=task_id,
                )
        # PRD §12 — workflow_used event (captured via adopt on workflow memories)
        workflow_id = result.get("workflow_id", "")
        if workflow_id:
            self.feedback_on_adopt(
                task_id=task_id,
                memory_id=workflow_id,
                adopted=True,
            )
        return result

    def on_tool_call(self, tool_name: str, result: str,
                     session_id: str = ""):
        """Context Memory PRD §8.1 — Tool call hook."""
        sid = session_id or self.conversation_id
        if sid and self._context_memory:
            try:
                self._context_memory.on_tool_call(sid, tool_name, result)
            except Exception:
                pass

    def on_message(self, role: str, content: str,
                   session_id: str = ""):
        """Context Memory PRD §12.1 — Message hook."""
        sid = session_id or self.conversation_id
        if sid:
            self.context_on_message(sid, role, content)

    def on_task_complete(self, task: str, result: str, scene: str = "",
                         session_id: str = "",
                         success: Optional[bool] = None):
        """
        User Memory PRD §7.1 + Context Memory PRD §7.1 / §12.1
        + Inspector PRD §12 — Task complete hook.

        - Writes decision to memory (memory_stored)
        - Fires task_completed feedback event with success flag
        - Extracts user memory
        - Triggers context summary generation
        """
        content = f"{task}: {result}" if result else task
        self.write_memory(content=content, scene=scene, memory_type="decision")
        combined = f"{task}. {result}" if result else task
        self.user_memory_store(combined)
        sid = session_id or self.conversation_id
        if sid:
            self.context_on_task_complete(sid, task=task, result=result)
        # PRD §12 — task_completed event
        task_id = self._task_id(scene, session_id) or task[:32]
        if success is None:
            result_lower = (result or "").lower()
            success = not any(kw in result_lower for kw in ("fail", "error", "exception", "timeout"))
        self.feedback_on_outcome(
            task_id=task_id,
            success=success,
        )
