# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Primary brain loaders for agent boot context injection."""
from __future__ import annotations

from pulse.core.brain_utils import (
    STATE_DIR, ANTI_PATTERNS_DIR,
    LESSONS_JSONL, FAILS_JSONL, PATTERNS_JSONL,
    load_json_dict, load_json,
)
from pulse.brain.brain_search import brain_search
from pulse.brain.boot.loaders_support import (
    _get_recent_titles, _load_jsonl_titles,
    _get_confirmed_titles, _extract_search_hits,
    _load_cross_agent_intel,
)


def load_recent_brain(agent_name: str = "search") -> dict:
    """Load recent brain data. IMPERATIVE — injected whether agents ask or not.

    Args:
        agent_name: Name of the booting agent. Used to load working memory context
                    for the correct agent (not hardcoded "search"). Defaults to
                    "search" for backward compatibility.
    """
    result = {"lessons": [], "failures": [], "anti_patterns": [], "patterns": [], "user_profile": {}, "confirmed_ground_truth": []}

    # Load User Profile (The distilled "Forgetfulness" Engine output)
    profile_file = STATE_DIR / "user_profile.json"
    if profile_file.exists():
        try:
            profile_data = load_json_dict(profile_file)
            result["user_profile"] = profile_data
        except Exception:
            pass

    # SQLite fast-path (sub-ms) with JSONL fallback (11.3MB scan)
    result["lessons"] = _get_recent_titles("lessons", 10) or _load_jsonl_titles(LESSONS_JSONL, 10)
    result["failures"] = _get_recent_titles("failures", 10) or _load_jsonl_titles(FAILS_JSONL, 10)

    # Anti-patterns from SQLite (JSON Kill List migration)
    try:
        from pulse.core.brain_db import get_active_anti_patterns
        ap_items = get_active_anti_patterns()
        active = [ap for ap in ap_items if ap.get("status") == "active"]
        active.sort(key=lambda ap: ap.get("evidence_count", 0), reverse=True)
        patterns = [f"{ap.get('description', '')[:60]} -> {ap.get('rule', '')[:60]}"
                    for ap in active]
        result["anti_patterns"] = patterns[:10]
    except Exception:
        pass

    result["patterns"] = _get_recent_titles("patterns", 5) or _load_jsonl_titles(PATTERNS_JSONL, 5)

    # Ground truth anchors — CONFIRMED items across lessons + patterns + failures (top 5 total)
    confirmed: list[str] = []
    for _tbl in ("lessons", "patterns", "failures"):
        confirmed.extend(_get_confirmed_titles(_tbl, limit=3))
    # Deduplicate while preserving order, keep top 5
    seen_gt: set[str] = set()
    deduped_gt: list[str] = []
    for t in confirmed:
        key = t.lower()[:40]
        if key not in seen_gt:
            seen_gt.add(key)
            deduped_gt.append(t)
    result["confirmed_ground_truth"] = deduped_gt[:5]

    # Decisions (N0 — past choices with context)
    try:
        from pulse.brain.persistence.decision_store import _load_decisions
        entries = _load_decisions()
        active = [e for e in entries if e.get("status") == "active"]
        active.sort(key=lambda x: x.get("timestamp", ""), reverse=True)
        result["decisions"] = []
        for e in active[:5]:
            alt_text = f" over {', '.join(e.get('alternatives_rejected', []))}" if e.get('alternatives_rejected') else ""
            reason = f" -- {e.get('reasoning', '')[:40]}" if e.get('reasoning') else ""
            result["decisions"].append(
                f"{e.get('choice', '')[:50]}{alt_text}{reason} (scope: {e.get('scope', '?')})"
            )
    except Exception:
        pass

    # Item 96: Temporal context reinstatement — inject last session's working memory
    try:
        from pulse.brain.memory.working_memory import get_active_contexts
        wm_items = get_active_contexts(agent_name)
        if wm_items:
            result["working_memory_context"] = [it.get("text", "")[:100] for it in wm_items[:5]]
    except Exception:
        pass

    return result


def load_relevant_brain(task_query: str, task_type: str = "") -> dict:
    """Load brain knowledge RELEVANT to the task via brain_search.

    Phase 4: task_type enables credit-driven attention weights and
    prioritize_by_credit() reranking so high-value items surface first.
    """
    if not brain_search or not task_query or len(task_query) < 5:
        return load_recent_brain()

    profile_file = STATE_DIR / "user_profile.json"
    user_profile = load_json_dict(profile_file) if profile_file.exists() else {}

    try:
        # Skip embeddings at boot — 177MB index parse adds ~40s cold start
        search_result = brain_search(
            task_query, max_per_source=5,
            task_type=task_type or "general",
            exclude_sources={"embeddings"})
        hits = search_result.get("results", {})
        total = search_result.get("total_results", 0)

        if total < 3:
            recent_data = load_recent_brain()
            recent_data["user_profile"] = user_profile
            return recent_data

        result = {
            "lessons": _extract_search_hits(hits.get("wins", []), 5),
            "failures": _extract_search_hits(hits.get("fails", []), 5),
            "anti_patterns": _extract_search_hits(hits.get("anti_patterns", []), 5),
            "patterns": _extract_search_hits(hits.get("domains", []), 3),
            "decisions": _extract_search_hits(hits.get("decisions", []), 3),
            "user_profile": user_profile,
        }

        # Phase 4: Rerank lists by accumulated reward credit
        try:
            from pulse.brain.metacognition.learned_prompt import prioritize_by_credit
            for key in ("lessons", "failures", "patterns"):
                if result[key]:
                    result[key] = prioritize_by_credit(result[key], limit=len(result[key]))
        except Exception:
            pass  # Cold start or missing ledger — no reranking

        return result
    except Exception:
        return load_recent_brain()


def load_daemon_injections(max_items: int = 5, agent_name: str = "") -> dict:
    """Load D4 constraints + D5 gate feedback + D6 golden paths + U7 cross-agent intel.

    Returns: {"synthetic_constraints": [...], "gate_feedback": [...], "golden_paths": [...], "cross_agent_intel": [...]}
    """
    result: dict = {"synthetic_constraints": [], "gate_feedback": [], "golden_paths": [], "cross_agent_intel": []}

    # D4: Synthetic constraints (precognitive guardrails)
    constraints_file = STATE_DIR / "synthetic_constraints.json"
    if constraints_file.exists():
        try:
            data = load_json_dict(constraints_file)
            active = [c for c in data.get("constraints", [])
                      if c.get("status") == "active"]
            active.sort(key=lambda c: -c.get("confidence", 0))
            result["synthetic_constraints"] = active[:max_items]
        except Exception:
            pass

    # D5: Gate feedback (temporary anti-patterns from swarm failures)
    feedback_file = STATE_DIR / "gate_feedback.json"
    if feedback_file.exists():
        try:
            from datetime import datetime, timezone
            data = load_json_dict(feedback_file)
            now = datetime.now(timezone.utc)
            active = []
            for e in data.get("entries", []):
                if e.get("status") != "active":
                    continue
                try:
                    expires = datetime.fromisoformat(e.get("expires_at", ""))
                    if expires.tzinfo is None:
                        expires = expires.replace(tzinfo=timezone.utc)
                    if expires < now:
                        continue
                except (ValueError, TypeError):
                    pass
                active.append(e)
            result["gate_feedback"] = active[:max_items]
        except Exception:
            pass

    # D6: Golden paths (proven success strategies from pathweaver)
    golden_file = STATE_DIR / "golden_paths.json"
    if golden_file.exists():
        try:
            data = load_json_dict(golden_file)
            active = [p for p in data.get("paths", [])
                      if p.get("status") == "active"]
            active.sort(key=lambda p: -p.get("confidence", 0))
            result["golden_paths"] = active[:max_items]
        except Exception:
            pass

    # U7: Cross-agent learning (Dr. Mirror Neurons)
    result["cross_agent_intel"] = _load_cross_agent_intel(agent_name, max_items)

    return result
