#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""High-level knowledge write APIs: learnings, failures, decisions, JSON lists."""
from pathlib import Path

from pulse.core.brain_io import _acquire
from pulse.core.pii_filter import redact_pii
from pulse.core.brain_utils import (
    atomic_update_json, HIPPOCAMPUS_DIR,
    LESSONS_FILE as _LESSONS_FILE, FAILS_FILE as _FAILS_FILE,
    DECISIONS_FILE as _DECISIONS_FILE,
    now_iso as _now_iso,
)
from pulse.brain.save_writers.quality import _passes_quality_gate
from pulse.brain.save_writers.jsonl_store import (
    _MD_TO_TYPE, build_jsonl_entry, append_knowledge_jsonl,
)

# Brain output files (canonical paths)
LESSONS_FILE = _LESSONS_FILE
FAILS_FILE = _FAILS_FILE
SESSIONS_FILE = HIPPOCAMPUS_DIR / "Evidence" / "Sessions" / "agent_sessions.json"
PRIVATE_LESSONS_FILE = HIPPOCAMPUS_DIR / "Private" / "private_lessons.jsonl"


def _write_entries_to_md(filepath: Path, header_title: str, header_desc: str,
                         entries: list, agent_name: str) -> int:
    """Write entries to JSONL brain via append_knowledge_md.

    Legacy name kept for backward compat — no longer writes MD.
    Shared by write_learnings, write_failures, and write_private_learnings.
    """
    if not entries:
        return 0
    from pulse.core.brain_analysis import compute_item_salience, compute_item_confidence
    count = 0
    for item in entries:
        text = (item or "").strip()
        if not text or len(text) < 25:
            continue
        sal = compute_item_salience(text, priority=1.0)
        conf = compute_item_confidence(text, source_type="scribe")
        try:
            from pulse.brain.extraction.wants_parser import compute_emotional_intensity
            em = compute_emotional_intensity(text)
        except Exception:
            em = 0.0
        append_knowledge_md(
            filepath, text, agent_name, _now_iso(), "general",
            confidence=conf, salience=sal,
            provenance={"source_agent": agent_name, "emotional_intensity": em},
        )
        count += 1
    return count


def write_learnings(learnings: list, agent_name: str) -> int:
    """Write learnings to the lessons file. Returns count written."""
    return _write_entries_to_md(
        LESSONS_FILE, "Auto-Extracted Lessons",
        "Automatically saved by agents via pulse_save.py.",
        learnings, agent_name,
    )


def write_failures(failures: list, agent_name: str) -> int:
    """Write failures to the fails file. Returns count written."""
    return _write_entries_to_md(
        FAILS_FILE, "Auto-Extracted Failures",
        "Automatically saved by agents via pulse_save.py.",
        failures, agent_name,
    )


def write_private_learnings(learnings: list, agent_name: str) -> int:
    """Write learnings to private brain. Returns count written."""
    return _write_entries_to_md(
        PRIVATE_LESSONS_FILE, "Private Lessons",
        "Private learnings \u2014 not shared publicly.",
        learnings, agent_name,
    )


def append_knowledge_md(filepath, description, source, timestamp, domain,
                        confidence, salience=1.0, dry_run=False, provenance=None):
    """Canonical writer: append a knowledge entry to the JSONL brain.

    Despite the legacy name (kept for backward compat), this now writes
    JSONL-only. MD writes were killed in Phase 4 — all readers are JSONL-first.
    The filepath param is used to determine knowledge_type via _MD_TO_TYPE.
    """
    if dry_run:
        return
    filepath = Path(filepath)

    if not _passes_quality_gate(description, salience=salience, confidence=confidence):
        return

    try:
        from pulse.admin.retroactive.retroactive_llm_extractor import _is_prompt_echo
        if _is_prompt_echo(description):
            return
    except ImportError:
        pass

    description = redact_pii(description)

    from pulse.core.brain_utils import BRAIN_JSONL_FILES
    knowledge_type = _MD_TO_TYPE.get(filepath.name, "lesson")
    jsonl_path = BRAIN_JSONL_FILES.get(knowledge_type)
    if jsonl_path:
        merged_prov = {"source_agent": source, "domain": domain}
        if provenance:
            merged_prov.update(provenance)
        jsonl_entry = build_jsonl_entry(
            description=description, source=source, timestamp=timestamp,
            domain=domain, confidence=confidence, salience=salience,
            knowledge_type=knowledge_type,
            provenance=merged_prov,
        )
        append_knowledge_jsonl(jsonl_path, jsonl_entry, dry_run=dry_run)


def append_decision(item, dry_run=False):
    """Canonical writer: append a decision to the decisions JSON file.

    Used by bridge_routing.py — single write path for all decision entries.
    atomic_update_json already uses filelock internally via brain_io._acquire().
    """
    if dry_run:
        return
    _DECISIONS_FILE.parent.mkdir(parents=True, exist_ok=True)

    def _updater(data):
        if not isinstance(data, dict):
            data = {"decisions": []}
        if "decisions" not in data:
            data["decisions"] = []
        from pulse.core.pulse_crypto import sign_item
        decision_entry = {
            "description": item.get("description", ""),
            "domain": item.get("domain", "general"),
            "confidence": item.get("confidence", 0.5),
            "timestamp": item.get("timestamp", ""),
            "source_agent": item.get("source_agent", "unknown"),
        }
        decision_entry = sign_item(decision_entry, item.get("source_agent", "unknown"))
        data["decisions"].append(decision_entry)
        return data

    # SQLite-first: skip atomic_update_json if SQLite insert succeeds
    sqlite_ok = False
    try:
        from pulse.core.brain_db import insert_knowledge
        insert_knowledge("decisions", {
            "content": item.get("choice", item.get("description", "")),
            "title": item.get("choice", item.get("description", ""))[:80],
            "domain": item.get("domain", "general"),
            "agent": item.get("source_agent", "unknown"),
            "confidence": float(item.get("confidence", 0.5)),
            "timestamp": item.get("timestamp", ""),
            "type": "decision",
        })
        sqlite_ok = True
    except Exception:
        pass

    if not sqlite_ok:
        atomic_update_json(_DECISIONS_FILE, _updater, default={})


# append_to_json_list moved to jsonl_store.py (Law 5 SoC)
