"""Memory Shell — MCP wrapper for CSTMemory.

Exposes CSTMemory write, read, query, nearby, validate, stats, info
through the standard shell run() interface.

Relationship to other shells with persistent memory:
    - ExplorationShell has self._mem = CSTMemory() internally
    - BeehiveShell has 24K pre-loaded tokens in dict
    - EcosystemShell has relationship graph
    - ChatShell has ConversationState

MemoryShell IS the central MCP access point for CSTMemory.
Other shells use CSTMemory internally; this shell exposes it directly.
"""

from typing import Any, Dict, List, Optional, Union

from ..memory import CSTMemory
from .._core.constants import LAYER_NAMES


class MemoryShell:
    """MCP shell wrapper for CSTMemory."""

    HEX_ID = "4D454D00"  # MEM\0
    name = "memory"
    version = "1.0.0"

    def __init__(self):
        self._mem = CSTMemory()

    # ------------------------------------------------------------------
    # COMMANDS
    # ------------------------------------------------------------------

    def status(self) -> Dict[str, Any]:
        """Memory status: token count, native backend, info."""
        info = self._mem.info()
        info["stats"] = self._mem.stats()
        return info

    def write(self, path: str) -> Dict[str, Any]:
        """Write a path into semantic memory. O(1) Morton encoding.

        Args:
            path: File path or content string.

        Returns:
            Token with morton, hex_id, basin, layer.
        """
        token = self._mem.write(path)
        return {
            "path": path,
            "morton": token.morton,
            "hex_id": token.hex_id,
            "basin": token.basin,
            "layer": token.layer,
            "layer_name": LAYER_NAMES.get(token.layer, f"L{token.layer}"),
            "coords": {"x": token.x, "y": token.y, "z": token.z},
        }

    def read(self, path: str) -> Dict[str, Any]:
        """Read token for a path. Returns None if not found.

        Args:
            path: File path to look up.
        """
        token = self._mem.read(path)
        if token is None:
            return {"path": path, "found": False}
        return {
            "path": path,
            "found": True,
            "morton": token.morton,
            "hex_id": token.hex_id,
            "basin": token.basin,
            "layer": token.layer,
            "layer_name": LAYER_NAMES.get(token.layer, f"L{token.layer}"),
        }

    def get(self, identifier: str) -> Dict[str, Any]:
        """Get token by hex_id or morton code.

        Args:
            identifier: Hex ID (8-char) or morton code (integer).
        """
        # Try as integer first
        ident: Union[str, int] = identifier
        if identifier.isdigit():
            ident = int(identifier)
        token = self._mem.get(ident)
        if token is None:
            return {"identifier": identifier, "found": False}
        return {
            "found": True,
            "morton": token.morton,
            "hex_id": token.hex_id,
            "basin": token.basin,
            "layer": token.layer,
            "layer_name": LAYER_NAMES.get(token.layer, f"L{token.layer}"),
            "path": getattr(token, "path", None),
        }

    def query(self, pattern: str, limit: str = "10") -> Dict[str, Any]:
        """Query tokens by semantic pattern.

        Args:
            pattern: Search pattern (concept, keyword).
            limit: Maximum results (default 10).
        """
        lim = int(limit) if limit.isdigit() else 10
        tokens = self._mem.query(pattern, limit=lim)
        return {
            "pattern": pattern,
            "count": len(tokens),
            "tokens": [
                {
                    "hex_id": t.hex_id,
                    "basin": t.basin,
                    "layer": t.layer,
                    "path": getattr(t, "path", None),
                }
                for t in tokens
            ],
        }

    def nearby(self, hex_id: str, radius: str = "100") -> Dict[str, Any]:
        """Find tokens near a given token (spatial proximity).

        Args:
            hex_id: Hex ID of the reference token.
            radius: Search radius (default 100).
        """
        token = self._mem.get(hex_id)
        if token is None:
            return {"hex_id": hex_id, "found": False, "nearby": []}
        rad = int(radius) if radius.isdigit() else 100
        nearby = self._mem.nearby(token, radius=rad)
        return {
            "hex_id": hex_id,
            "found": True,
            "radius": rad,
            "count": len(nearby),
            "nearby": [
                {"hex_id": t.hex_id, "basin": t.basin, "layer": t.layer}
                for t in nearby
            ],
        }

    def index(self, path: str, extensions: str = "") -> Dict[str, Any]:
        """Index a directory. One call indexes everything.

        Args:
            path: Directory path to index.
            extensions: Comma-separated extensions (e.g., '.py,.js').
        """
        ext_list = None
        if extensions:
            ext_list = [e.strip() for e in extensions.split(",")]
        count = self._mem.index(path, extensions=ext_list)
        return {
            "path": path,
            "indexed": count,
            "total_tokens": len(self._mem),
        }

    def validate(self, hex_id: str) -> Dict[str, Any]:
        """Validate a token's Morton roundtrip integrity.

        Args:
            hex_id: Hex ID of the token.
        """
        token = self._mem.get(hex_id)
        if token is None:
            return {"hex_id": hex_id, "found": False, "valid": False}
        valid = self._mem.validate(token)
        return {"hex_id": hex_id, "found": True, "valid": valid}

    def stats(self) -> Dict[str, Any]:
        """Get memory statistics."""
        return self._mem.stats()

    def benchmark(self, iterations: str = "10000") -> Dict[str, Any]:
        """Run performance benchmark.

        Args:
            iterations: Number of iterations (default 10000).
        """
        iters = int(iterations) if iterations.isdigit() else 10000
        return self._mem.benchmark(iterations=iters)

    # ------------------------------------------------------------------
    # SHELL INTERFACE
    # ------------------------------------------------------------------

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Execute a memory command.

        Commands:
            status    — Token count, backend info
            write     — Write a path to memory
            read      — Read token for a path
            get       — Get token by hex_id or morton
            query     — Semantic query
            nearby    — Find spatially proximate tokens
            index     — Index a directory
            validate  — Validate token integrity
            stats     — Memory statistics
            benchmark — Performance benchmark
        """
        handlers = {
            "status": lambda: self.status(),
            "write": lambda: self.write(args[0]) if args else self.write("test"),
            "read": lambda: self.read(args[0]) if args else self.read("test"),
            "get": lambda: self.get(args[0]) if args else self.get("00000000"),
            "query": lambda: self.query(args[0], args[1] if len(args) > 1 else "10") if args else self.query("test"),
            "nearby": lambda: self.nearby(args[0], args[1] if len(args) > 1 else "100") if args else self.nearby("00000000"),
            "index": lambda: self.index(args[0], args[1] if len(args) > 1 else "") if args else self.index("."),
            "validate": lambda: self.validate(args[0]) if args else self.validate("00000000"),
            "stats": lambda: self.stats(),
            "benchmark": lambda: self.benchmark(args[0] if args else "10000"),
        }
        handler = handlers.get(command, handlers["status"])
        return handler()
