"""Reasoning chains as first-class memory objects."""

from __future__ import annotations

import uuid
from dataclasses import dataclass, field
from typing import List, Optional

from .episode import Episode
from .symbolic import ScoredPath


@dataclass
class ReasoningChain:
    """
    A specific sequence of symbolic reasoning steps that leads to a conclusion.

    This is a 'derived' memory object, meaning it is constructed from
    other base episodes (facts/observations).
    """
    chain_id: str
    scored_path: ScoredPath
    query_context: str
    source_episodes: List[str] = field(default_factory=list)
    created_at: Optional[float] = None

    @classmethod
    def from_path(
        cls,
        scored_path: ScoredPath,
        query_text: str,
        source_episodes: List[str] | None = None
    ) -> "ReasoningChain":
        return cls(
            chain_id=str(uuid.uuid4()),
            scored_path=scored_path,
            query_context=query_text,
            source_episodes=source_episodes or []
        )

    def explain(self, format: str = "text") -> str:
        """
        Produce a human or machine-readable explanation of this reasoning chain.

        Args:
            format: "text" (structured), "mermaid" (visual graph), "json" (data).
        """
        sp = self.scored_path

        if format == "text":
            # Structured text breakdown
            relation_str = ""
            for i, node in enumerate(sp.path[:-1]):
                next_node = sp.path[i+1]
                relation_str += f"{node} --[relation]--> {next_node}"
                if i < len(sp.path) - 2:
                    relation_str += "\n      "

            explanation = (
                f"Reasoning Chain Explanation\n"
                f"--------------------------\n"
                f"Query: {self.query_context}\n\n"
                f"Path:\n  {sp.explanation}\n\n"
                f"Scores:\n"
                f"  Trust Contribution: {getattr(sp, 'trust_score', 0.0):.2f}\n"
                f"  Semantic Contribution: {getattr(sp, 'semantic_score', 0.0):.2f}\n"
                f"  Length Decay: {getattr(sp, 'length_penalty', 1.0):.2f}\n"
                f"  Final Score: {sp.score:.2f}\n\n"
                f"Rationale:\n"
                f"  - Verified structured path through knowledge graph.\n"
                f"  - Adjusted for trust and path length."
            )
            return explanation

        elif format == "mermaid":
            # Visual mermaid graph
            lines = ["graph LR"]
            path = sp.path

            # Nodes & Edges
            for i in range(len(path) - 1):
                u, v = path[i], path[i+1]
                # Try to get edge trust if available in description or just generic
                label = f"trust={getattr(sp, 'trust_score', '?'):.1f}"
                lines.append(f'  {u} -->|"{label}"| {v}')

            # Styling
            for node in path:
                lines.append(f"  style {node} fill:#E3F2FD,stroke:#333,stroke-width:1px")

            # Emphasize this is a derived chain
            lines.append(f"  %% Derived reasoning chain for: {self.query_context}")

            return "\n".join(lines)

        elif format == "json":
            import json
            return json.dumps({
                "query": self.query_context,
                "path": sp.path,
                "score": sp.score,
                "components": sp.to_dict().get("components", {})
            }, indent=2)

        else:
            return self.to_text()

    def to_text(self) -> str:
        """Legacy wrapper for explain('text')."""
        return self.explain("text")

    def to_episode(self) -> Episode:
        """
        Convert this chain into a persistable Episode.

        The episode is marked as 'derived' and type='reasoning_chain'.
        """
        # We store the full structured data in meta
        meta = {
            "type": "reasoning_chain",
            "episode_type": "derived",
            "chain_id": self.chain_id,
            "query": self.query_context,
            "score": self.scored_path.score,
            "components": self.scored_path.to_dict()["components"],
            "path": self.scored_path.path,
            "source_episodes": self.source_episodes
        }

        # We rely on Memory to assign the final numeric ID during insertion,
        # but we can optionally set a temporary ID if strictly needed.
        # Here we create a standard Episode.
        # Note: 'embedding' is usually computed by memory.add_episode(),
        # but we can just provide the text content here.

        return Episode(
            id=self.chain_id,
            meta=meta
        )
