#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
# See LICENSE file for details.
"""
PULSE API — Brain router. Endpoints for brain data (lessons, failures, patterns, graph, etc).
"""
from __future__ import annotations

import json
import re
import sys
import unicodedata
from pathlib import Path
from fastapi import APIRouter, HTTPException, Request

from pulse.core.pii_filter import redact_pii
from pulse.core.brain_utils import (
    read_jsonl_entries, LESSONS_JSONL, FAILS_JSONL, PATTERNS_JSONL,
)

# --- Input Sanitization ---
_QUERY_UNSAFE = re.compile(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]')


def _sanitize_query(q: str) -> str:
    """Remove control chars, normalize unicode, collapse whitespace."""
    q = _QUERY_UNSAFE.sub('', q)
    q = unicodedata.normalize('NFC', q)
    q = ' '.join(q.split())
    return q.strip()

SCRIPT_DIR = Path(__file__).resolve().parent
ROOT_DIR = SCRIPT_DIR.parent.parent
TOOLS_DIR = ROOT_DIR / "pulse" / "brain"
HIPPOCAMPUS = ROOT_DIR / "Hippocampus"


router = APIRouter(prefix="/v1/brain", tags=["brain"])


def _read_jsonl_items(jsonl_path: Path) -> list[str]:
    """Read JSONL brain file and return content strings (PII-redacted)."""
    entries = read_jsonl_entries(jsonl_path)
    items = []
    for entry in entries:
        content = entry.get("content", "")
        if content:
            items.append(redact_pii(content[:500]))
    return items


def _paginate(items: list, offset: int = 0, limit: int = 200) -> tuple[list, int]:
    """Return a page of items and total count."""
    total = len(items)
    return items[offset:offset + limit], total


@router.get("/lessons")
async def get_lessons(offset: int = 0, limit: int = 200):
    """Return brain lessons (paginated)."""
    all_items = _read_jsonl_items(LESSONS_JSONL)
    items, total = _paginate(all_items, offset, limit)
    return {"count": total, "offset": offset, "limit": limit, "lessons": items}


@router.get("/failures")
async def get_failures(offset: int = 0, limit: int = 200):
    """Return brain failures (paginated)."""
    all_items = _read_jsonl_items(FAILS_JSONL)
    items, total = _paginate(all_items, offset, limit)
    return {"count": total, "offset": offset, "limit": limit, "failures": items}


@router.get("/patterns")
async def get_patterns(offset: int = 0, limit: int = 200):
    """Return brain patterns (paginated)."""
    all_items = _read_jsonl_items(PATTERNS_JSONL)
    items, total = _paginate(all_items, offset, limit)
    return {"count": total, "offset": offset, "limit": limit, "patterns": items}


@router.get("/graph")
async def get_graph():
    """Return knowledge graph summary."""
    graph_file = HIPPOCAMPUS / "knowledge_graph.json"
    if not graph_file.exists():
        return {"nodes": 0, "edges": 0, "data": {}}
    import json
    data = json.loads(graph_file.read_text(encoding="utf-8"))
    return {
        "nodes": len(data.get("nodes", [])),
        "edges": len(data.get("edges", [])),
        "data": data,
    }


@router.get("/search")
async def search_brain(q: str = ""):
    """Search brain for a query string."""
    q = _sanitize_query(q)
    if not q or len(q) < 2:
        raise HTTPException(status_code=400, detail="Query must be at least 2 characters.")
    if len(q) > 1_000:
        raise HTTPException(status_code=400, detail="Query too long (max 1000 chars).")
    try:
        import subprocess
        # Escape regex metacharacters to prevent ReDoS
        safe_q = re.escape(q)
        result = subprocess.run(
            [sys.executable, str(TOOLS_DIR / "brain_search.py"), safe_q, "--json", "--exclude-private"],
            capture_output=True, timeout=15, cwd=str(ROOT_DIR),
        )
        stdout = result.stdout.decode("utf-8", errors="replace") if isinstance(result.stdout, bytes) else result.stdout
        if result.returncode == 0 and stdout.strip():
            import json
            # stdout has banner text before JSON — extract the JSON object
            json_start = stdout.find("{")
            if json_start >= 0:
                return json.loads(stdout[json_start:])
        # Don't leak raw subprocess output
        print(f"[API] Search returned no JSON (rc={result.returncode})")
        return {"query": q, "results": [], "total_results": 0, "elapsed_ms": 0}
    except Exception as e:
        print(f"[API] Search error: {e}")
        return {"query": q, "results": [], "total_results": 0, "elapsed_ms": 0}


def _read_json_list(filepath: Path, key: str | None = None) -> list:
    """Read a JSON file and return its list contents.
    If key is provided and data is a dict, extract data[key]."""
    if not filepath.exists():
        return []
    import json
    try:
        data = json.loads(filepath.read_text(encoding="utf-8"))
        if isinstance(data, list):
            return data
        if isinstance(data, dict) and key:
            items = data.get(key, [])
            return items if isinstance(items, list) else []
        return []
    except Exception:
        return []


@router.get("/schemas")
async def get_schemas():
    """Return domain schemas extracted by the schema daemon."""
    items = _read_json_list(HIPPOCAMPUS / "Patterns" / "schemas.json", key="schemas")
    return {"count": len(items), "schemas": items}


@router.get("/procedures")
async def get_procedures():
    """Return procedures extracted by the procedure daemon."""
    items = _read_json_list(HIPPOCAMPUS / "Evidence" / "Metrics" / "procedural_log.json", key="procedures")
    return {"count": len(items), "procedures": items}


@router.get("/wants")
async def get_wants():
    """Return wants from intents tracking."""
    items = _read_json_list(HIPPOCAMPUS / "Intents" / "wants.json")
    return {"count": len(items), "wants": items}


@router.get("/dont_wants")
async def get_dont_wants():
    """Return dont_wants from intents tracking."""
    items = _read_json_list(HIPPOCAMPUS / "Intents" / "dont_wants.json")
    return {"count": len(items), "dont_wants": items}


@router.get("/strategic_intents")
async def get_strategic_intents():
    """Return strategic intents."""
    items = _read_json_list(HIPPOCAMPUS / "Intents" / "strategic_intents.json", key="intents")
    return {"count": len(items), "strategic_intents": items}


@router.get("/anti_patterns")
async def get_anti_patterns():
    """Return active anti-patterns."""
    items = _read_json_list(HIPPOCAMPUS / "Anti_Patterns" / "anti_patterns_active.json")
    return {"count": len(items), "anti_patterns": items}
