"""Query helpers for g-gremlin memory."""

from __future__ import annotations

import json
import re
from pathlib import Path
from typing import Any, Mapping, Sequence


def filter_entries(
    entries: Sequence[Any],
    *,
    command_id: str | None = None,
    status: str | None = None,
    tag: str | None = None,
) -> list[dict[str, Any]]:
    filtered: list[dict[str, Any]] = []
    status_norm = normalize_status(status) if status else None
    tag_norm = str(tag or "").strip().lower()
    for raw in entries:
        if not isinstance(raw, dict):
            continue
        if command_id and str(raw.get("command_id") or "") != command_id:
            continue
        if status_norm and normalize_status(raw.get("status")) != status_norm:
            continue
        if tag_norm:
            tags = [str(value).strip().lower() for value in raw.get("tags", []) if str(value).strip()]
            if tag_norm not in tags:
                continue
        filtered.append(raw)
    filtered.sort(key=lambda row: str(row.get("created_at") or ""), reverse=True)
    return filtered


def search_entries(entries: Sequence[Any], query: str, *, limit: int = 10) -> list[dict[str, Any]]:
    tokens = tokenize(query)
    if not tokens:
        return []
    scored: list[tuple[int, str, str, dict[str, Any]]] = []
    for raw in entries:
        if not isinstance(raw, dict):
            continue
        score = score_entry(raw, tokens)
        if score <= 0:
            continue
        scored.append((score, str(raw.get("created_at") or ""), str(raw.get("entry_id") or ""), raw))
    scored.sort(key=lambda row: (-row[0], row[1], row[2]), reverse=False)
    return [entry for _, _, _, entry in scored[: max(0, int(limit))]]


def normalize_status(value: Any) -> str:
    normalized = str(value or "").strip().lower()
    if normalized in {"success", "pass", "passed", "ok"}:
        return "success"
    if normalized in {"partial", "warning", "warnings", "degraded"}:
        return "partial"
    if normalized in {"error", "errors", "fail", "failed"}:
        return "fail"
    if normalized in {"unknown", "other"}:
        return "unknown"
    return "success" if normalized == "" else "unknown"


def tokenize(text: str) -> list[str]:
    return [token for token in re.findall(r"[a-z0-9][a-z0-9._-]*", str(text).lower()) if token]


def score_entry(entry: Mapping[str, Any], tokens: Sequence[str]) -> int:
    haystacks = {
        "title": str(entry.get("title") or "").lower(),
        "summary": str(entry.get("summary") or "").lower(),
        "command_id": str(entry.get("command_id") or "").lower(),
        "issues": " ".join(normalize_text_list(entry.get("issues"))).lower(),
        "next": " ".join(normalize_text_list(entry.get("suggested_next_steps"))).lower(),
        "tags": " ".join(str(tag).lower() for tag in entry.get("tags", []) if tag),
    }
    score = 0
    for token in tokens:
        if token in haystacks["title"]:
            score += 5
        if token in haystacks["tags"]:
            score += 4
        if token in haystacks["command_id"]:
            score += 3
        if token in haystacks["summary"]:
            score += 2
        if token in haystacks["issues"]:
            score += 2
        if token in haystacks["next"]:
            score += 1
    return score


def normalize_text_list(value: Any) -> list[str]:
    if value is None:
        return []
    raw_items = list(value) if isinstance(value, (list, tuple, set)) else [value]
    normalized: list[str] = []
    seen: set[str] = set()
    for raw in raw_items:
        text = stringify_textish(raw).strip()
        if not text or text in seen:
            continue
        seen.add(text)
        normalized.append(text)
    return normalized


def stringify_textish(value: Any) -> str:
    if isinstance(value, str):
        return value
    if isinstance(value, Path):
        return value.as_posix()
    if isinstance(value, Mapping):
        issue_type = str(value.get("type") or value.get("diagnosis") or "").strip()
        cell = str(value.get("cell") or "").strip()
        message = str(
            value.get("message")
            or value.get("summary")
            or value.get("detail")
            or value.get("formula")
            or value.get("current_formula")
            or ""
        ).strip()
        parts = [part for part in (issue_type, cell, message) if part]
        if parts:
            return ": ".join(parts[:2]) if len(parts) == 2 else " - ".join(parts)
        return json.dumps(normalize_scalar(value), ensure_ascii=True, sort_keys=True)
    return str(value)


def normalize_scalar(value: Any) -> Any:
    if isinstance(value, Path):
        return value.as_posix()
    if isinstance(value, Mapping):
        return {str(key): normalize_scalar(item) for key, item in value.items()}
    if isinstance(value, (list, tuple, set)):
        return [normalize_scalar(item) for item in value]
    if isinstance(value, (str, int, float, bool)) or value is None:
        return value
    return str(value)
