"""
Observation Schema — Phase 23.5

Unified atomic memory structure capturing agent behavior events.
All memory ingestion flows through this schema before reaching the
DAG Memory Layer.

Types:
    tool_execution      — tool call + result
    agent_step          — single reasoning step
    session_event       — session start/end
    task_event          — task lifecycle (start/complete/fail)
    decision            — decision made by agent
    observation         — generic observation
"""

import time
import uuid
from dataclasses import dataclass, field, asdict
from typing import Any, Dict, List, Optional


VALID_TYPES = {
    "tool_execution",
    "agent_step",
    "session_event",
    "task_event",
    "decision",
    "observation",
}


@dataclass
class Observation:
    """
    Atomic unit of agent behavior — the raw input to the memory system.

    Fields:
        id          — unique identifier (obs_<uuid4 short>)
        type        — one of VALID_TYPES
        agent_id    — which agent produced this
        task_id     — task this observation belongs to
        content     — structured payload (tool name, action, result, etc.)
        context     — scene, tags, session_id
        timestamp   — unix timestamp (float)
        confidence  — 0.0–1.0 confidence score
    """
    id: str = field(default_factory=lambda: f"obs_{uuid.uuid4().hex[:12]}")
    type: str = "observation"
    agent_id: str = ""
    task_id: str = ""
    content: Dict[str, Any] = field(default_factory=dict)
    context: Dict[str, Any] = field(default_factory=dict)
    timestamp: float = field(default_factory=time.time)
    confidence: float = 0.9

    def __post_init__(self):
        if self.type not in VALID_TYPES:
            self.type = "observation"
        self.confidence = max(0.0, min(1.0, float(self.confidence)))

    def to_dict(self) -> dict:
        return asdict(self)

    def to_text(self) -> str:
        """Human-readable summary for embedding / storage."""
        parts = []
        t = self.type.replace("_", " ").title()
        parts.append(f"[{t}]")
        if self.agent_id:
            parts.append(f"agent={self.agent_id}")
        if self.task_id:
            parts.append(f"task={self.task_id}")
        for k, v in self.content.items():
            parts.append(f"{k}={v}")
        scene = self.context.get("scene", "")
        if scene:
            parts.append(f"scene={scene}")
        return " | ".join(parts)

    @classmethod
    def from_dict(cls, d: dict) -> "Observation":
        return cls(
            id=d.get("id", f"obs_{uuid.uuid4().hex[:12]}"),
            type=d.get("type", "observation"),
            agent_id=d.get("agent_id", ""),
            task_id=d.get("task_id", ""),
            content=d.get("content", {}),
            context=d.get("context", {}),
            timestamp=float(d.get("timestamp", time.time())),
            confidence=float(d.get("confidence", 0.9)),
        )

    @classmethod
    def tool_execution(cls, tool: str, action: str, result: str,
                       agent_id: str = "", task_id: str = "",
                       scene: str = "", tags: List[str] = None,
                       confidence: float = 0.9) -> "Observation":
        return cls(
            type="tool_execution",
            agent_id=agent_id,
            task_id=task_id,
            content={"tool": tool, "action": action, "result": result},
            context={"scene": scene, "tags": tags or []},
            confidence=confidence,
        )

    @classmethod
    def agent_step(cls, step: str, reasoning: str = "",
                   agent_id: str = "", task_id: str = "",
                   scene: str = "", confidence: float = 0.85) -> "Observation":
        return cls(
            type="agent_step",
            agent_id=agent_id,
            task_id=task_id,
            content={"step": step, "reasoning": reasoning},
            context={"scene": scene},
            confidence=confidence,
        )

    @classmethod
    def session_event(cls, event: str, session_id: str = "",
                      agent_id: str = "", scene: str = "") -> "Observation":
        return cls(
            type="session_event",
            agent_id=agent_id,
            content={"event": event, "session_id": session_id},
            context={"scene": scene},
            confidence=1.0,
        )

    @classmethod
    def task_event(cls, event: str, task_id: str = "",
                   status: str = "", agent_id: str = "",
                   scene: str = "", confidence: float = 0.95) -> "Observation":
        return cls(
            type="task_event",
            agent_id=agent_id,
            task_id=task_id,
            content={"event": event, "status": status},
            context={"scene": scene},
            confidence=confidence,
        )
