#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
# See LICENSE file for details.
"""
Brain Search Sources (Extended): Patterns, schemas, procedures, graph, etc. (V43.13)

Extended search functions for specialized brain regions.
All functions have the same signature: (query: str, config: dict) -> list

Dependencies: Python stdlib + brain_utils + search_utils
"""

import json
import sys
from pathlib import Path

from pulse.core.brain_utils import (load_json, load_json_dict, search_relevance,
                         PATTERNS_DIR, WINS_DIR, FAILS_DIR, DOMAINS_DIR, HIPPOCAMPUS_DIR,
                         LESSONS_JSONL, FAILS_JSONL, PATTERNS_JSONL, PRIVATE_JSONL)
from .search_utils import compute_salience, days_since, get_retrieval_count


def _search_jsonl(jsonl_path: Path, query: str, config: dict,
                  source_name: str, result_type: str) -> list:
    """Generic JSONL search: stream lines, match query, return scored results.

    Returns empty list if JSONL doesn't exist or is empty, allowing MD fallback.
    """
    if not jsonl_path.exists() or jsonl_path.stat().st_size == 0:
        return []
    results = []
    with open(jsonl_path, "r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                entry = json.loads(line)
            except json.JSONDecodeError:
                continue
            content = entry.get("content", "")
            title = entry.get("title", "")
            search_text = f"{title} {content}"
            sim = search_relevance(query, search_text)
            if sim < 0.2:
                continue
            days = days_since(entry.get("timestamp", ""))
            item_salience = entry.get("salience", 1.0)
            results.append({
                "source": source_name,
                "file": jsonl_path.name,
                "title": title,
                "text": content[:200],
                "relevance": round(sim, 3),
                "salience": compute_salience(sim, days, config, item_salience,
                                             retrieval_count=get_retrieval_count(content[:200])),
                "confidence": entry.get("confidence", 0.5),
                "type": result_type,
                "consolidation_count": entry.get("consolidation_count", 1),
                "entry_id": entry.get("id", ""),
            })
    return sorted(results, key=lambda x: -x["salience"])


def search_wins(query: str, config: dict) -> list:
    """Search for PROVEN approaches — lessons with consolidation_count >= threshold.

    JSONL fast-path: filters by consolidation_count directly from structured data.
    MD fallback: regex extraction from markdown (legacy).
    """
    import re
    min_cc = config.get("wins", {}).get("min_consolidation_count", 3)

    # JSONL-only: structured consolidation_count filtering
    jsonl_results = _search_jsonl(LESSONS_JSONL, query, config, "wins", "proven_approach")
    return [r for r in jsonl_results if r.get("consolidation_count", 1) >= min_cc]


def search_fails(query: str, config: dict) -> list:
    """Search FAIL history for past mistakes. JSONL only."""
    return _search_jsonl(FAILS_JSONL, query, config, "fails", "past_mistake")


def search_domains(query: str, config: dict) -> list:
    """Search domain knowledge files (JSON only — MDs removed from Hippocampus)."""
    results = []
    if not DOMAINS_DIR.exists():
        return results
    for df in sorted(DOMAINS_DIR.rglob("*.json")):
        if df.name == "knowledge_graph.json":
            continue  # Searched by search_graph()
        items = load_json(df)
        if isinstance(items, dict):
            items = items.get("facts", items.get("items", [items]))
        if not isinstance(items, list):
            continue
        for item in items:
            text = item.get("description", "") or item.get("text", "") or item.get("claim", "")
            sim = search_relevance(query, text)
            if sim >= 0.2:
                days = days_since(item.get("timestamp", ""))
                results.append({
                    "source": "domains", "file": df.name,
                    "text": text[:200], "relevance": round(sim, 3),
                    "salience": compute_salience(sim, days, config,
                                                 retrieval_count=get_retrieval_count(text[:200])),
                    "type": "domain_context",
                    "confidence": item.get("confidence", 0.5),
                })
    return sorted(results, key=lambda x: -x["salience"])


def search_private(query: str, config: dict) -> list:
    """Search private brain files. JSONL + JSON (no MD)."""
    # JSONL fast-path
    results = _search_jsonl(PRIVATE_JSONL, query, config, "private", "private_knowledge")

    # Also search private JSON files (evidence)
    private_dir = HIPPOCAMPUS_DIR / "Private"
    if private_dir.exists():
        for pf in sorted(private_dir.rglob("*.json")):
            items = load_json(pf)
            if not isinstance(items, list):
                continue
            for item in items:
                text = item.get("description", "") or item.get("text", "")
                sim = search_relevance(query, text)
                if sim >= 0.2:
                    days = days_since(item.get("timestamp") or item.get("date"))
                    results.append({
                        "source": "private", "file": pf.name,
                        "text": text[:200], "relevance": round(sim, 3),
                        "salience": compute_salience(sim, days, config,
                                                     retrieval_count=get_retrieval_count(text[:200])),
                        "type": "private_knowledge"
                    })
    return sorted(results, key=lambda x: -x["salience"])


def search_patterns(query: str, config: dict) -> list:
    """Search Patterns files. JSONL only."""
    return _search_jsonl(PATTERNS_JSONL, query, config, "patterns", "pattern")


def search_schemas(query: str, config: dict) -> list:
    """Search proven schemas (2x weight — these are battle-tested patterns)."""
    results = []
    schemas_file = PATTERNS_DIR / "schemas.json"
    if not schemas_file.exists():
        return results
    data = load_json_dict(schemas_file)
    for schema in data.get("schemas", []):
        name = schema.get("name", "")
        desc = schema.get("description", "")
        search_text = f"{name} {desc}"
        sim = search_relevance(query, search_text)
        if sim >= 0.2:
            conf = schema.get("confidence", 0.5)
            # Schemas get 2x salience boost (proven patterns)
            sal = compute_salience(sim, 0, config, conf * 10,
                                  retrieval_count=get_retrieval_count(search_text[:200]))
            results.append({
                "source": "schemas", "title": name,
                "text": desc[:200], "relevance": round(sim, 3),
                "salience": round(sal * 2.0, 4),
                "confidence": conf,
                "schema_id": schema.get("schema_id", ""),
                "evidence_count": schema.get("evidence_count", 0),
                "type": "proven_schema",
            })
    return sorted(results, key=lambda x: -x["salience"])


def search_graph(query: str, config: dict) -> list:
    """Search knowledge graph for causal relationships (Phase 7)."""
    results = []
    try:
        from pulse.graph.knowledge_graph import query_related
        hits = query_related(query, max_results=5)
        for h in hits:
            results.append({
                "source": "graph",
                "text": h.get("text", "")[:200],
                "type": h.get("type", "CONCEPT"),
                "relationship": h.get("relationship", ""),
                "relevance": h.get("confidence", 0.5),
                "salience": compute_salience(
                    h.get("confidence", 0.5), 0, config,
                    retrieval_count=get_retrieval_count(h.get("text", "")[:200])
                ),
                "confidence": h.get("confidence", 0.5),
            })
    except ImportError:
        pass  # knowledge_graph not available
    except Exception:
        pass  # Graph not built yet or other error
    return sorted(results, key=lambda x: -x.get("salience", 0))


def search_procedures(query: str, config: dict) -> list:
    """Search procedural log for relevant proven workflows."""
    results = []
    proc_file = HIPPOCAMPUS_DIR / "Evidence" / "Metrics" / "procedural_log.json"
    if not proc_file.exists():
        return results
    data = load_json_dict(proc_file)
    for proc in data.get("procedures", []):
        name = proc.get("name", "")
        desc = proc.get("description", "")
        domain = proc.get("domain", "")
        search_text = f"{name} {desc} {domain}"
        sim = search_relevance(query, search_text)
        if sim >= 0.2 and proc.get("confidence", 0) >= 0.3:
            results.append({
                "source": "procedures", "title": name,
                "text": f"{desc} (success: {proc.get('success_rate', 0):.0%}, "
                        f"{proc.get('executions', 0)} runs)",
                "relevance": round(sim, 3),
                "salience": compute_salience(sim, 0, config, proc.get("confidence", 0.5) * 5,
                                             retrieval_count=get_retrieval_count(search_text[:200])),
                "confidence": proc.get("confidence", 0),
                "type": "proven_procedure",
            })
    return sorted(results, key=lambda x: -x["salience"])
