import logging
#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""
Neural Render: State rendering and summarization for ACTIVE_NEURAL_STATE.md.

Extracted from pulse_neural.py per Law 7 (files <= 300 lines).

Functions:
  - _compress_learnings: Weight and select top learning summaries
  - _summarize_strategy: Load top strategic intents
  - _summarize_tasks: Read task board summary
  - _render_state: Build full neural state markdown content

Dependencies: Python stdlib + brain_utils (internal)
"""
import math
import sys
from datetime import datetime, timezone
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_dict, TASK_BOARD_FILE, HIPPOCAMPUS_DIR, now_iso as _now_iso

LEARNING_DIRS = [
    ROOT_DIR / "Hippocampus" / "Lessons",
    ROOT_DIR / "Hippocampus" / "Patterns",
    ROOT_DIR / "Hippocampus" / "Failures",
    ROOT_DIR / "Hippocampus" / "Patterns",
]

STRATEGIC_INTENTS_FILE = HIPPOCAMPUS_DIR / "Evidence" / "Intents" / "strategic_intents.json"


def _truncate(s: str, n: int = 140) -> str:
    s = " ".join(s.split())
    return s if len(s) <= n else s[: n - 3] + "..."


def _strip_front_matter(text: str) -> str:
    lines = text.splitlines()
    if lines and lines[0].strip() == "---":
        for idx in range(1, len(lines)):
            if lines[idx].strip() == "---":
                return "\n".join(lines[idx + 1 :])
        return ""
    return text


def _extract_sections(text: str) -> list[dict]:
    body = _strip_front_matter(text)
    sections = []
    current = None
    for line in body.splitlines():
        if line.startswith("## "):
            current = {"title": line[3:].strip(), "lines": []}
            sections.append(current)
            continue
        if current is not None:
            current["lines"].append(line)
    return sections


def _summarize_section(section: dict) -> str:
    for line in section["lines"]:
        stripped = line.strip()
        if stripped:
            return stripped
    return ""


def _learning_paths():
    for base in LEARNING_DIRS:
        if not base.exists():
            continue
        for path in sorted(base.glob("*.md")):
            if path.name.startswith("_"):
                continue
            if not path.is_file():
                continue
            yield path


def _parse_front_matter(text: str) -> dict:
    data = {}
    lines = text.splitlines()
    if not lines or lines[0].strip() != "---":
        return data
    for line in lines[1:]:
        if line.strip() == "---":
            break
        if ":" in line:
            key, value = line.split(":", 1)
            data[key.strip()] = value.strip()
    return data


def _weight_from_frontmatter(meta: dict) -> float:
    weight = 0.5
    conf = meta.get("confidence")
    if conf is not None:
        try:
            weight = max(weight, float(conf))
        except ValueError:
            logging.debug("Suppressed exception in neural_render", exc_info=True)
    evidence = meta.get("evidence_count")
    if evidence is not None:
        try:
            weight += min(1.0, float(evidence) / 10.0)
        except ValueError:
            logging.debug("Suppressed exception in neural_render", exc_info=True)
    return min(weight, 1.5)


def _compress_learnings() -> list[str]:
    entries = []
    for path in _learning_paths():
        try:
            text = path.read_text(encoding="utf-8", errors="replace")
        except Exception:
            continue
        meta = _parse_front_matter(text)
        sections = _extract_sections(text)
        for section in sections:
            summary = _summarize_section(section) or section.get("title", "")
            if not summary:
                continue
            label = f"{path.parent.name}/{path.stem}: {summary}"
            entries.append({"label": label, "weight": _weight_from_frontmatter(meta)})
    if not entries:
        return []
    entries.sort(key=lambda e: e["weight"], reverse=True)
    limit = max(1, math.ceil(len(entries) * 0.2))
    selected = entries[:limit]
    return [_truncate(item["label"]) for item in selected]


def _summarize_strategy() -> list[str]:
    """Load top strategic intents for neural state."""
    data = load_json_dict(STRATEGIC_INTENTS_FILE)
    intents = data.get("intents", [])
    if not intents:
        return []
    lines = []
    for intent in intents[:5]:
        status = intent.get("status", "")
        text = intent.get("text", intent.get("title", "?"))
        lines.append(f"[{status}] {text[:100]}")
    return lines


def _summarize_tasks() -> list[str]:
    """Read task board and return summary lines for neural state."""
    board = load_json_dict(TASK_BOARD_FILE)
    dispatches = board.get("dispatches", [])
    if not dispatches:
        return []

    lines = []
    for dispatch in dispatches:
        for t in dispatch.get("tasks", []):
            status = t.get("status", "?")
            claimed = t.get("claimed_by") or ""
            suffix = f" ({claimed})" if claimed else ""
            lines.append(f"[{status}] {t.get('title', '?')}{suffix}")
    return lines


def _render_state(query: str, rec: dict, donts: list, learning_summaries: list[str]) -> str:
    updated = _now_iso()

    goals = []
    if query:
        goals.append(_truncate(query))
    while len(goals) < 3:
        goals.append("EMPTY")

    risks = []
    for w in rec.get("warnings", [])[:3]:
        title = w.get("title") or w.get("text") or "risk"
        risks.append(_truncate(title))
    while len(risks) < 3:
        risks.append("EMPTY")

    actions = []
    approach = rec.get("approach") or {}
    if approach.get("summary"):
        actions.append(_truncate(approach["summary"]))
    while len(actions) < 3:
        actions.append("EMPTY")

    dont_do = []
    for d in donts:
        dont_do.append(_truncate(d))
    while len(dont_do) < 3:
        dont_do.append("EMPTY")

    confidence = rec.get("confidence", 0.1)
    conf_line = f"{confidence} | based on wins/fails/evidence"
    state_line = "constraints: none"

    lines = []
    lines.append("# ACTIVE_NEURAL_STATE")
    lines.append(f"Updated: {updated}")
    lines.append(f"Query: {query or 'EMPTY'}")
    lines.append("")
    lines.append("TOP_GOALS:")
    lines.extend([f"- {g}" for g in goals])
    lines.append("")
    lines.append("RISKS:")
    lines.extend([f"- {r}" for r in risks])
    lines.append("")
    lines.append("NEXT_ACTIONS:")
    lines.extend([f"- {a}" for a in actions])
    lines.append("")
    lines.append("DONT_DO:")
    lines.extend([f"- {d}" for d in dont_do])
    lines.append("")
    lines.append(f"CONFIDENCE: {conf_line}")
    lines.append(f"STATE: {state_line}")
    lines.append("")
    lines.append("COMPRESSED_LEARNINGS:")
    for learning in learning_summaries:
        lines.append(f"- {learning}")
    if not learning_summaries:
        lines.append("- none")
    lines.append("")
    # Strategic Intents (from Plans Digestion)
    strategy_lines = _summarize_strategy()
    lines.append("STRATEGIC_INTENTS:")
    if strategy_lines:
        for sl in strategy_lines:
            lines.append(f"- {sl}")
    else:
        lines.append("- none (run synthesize_plans.py)")
    lines.append("")

    # ASOP Task Board
    task_lines = _summarize_tasks()
    lines.append("CURRENT_TASKS:")
    if task_lines:
        for tl in task_lines:
            lines.append(f"- {tl}")
    else:
        lines.append("- none")
    lines.append("")

    lines.append("SOURCE_EVIDENCE:")
    lines.append("- Hippocampus/Evidence/Sessions/current_session.json")
    lines.append("- Hippocampus/Patterns/anti_patterns.md")
    lines.append("- Hippocampus/Failures/")
    lines.append("")

    return "\n".join(lines)