#!/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 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)
from .search_utils import compute_salience, days_since, get_retrieval_count


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

    Only returns battle-tested knowledge that has been independently confirmed
    by multiple extraction passes or reconsolidation merges. Before consolidation
    runs, this correctly returns empty (nothing is proven yet).
    """
    import re
    min_cc = config.get("wins", {}).get("min_consolidation_count", 3)
    results = []
    if not WINS_DIR.exists():
        return results
    for wf in sorted(WINS_DIR.rglob("*.md")):
        with open(wf, "r", encoding="utf-8") as f:
            content = f.read()
        sections = content.split("## ")
        for section in sections[1:]:
            lines = section.strip().split("\n")
            title = lines[0].strip()
            body = "\n".join(lines[1:]).strip()
            # Only include entries with sufficient consolidation count
            cc_match = re.search(r"\*\*Consolidation Count:\*\*\s*(\d+)", body)
            cc = int(cc_match.group(1)) if cc_match else 1
            if cc < min_cc:
                continue
            sim = search_relevance(query, f"{title} {body}")
            if sim >= 0.2:
                results.append({
                    "source": "wins", "file": wf.name, "title": title,
                    "text": body[:200], "relevance": round(sim, 3),
                    "salience": compute_salience(sim, 30, config,
                                                 retrieval_count=get_retrieval_count(body[:200])),
                    "consolidation_count": cc,
                    "type": "proven_approach"
                })
    return sorted(results, key=lambda x: -x["salience"])


def search_fails(query: str, config: dict) -> list:
    """Search FAIL history for past mistakes."""
    results = []
    if not FAILS_DIR.exists():
        return results
    for ff in sorted(FAILS_DIR.rglob("*.md")):
        with open(ff, "r", encoding="utf-8") as f:
            content = f.read()
        sections = content.split("## ")
        for section in sections[1:]:
            lines = section.strip().split("\n")
            title = lines[0].strip()
            body = "\n".join(lines[1:]).strip()
            sim = search_relevance(query, f"{title} {body}")
            if sim >= 0.2:
                results.append({
                    "source": "fails", "file": ff.name, "title": title,
                    "text": body[:200], "relevance": round(sim, 3),
                    "salience": compute_salience(sim, 30, config,
                                                 retrieval_count=get_retrieval_count(body[:200])),
                    "type": "past_mistake"
                })
    return sorted(results, key=lambda x: -x["salience"])


def search_domains(query: str, config: dict) -> list:
    """Search domain knowledge files."""
    results = []
    if not DOMAINS_DIR.exists():
        return results
    for df in sorted(DOMAINS_DIR.rglob("*.md")):
        with open(df, "r", encoding="utf-8") as f:
            content = f.read()
        sim = search_relevance(query, content[:500])
        if sim >= 0.2:
            results.append({
                "source": "domains", "file": df.name,
                "text": content[:200], "relevance": round(sim, 3),
                "salience": compute_salience(sim, 7, config,
                                             retrieval_count=get_retrieval_count(content[:200])),
                "type": "domain_context"
            })
    return sorted(results, key=lambda x: -x["salience"])


def search_private(query: str, config: dict) -> list:
    """Search private brain files (Hippocampus/Private/)."""
    results = []
    private_dir = HIPPOCAMPUS_DIR / "Private"
    if not private_dir.exists():
        return results
    for pf in sorted(private_dir.rglob("*.md")):
        with open(pf, "r", encoding="utf-8") as f:
            content = f.read()
        sections = content.split("## ")
        for section in sections[1:]:
            lines = section.strip().split("\n")
            title = lines[0].strip()
            body = "\n".join(lines[1:]).strip()
            sim = search_relevance(query, f"{title} {body}")
            if sim >= 0.2:
                results.append({
                    "source": "private", "file": pf.name, "title": title,
                    "text": body[:200], "relevance": round(sim, 3),
                    "salience": compute_salience(sim, 7, config,
                                                 retrieval_count=get_retrieval_count(body[:200])),
                    "type": "private_knowledge"
                })
    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/*.md files (auto_patterns, skill_patterns, current_patterns)."""
    results = []
    if not PATTERNS_DIR.exists():
        return results
    for pf in sorted(PATTERNS_DIR.rglob("*.md")):
        with open(pf, "r", encoding="utf-8") as f:
            content = f.read()
        sections = content.split("## ")
        for section in sections[1:]:
            lines = section.strip().split("\n")
            title = lines[0].strip()
            body = "\n".join(lines[1:]).strip()
            sim = search_relevance(query, f"{title} {body}")
            if sim >= 0.2:
                results.append({
                    "source": "patterns", "file": pf.name, "title": title,
                    "text": body[:200], "relevance": round(sim, 3),
                    "salience": compute_salience(sim, 14, config,
                                                 retrieval_count=get_retrieval_count(body[:200])),
                    "type": "pattern"
                })
    return sorted(results, key=lambda x: -x["salience"])


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"])
