"""Phase 4.3: Query expansion via graph neighbors and FTS stemming.

Expands a search query by adding related terms from the knowledge graph,
so "configure" also finds items about "configuration settings" and
"embedding" also finds items about "vector" and "similarity".
"""
from __future__ import annotations
import logging

log = logging.getLogger("pulse.brain_search.query_expansion")


def expand_query_via_graph(query: str, max_expansions: int = 3) -> str:
    """Expand query with related terms from graph neighbors.

    Looks up the query terms in graph_nodes, follows COACTIVATION and
    RELATED_TO edges, and appends the neighbor texts as OR terms.
    """
    try:
        from pulse.core.brain_db import get_conn
        conn = get_conn()

        terms = [t for t in query.lower().split() if len(t) >= 4]
        if not terms:
            return query

        # Find graph nodes matching any query term
        expansions = set()
        for term in terms[:3]:  # limit to 3 terms to avoid explosion
            rows = conn.execute(
                "SELECT id FROM graph_nodes "
                "WHERE LOWER(text) LIKE ? LIMIT 5",
                (f"%{term}%",)
            ).fetchall()
            node_ids = [r[0] for r in rows]
            if not node_ids:
                continue

            # Follow edges to neighbors
            placeholders = ",".join("?" * len(node_ids))
            neighbors = conn.execute(
                f"SELECT DISTINCT n.text FROM graph_edges e "
                f"JOIN graph_nodes n ON n.id = e.to_node "
                f"WHERE e.from_node IN ({placeholders}) "
                f"AND e.edge_type IN ('CAUSES', 'PREVENTS', 'REQUIRES', 'ENABLES', 'RELATED_TO', 'COACTIVATION') "
                f"AND e.confidence > 0.4 "
                f"ORDER BY e.confidence DESC LIMIT ?",
                (*node_ids, max_expansions)
            ).fetchall()
            for n in neighbors:
                text = (n[0] or "").strip()
                if text and text.lower() not in query.lower() and len(text) < 50:
                    expansions.add(text)

        if not expansions:
            return query

        # Build expanded query: original OR expansion1 OR expansion2
        expanded = query
        for exp in list(expansions)[:max_expansions]:
            expanded += f" OR {exp}"
        log.debug("Query expanded: %r -> %r", query[:60], expanded[:120])
        return expanded
    except Exception as e:
        log.debug("Query expansion failed: %s", e)
        return query


def expand_query_fts(query: str) -> str:
    """For FTS5 queries: wrap terms for best porter stemming match.

    Porter stemming is built into FTS5 tokenizer. This function
    ensures proper FTS5 syntax for multi-word queries.
    """
    terms = query.split()
    if len(terms) <= 1:
        return query
    # Use implicit AND for multi-word queries (FTS5 default)
    # Each term will be stemmed by porter tokenizer automatically
    return " ".join(terms)


def apply_faceted_filters(results: dict, filters: dict):
    """Phase 4.4: Apply faceted filters to search results post-scoring.

    Supported filters:
        min_confidence: float (0-1) — drop items below threshold
        domain: str or list[str] — keep only matching domains
        source_type: str — CONFIRMED, OBSERVED, INFERRED, CONVERSATIONAL
        after: str (ISO date) — items newer than
        before: str (ISO date) — items older than
    """
    min_conf = filters.get("min_confidence")
    domains = filters.get("domain")
    if isinstance(domains, str):
        domains = [domains]
    source_type = filters.get("source_type")
    after_date = filters.get("after")
    before_date = filters.get("before")

    for source_name in list(results.keys()):
        hits = results[source_name]
        if not isinstance(hits, list):
            continue
        filtered = []
        for h in hits:
            if min_conf and h.get("confidence", 0) < min_conf:
                continue
            if domains and h.get("domain", "general") not in domains:
                continue
            if source_type and h.get("source_type", "") != source_type:
                continue
            ts = h.get("timestamp", "")
            if after_date and ts and ts < after_date:
                continue
            if before_date and ts and ts > before_date:
                continue
            filtered.append(h)
        results[source_name] = filtered
