"""Memory query interface.

Provides high-level query capabilities for the vector memory system.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from oclawma.memory.embeddings import EmbeddingProvider
from oclawma.memory.store import MemoryChunk, SearchResult, VectorMemoryStore


@dataclass
class QueryResult:
    """Result from a memory query."""

    query: str
    results: list[SearchResult]
    summary: str

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "query": self.query,
            "results": [r.to_dict() for r in self.results],
            "summary": self.summary,
        }

    def to_markdown(self) -> str:
        """Convert to markdown format."""
        lines = [
            f"## Query: {self.query}",
            "",
            self.summary,
            "",
            "### Relevant Memories",
            "",
        ]

        for i, result in enumerate(self.results, 1):
            chunk = result.chunk
            lines.append(f"**{i}. [{chunk.chunk_type.upper()}]** {chunk.source}")
            lines.append(f"   Similarity: {result.similarity:.2%}")
            lines.append(
                f"   > {chunk.content[:200]}..."
                if len(chunk.content) > 200
                else f"   > {chunk.content}"
            )
            lines.append("")

        return "\n".join(lines)

    def __str__(self) -> str:
        """String representation."""
        lines = [f"Query: {self.query}", ""]

        if not self.results:
            lines.append("No relevant memories found.")
            return "\n".join(lines)

        lines.append(self.summary)
        lines.append("")
        lines.append("Relevant memories:")

        for i, result in enumerate(self.results, 1):
            chunk = result.chunk
            lines.append(f"\n{i}. [{chunk.chunk_type}] {chunk.source} ({result.similarity:.0%})")
            content = chunk.content[:150] + "..." if len(chunk.content) > 150 else chunk.content
            lines.append(f"   {content}")

        return "\n".join(lines)


class MemoryQuery:
    """High-level interface for querying vector memory.

    Provides the "What do I know about X?" functionality.
    """

    def __init__(
        self,
        store: VectorMemoryStore,
        embedding_provider: EmbeddingProvider,
    ) -> None:
        """Initialize the memory query interface.

        Args:
            store: Vector memory store.
            embedding_provider: Provider for generating embeddings.
        """
        self.store = store
        self.embedding_provider = embedding_provider

    def query(
        self,
        query_text: str,
        top_k: int = 5,
        source_filter: str | None = None,
        chunk_type_filter: str | None = None,
        min_similarity: float = 0.3,
        include_summary: bool = True,
    ) -> QueryResult:
        """Query the memory store.

        Args:
            query_text: Query text (e.g., "What do I know about X?").
            top_k: Number of results to return.
            source_filter: Optional filter by source.
            chunk_type_filter: Optional filter by chunk type.
            min_similarity: Minimum similarity threshold.
            include_summary: Whether to generate a summary.

        Returns:
            Query result with relevant memories.
        """
        # Generate embedding for query
        embeddings = self.embedding_provider.embed_sync([query_text])
        query_embedding = embeddings[0]

        # Search the store
        results = self.store.search(
            query_embedding=query_embedding,
            top_k=top_k,
            source_filter=source_filter,
            chunk_type_filter=chunk_type_filter,
            min_similarity=min_similarity,
        )

        # Generate summary
        summary = ""
        if include_summary and results:
            summary = self._generate_summary(query_text, results)
        elif not results:
            summary = "No relevant memories found for this query."

        return QueryResult(
            query=query_text,
            results=results,
            summary=summary,
        )

    def query_about(
        self,
        topic: str,
        top_k: int = 5,
    ) -> QueryResult:
        """Query about a specific topic.

        Convenience method for "What do I know about X?" queries.

        Args:
            topic: Topic to query about.
            top_k: Number of results to return.

        Returns:
            Query result with relevant memories.
        """
        query_text = f"What do I know about {topic}?"
        return self.query(query_text, top_k=top_k)

    def search_by_type(
        self,
        chunk_type: str,
        query_text: str = "",
        top_k: int = 5,
    ) -> QueryResult:
        """Search within a specific chunk type.

        Args:
            chunk_type: Type of chunks to search (e.g., "principle", "fact").
            query_text: Optional query text.
            top_k: Number of results to return.

        Returns:
            Query result with relevant memories of the specified type.
        """
        if query_text:
            return self.query(
                query_text=query_text,
                top_k=top_k,
                chunk_type_filter=chunk_type,
            )
        else:
            # Return all chunks of this type (no semantic search)
            # This requires a different approach - get all and sort by recency
            self.store.get_stats()
            # For now, just do a generic search
            return self.query(
                query_text=f"Show me {chunk_type} memories",
                top_k=top_k,
                chunk_type_filter=chunk_type,
            )

    def find_related(
        self,
        chunk_id: int,
        top_k: int = 5,
    ) -> QueryResult:
        """Find memories related to a specific chunk.

        Args:
            chunk_id: ID of the reference chunk.
            top_k: Number of related memories to return.

        Returns:
            Query result with related memories.
        """
        # Get the reference chunk
        chunk = self.store.get_chunk(chunk_id)
        if not chunk:
            return QueryResult(
                query=f"Find related to chunk {chunk_id}",
                results=[],
                summary="Chunk not found.",
            )

        # Use the chunk content as query
        return self.query(
            query_text=chunk.content,
            top_k=top_k + 1,  # +1 because we'll filter out the original
        )

    def get_recent_memories(
        self,
        limit: int = 10,
        source: str | None = None,
    ) -> list[MemoryChunk]:
        """Get recent memories.

        Args:
            limit: Maximum number of memories to return.
            source: Optional source filter.

        Returns:
            List of recent memory chunks.
        """
        # This would require a method in the store to get by recency
        # For now, return empty list (implement in store if needed)
        return []

    def _generate_summary(
        self,
        query: str,
        results: list[SearchResult],
    ) -> str:
        """Generate a human-readable summary of search results.

        Args:
            query: Original query.
            results: Search results.

        Returns:
            Summary string.
        """
        if not results:
            return "No relevant memories found."

        # Group by source and type
        by_source: dict[str, list[SearchResult]] = {}
        by_type: dict[str, list[SearchResult]] = {}

        for result in results:
            source = result.chunk.source
            chunk_type = result.chunk.chunk_type

            by_source.setdefault(source, []).append(result)
            by_type.setdefault(chunk_type, []).append(result)

        # Build summary
        lines = []

        # Overall match quality
        sum(r.similarity for r in results) / len(results)
        best_match = max(results, key=lambda r: r.similarity)

        lines.append(f"Found {len(results)} relevant memories")
        lines.append(f"Best match: {best_match.similarity:.0%} similarity")

        # Sources
        if len(by_source) > 1:
            sources_str = ", ".join(f"{s} ({len(c)})" for s, c in by_source.items())
            lines.append(f"Sources: {sources_str}")

        # Types
        if len(by_type) > 1:
            types_str = ", ".join(f"{t} ({len(c)})" for t, c in by_type.items())
            lines.append(f"Types: {types_str}")

        # Key content preview
        lines.append("")
        lines.append("Key points:")
        for i, result in enumerate(results[:3], 1):
            content = result.chunk.content
            # Extract first sentence or truncate
            first_sentence = content.split(".")[0] if "." in content else content[:100]
            if len(first_sentence) > 120:
                first_sentence = first_sentence[:117] + "..."
            lines.append(f"  {i}. {first_sentence}")

        return "\n".join(lines)

    def get_stats(self) -> dict[str, Any]:
        """Get memory statistics.

        Returns:
            Dictionary with memory statistics.
        """
        return self.store.get_stats()

    def format_results(
        self,
        query_result: QueryResult,
        format_type: str = "markdown",
    ) -> str:
        """Format query results in different formats.

        Args:
            query_result: Query result to format.
            format_type: Format type ("markdown", "json", "text").

        Returns:
            Formatted string.
        """
        if format_type == "markdown":
            return query_result.to_markdown()
        elif format_type == "json":
            import json

            return json.dumps(query_result.to_dict(), indent=2)
        else:
            return str(query_result)
