from __future__ import annotations

import json
import re
from pathlib import Path
from typing import Optional

from core_memory.config import load_query_expansions
from core_memory.entity.registry import load_entity_registry
from core_memory.entity.retrieval import expand_query_with_entities, infer_query_entity_context
from core_memory.policy.incidents import incident_match_strength, load_incidents

from .normalize import classify_intent as _classify_intent_canonical
from .normalize import normalize_query as _normalize_query_canonical

_STOP = {"the", "and", "for", "with", "that", "this", "what", "when", "why", "did", "was", "are", "about", "into", "from", "where"}


def _tokenize(text: str) -> set[str]:
    """Tokenize text into searchable terms."""
    return {t.lower() for t in (text or "").replace("_", " ").replace("-", " ").split() if len(t) >= 3}


def _is_memory_intent(text: str) -> bool:
    """Detect if query indicates memory recall intent."""
    q = (text or "").lower()
    cues = [
        "remember",
        "what did we decide",
        "earlier",
        "last time",
        "previous",
        "why did we",
        "recall",
        "history",
        "find memory",
    ]
    return any(c in q for c in cues)


def _expand_query_tokens(
    text: str,
    base_tokens: set[str],
    max_extra: int = 24,
    root: Optional[Path] = None,
) -> set[str]:
    """Bounded synonym/entity expansion for better deterministic recall hits.

    Expansions are loaded from config (shipped defaults + user overrides).
    If root is provided, user overrides from the store config dir are included.
    """
    q = (text or "").lower()
    expanded = set(base_tokens)

    config = load_query_expansions(root)
    phrase_map = config.get("phrase_map", {})
    token_map = config.get("token_map", {})

    extras: list[str] = []
    for phrase, words in phrase_map.items():
        if phrase in q:
            extras.extend(list(words))

    for t in list(base_tokens):
        for w in token_map.get(t, set()):
            extras.append(w)

    for w in extras:
        if len(expanded) >= len(base_tokens) + max(0, int(max_extra)):
            break
        expanded.add(w)

    return expanded


def _stem_lite(tok: str) -> str:
    t = tok
    if t.endswith("ing") and len(t) > 5:
        t = t[:-3]
    elif t.endswith("ed") and len(t) > 4:
        t = t[:-2]
    elif t.endswith("s") and len(t) > 4:
        t = t[:-1]
    return t


def normalize_query(query: str) -> dict:
    return _normalize_query_canonical(query)


def classify_intent(query: str) -> dict:
    return _classify_intent_canonical(query)


def _load_topic_aliases(root: Path) -> list[dict]:
    p = Path(__file__).parents[1] / "data" / "topic_aliases.json"
    if not p.exists():
        return []
    try:
        data = json.loads(p.read_text(encoding="utf-8"))
        return data if isinstance(data, list) else []
    except Exception:
        return []


def resolve_query_anchors(query: str, root: Path) -> dict:
    qn = normalize_query(query)
    qraw = qn["raw_normalized"]
    qtokens = set(qraw.split())

    matched_incidents = []
    expansions = []
    for row in load_incidents(root):
        iid = str(row.get("incident_id") or "")
        st = float(incident_match_strength(query, iid, root))
        if st <= 0:
            continue
        matched_incidents.append({"incident_id": iid, "strength": st})
        expansions.extend(iid.replace("_", " ").split())

    matched_topics = []
    for row in _load_topic_aliases(root):
        tk = str(row.get("topic_key") or "")
        aliases = [str(a or "").lower() for a in (row.get("aliases") or [])]
        strength = 0.0
        for a in aliases:
            an = " ".join(a.replace("_", " ").replace("-", " ").split())
            if an and an in qraw:
                strength = max(strength, 1.0)
            else:
                ats = set(an.split())
                if ats and qtokens.intersection(ats):
                    strength = max(strength, 0.5)
        if strength > 0:
            matched_topics.append({"topic_key": tk, "strength": strength})
            expansions.extend(tk.replace("_", " ").split())

    expanded_query = " ".join([qraw] + expansions).strip()
    expanded_query = re.sub(r"\s+", " ", expanded_query)

    entity_registry = load_entity_registry(root)
    entity_context = infer_query_entity_context(query, entity_registry)
    expanded_query = expand_query_with_entities(expanded_query, entity_context, entity_registry)

    return {
        "normalized": qn,
        "matched_incidents": sorted(matched_incidents, key=lambda x: (-x["strength"], x["incident_id"])),
        "matched_topics": sorted(matched_topics, key=lambda x: (-x["strength"], x["topic_key"])),
        "matched_entities": {
            "resolved_entity_ids": list(entity_context.get("resolved_entity_ids") or []),
            "matched_aliases": list(entity_context.get("matched_aliases") or []),
            "labels": list(entity_context.get("labels") or []),
        },
        "expanded_query": expanded_query,
    }
