"""Query executor — runs execution plans against a LociDB instance."""

from __future__ import annotations

from typing import Any, Dict, List, TYPE_CHECKING

from .parser import MQLParseError, parse
from .planner import MemorizePlan, RecallPlan, plan

if TYPE_CHECKING:
    from ..core.db import LociDB


def execute(db: "LociDB", mql: str) -> List[Dict[str, Any]]:
    """Parse, plan, and execute an MQL statement against *db*.

    Returns a list of result dicts.
    """
    ast = parse(mql)
    execution_plan = plan(ast)

    if isinstance(execution_plan, RecallPlan):
        return _execute_recall(db, execution_plan)
    elif isinstance(execution_plan, MemorizePlan):
        return _execute_memorize(db, execution_plan)
    else:
        raise MQLParseError(f"Cannot execute plan type: {type(execution_plan)}")


def _execute_recall(db: "LociDB", p: RecallPlan) -> List[Dict[str, Any]]:
    if p.query_text is None:
        # No SIMILAR_TO — fall back to listing by importance
        nodes = db._store.list_nodes(
            scope=p.scope,
            min_importance=p.min_importance,
            limit=p.limit,
        )
        return [_project({"node": n, "similarity": None, "importance": n.importance}, p.projected_fields) for n in nodes]

    results = db.recall(
        query=p.query_text,
        entities=p.entity_filter or None,
        scope=p.scope,
        time_window_days=p.time_window_days,
        min_importance=p.min_importance,
        top_k=p.limit,
    )

    # Post-filter by similarity threshold
    if p.similarity_threshold > 0:
        results = [r for r in results if r["similarity"] >= p.similarity_threshold]

    return [_project(r, p.projected_fields) for r in results]


def _execute_memorize(db: "LociDB", p: MemorizePlan) -> List[Dict[str, Any]]:
    node = db.memorize(
        content=p.content,
        entities=p.entities,
        scope=p.scope,
        importance=None if p.auto_score else 0.5,
    )
    return [{"status": "memorized", "node_id": node.id, "importance": node.importance}]


def _project(result: Dict[str, Any], fields: List[str]) -> Dict[str, Any]:
    """Project only the requested fields from a result dict."""
    if "*" in fields:
        # Flatten node into the result
        node = result.get("node")
        if node is None:
            return result
        flat: Dict[str, Any] = {
            "content": node.content if hasattr(node, "content") else None,
            "importance": result.get("importance", getattr(node, "importance", None)),
            "similarity": result.get("similarity"),
            "entity_tags": getattr(node, "entity_tags", []),
            "scope": getattr(node, "context_scope", None),
            "timestamp": str(getattr(node, "timestamp", "")),
            "id": getattr(node, "id", None),
        }
        return flat

    projected: Dict[str, Any] = {}
    node = result.get("node")
    for f in fields:
        f = f.strip()
        if f in result:
            projected[f] = result[f]
        elif node and hasattr(node, f):
            projected[f] = getattr(node, f)
    return projected
