#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Retroactive Intents Cleaner — Brain V3. Deduplicates/distills noisy intents via LLM."""
from __future__ import annotations

import io, json, os, shutil, sys, time
from datetime import datetime, timezone
from pathlib import Path

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

if str(ROOT_DIR) not in sys.path:
    sys.path.insert(0, str(ROOT_DIR))

if os.name == "nt" and __name__ == "__main__":
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace", line_buffering=True)
    sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace", line_buffering=True)

from pulse.core.llm_router import dispatch, extract_json

INTENTS_DIR = ROOT_DIR / "Hippocampus" / "Evidence" / "Intents"
SCHEMAS_FILE = ROOT_DIR / "Hippocampus" / "Patterns" / "schemas.json"
WANTS_FILE = INTENTS_DIR / "wants.json"
DONT_WANTS_FILE = INTENTS_DIR / "dont_wants.json"
BATCH_SIZE = 80

_INTENT_PROMPT = """You are the PULSE Intent Purifier. Distill raw {kind} into ONLY actionable, unique {goal}.
REMOVE: duplicates (keep best-worded), fragments <15 chars, code/path fragments, task-specific requests, contradictions.
KEEP: architectural preferences, system behavior rules, tool/workflow prefs, quality standards.
OUTPUT: Strict JSON array of cleaned strings. No duplicates. Each a complete, actionable sentence.
If NONE are valid, return: []

Raw {kind}:
"""

_SCHEMAS_PROMPT = """You are the PULSE Schema Purifier. Keep ONLY schemas representing genuine procedural patterns. Remove "agent ran successfully" event sequences.
OUTPUT: JSON object {{"version": "1.0", "description": "Proven patterns", "schemas": [...]}}. If none useful, return empty schemas array.

Schemas:
"""


def _load_json(path: Path) -> list | dict:
    if not path.exists():
        return []
    return json.loads(path.read_text(encoding="utf-8"))


def _batch(entries: list, size: int = BATCH_SIZE):
    for i in range(0, len(entries), size):
        yield entries[i : i + size]


def clean_intents(entries: list, key: str, kind: str, label: str, dry_run: bool = False) -> list[str]:
    """Send entries through LLM in batches, collect cleaned results."""
    if not entries:
        return []
    texts = [e.get(key, "") for e in entries if isinstance(e, dict) and e.get(key)]
    print(f"  [{label}] {len(texts)} raw -> batching ({BATCH_SIZE}/batch)...")
    prompt_template = _INTENT_PROMPT.format(
        kind=kind, goal="preferences" if "want" in kind else "prohibitions"
    )
    all_clean: list[str] = []
    total = (len(texts) + BATCH_SIZE - 1) // BATCH_SIZE
    for i, batch in enumerate(_batch(texts), 1):
        batch_text = json.dumps(batch, indent=None)
        if dry_run:
            print(f"    Batch {i}/{total}: {len(batch)} entries (~{len(batch_text)//4} tokens)")
            continue
        try:
            raw = dispatch(prompt_template + batch_text, task_type="fast")
            parsed = extract_json(raw)
            if isinstance(parsed, list):
                items = parsed
            elif isinstance(parsed, dict):
                items = parsed.get("wants", parsed.get("dont_wants", []))
                if not isinstance(items, list):
                    items = []
            else:
                items = []
            items = [s.strip() for s in items if isinstance(s, str) and len(s.strip()) >= 15]
            all_clean.extend(items)
            print(f"    [{label}] {i}/{total}: {len(batch)} -> {len(items)} clean")
        except Exception as e:
            print(f"    [{label}] Batch {i}/{total} ERROR: {e}")
    seen, deduped = set(), []
    for item in all_clean:
        k = item.lower().strip()
        if k not in seen:
            seen.add(k)
            deduped.append(item)
    print(f"  [{label}] Result: {len(texts)} -> {len(all_clean)} -> {len(deduped)} deduped")
    return deduped


def _rebuild_json(entries: list[str], key: str) -> list[dict]:
    """Rebuild intent JSON with clean entries in proper schema."""
    ts = datetime.now(timezone.utc).isoformat()
    base = {"priority": 7, "domain": "general", "visibility": "public",
            "timestamp": ts, "source": "llm_cleaned", "session_id": "intents_cleaner"}
    if key == "dont_want":
        base.update(reason="system_rule", evidence_count=1, status="active")
    return [{key: e, **base} for e in entries]


def _clean_schemas(dry_run: bool = False) -> dict | None:
    if not SCHEMAS_FILE.exists():
        return None
    data = _load_json(SCHEMAS_FILE)
    if not isinstance(data, dict):
        return None
    schemas = data.get("schemas", [])
    print(f"  [schemas] {len(schemas)} raw schemas")
    if dry_run:
        return None
    try:
        raw = dispatch(_SCHEMAS_PROMPT + json.dumps(data, indent=2), task_type="fast")
        result = extract_json(raw)
        if isinstance(result, dict) and "schemas" in result:
            print(f"  [schemas] {len(schemas)} -> {len(result['schemas'])} clean")
            return result
    except Exception as e:
        print(f"  [schemas] ERROR: {e}")
    return None


def main():
    import argparse
    parser = argparse.ArgumentParser(description="Brain V3 Retroactive Intents Cleaner")
    parser.add_argument("--dry-run", action="store_true")
    args = parser.parse_args()

    print("=" * 60)
    print("BRAIN V3 — Retroactive Intents Cleaner")
    print("=" * 60)
    if args.dry_run:
        print("MODE: DRY RUN\n")

    if not args.dry_run:
        ts = datetime.now().strftime("%Y%m%d_%H%M%S")
        for src in [WANTS_FILE, DONT_WANTS_FILE, SCHEMAS_FILE]:
            if src.exists():
                shutil.copy2(src, src.with_suffix(f".pre_clean_{ts}.json"))

    print("[1/3] Cleaning wants.json...")
    wants_data = _load_json(WANTS_FILE)
    clean_wants = clean_intents(
        wants_data if isinstance(wants_data, list) else [],
        key="want", kind="wants (user preferences)", label="wants", dry_run=args.dry_run)

    print("\n[2/3] Cleaning dont_wants.json...")
    dw_data = _load_json(DONT_WANTS_FILE)
    clean_dw = clean_intents(
        dw_data if isinstance(dw_data, list) else [],
        key="dont_want", kind="dont_wants (prohibitions)", label="dont_wants", dry_run=args.dry_run)

    print("\n[3/3] Cleaning schemas.json...")
    clean_schemas_data = _clean_schemas(dry_run=args.dry_run)

    if not args.dry_run:
        if clean_wants:
            WANTS_FILE.write_text(json.dumps(_rebuild_json(clean_wants, "want"), indent=2, ensure_ascii=False), encoding="utf-8")
            print(f"  wants.json: {len(clean_wants)} entries")
        if clean_dw:
            DONT_WANTS_FILE.write_text(json.dumps(_rebuild_json(clean_dw, "dont_want"), indent=2, ensure_ascii=False), encoding="utf-8")
            print(f"  dont_wants.json: {len(clean_dw)} entries")
        if clean_schemas_data:
            clean_schemas_data["last_updated"] = datetime.now(timezone.utc).isoformat()
            SCHEMAS_FILE.write_text(json.dumps(clean_schemas_data, indent=2, ensure_ascii=False), encoding="utf-8")

    w_count = len(wants_data) if isinstance(wants_data, list) else 0
    d_count = len(dw_data) if isinstance(dw_data, list) else 0
    print(f"\n{'=' * 60}")
    print(f"SUMMARY: wants {w_count}->{len(clean_wants)}, dont_wants {d_count}->{len(clean_dw)}")
    print(f"{'=' * 60}")


if __name__ == "__main__":
    main()
