#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
# See LICENSE file for details.
"""Brain Search Sources (Core): anti-patterns, predictions, evidence."""

import logging
from pathlib import Path

SCRIPT_DIR = Path(__file__).resolve().parent
ROOT_DIR = SCRIPT_DIR.parent.parent.parent

from pulse.core.brain_utils import (load_json, load_json_dict, search_relevance,
                         EVIDENCE_DIR, ANTI_PATTERNS_DIR, STATE_DIR)
from pulse.brain.index.search_utils import compute_salience, days_since, get_retrieval_count
from .search_sources_ext import _search_sqlite
from .search_anti_patterns import search_anti_patterns

log = logging.getLogger("pulse.search.core")

def search_predictions(query: str, config: dict) -> list:
    """Search active prediction alerts (Phase 8 feedback loop)."""
    results = []
    alerts_file = STATE_DIR / "prediction_alerts.json"
    if not alerts_file.exists():
        return results

    data = load_json_dict(alerts_file)
    for alert in data.get("alerts", []):
        if alert.get("status") != "active":
            continue
        
        # Match against task title + prediction text
        search_text = f"{alert.get('task_title', '')} {alert.get('prediction', '')}"
        sim = search_relevance(query, search_text)
        
        # High threshold (0.4) because predictions are specific to tasks
        # If matched, give HUGE salience boost (it's an active warning)
        if sim >= 0.4:
            days = days_since(alert.get("timestamp", ""))
            results.append({
                "source": "predictions",
                "title": f"[PREDICTION] {alert.get('task_title', '')[:40]}...",
                "text": alert.get("prediction", "")[:300],
                "relevance": round(sim, 3),
                "salience": compute_salience(sim, days, config, 10.0), # 10x boost
                "severity": "critical",
                "confidence": alert.get("confidence", 0.5),
                "task_id": alert.get("task_id", "")
            })
            
    return sorted(results, key=lambda x: -x["salience"])


_SKIP_EVIDENCE_FILES = {"wants.json", "dont_wants.json", "strategic_intents.json"}
_MAX_EVIDENCE_FILES = 50
_MAX_ITEMS_PER_FILE = 2000

def search_evidence(query: str, config: dict) -> list:
    """Search raw evidence files (wants, dont_wants, sessions, learnings, etc.).

    SQLite FTS5 fast-path with flat-file rglob fallback.
    The evidence table content column stores the full text, so FTS5 MATCH
    covers the same key fallback chain that the JSON scan builds manually.
    """
    # --- SQLite fast-path (activates once evidence table is populated) ---
    sqlite_results = _search_sqlite("evidence", "evidence", "observation", query, config)
    if sqlite_results:
        return sqlite_results

    # --- Flat-file fallback (primary path until migration) ---
    results = []
    if not EVIDENCE_DIR.exists():
        return results
    file_count = 0
    for ef in sorted(EVIDENCE_DIR.rglob("*.json")):
        if ef.name in _SKIP_EVIDENCE_FILES:
            continue
        if file_count >= _MAX_EVIDENCE_FILES:
            break
        file_count += 1
        items = load_json(ef)
        if not isinstance(items, list):
            continue
        for item in items[:_MAX_ITEMS_PER_FILE]:
            # Extended fallback chain — covers all known key formats
            text = (item.get("evidence", "")
                    or item.get("want", "")
                    or item.get("dont_want", "")
                    or item.get("description", "")
                    or item.get("text", "")
                    or item.get("lesson", "")
                    or item.get("discovery", "")
                    or item.get("decision", "")
                    or item.get("issue", "")
                    or item.get("user_insight", ""))
            # Domain learnings: combine prompt + response summaries
            if not text and item.get("prompt_summary"):
                ps = item.get("prompt_summary", "")
                rs = item.get("response_summary", "")
                text = f"{ps} {rs}".strip() if rs else ps
            # Agent sessions: combine learnings + failures lists
            if not text:
                parts = []
                if isinstance(item.get("learnings"), list):
                    parts.extend(item["learnings"])
                elif isinstance(item.get("learnings"), str) and item["learnings"]:
                    parts.append(item["learnings"])
                if isinstance(item.get("failures"), list):
                    parts.extend(item["failures"])
                elif isinstance(item.get("failures"), str) and item["failures"]:
                    parts.append(item["failures"])
                if parts:
                    text = " ".join(parts)
            # Always prepend domain for topic matching
            domain = item.get("domain", "")
            if domain and text:
                text = f"{domain} {text}"
            # Context envelope matching (Phase 4): boost if query matches branch/files
            ctx = item.get("context", {})
            ctx_text = f"{ctx.get('git_branch', '')} {' '.join(ctx.get('recent_files', []))}"
            if ctx_text.strip():
                text = f"{text} {ctx_text}"
            sim = search_relevance(query, text)
            if sim >= 0.2:
                days = days_since(item.get("date") or item.get("timestamp"))
                item_sal = item.get("salience", 1.0)
                results.append({
                    "source": "evidence", "file": ef.name,
                    "text": text[:500], "relevance": round(sim, 3),
                    "salience": compute_salience(sim, days, config, item_sal,
                                                 retrieval_count=get_retrieval_count(text[:200])),
                    "confidence": item.get("confidence", 0.5),
                    "type": item.get("type", "observation"),
                    "context": ctx if ctx else None,
                })
    return sorted(results, key=lambda x: -x["salience"])
