#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""
Worker Memory Fast-Path — Brain V4 Phase 3 + V5 Immune System

Handles EXTRACT_KNOWLEDGE, CLEAN_INTENTS, and CHECK_ANTIPATTERN tasks
directly, bypassing the normal LLM conversation loop. Workers call
llm_router.dispatch() with the payload and write results to brain
via the locked save_writers.

This is dramatically faster and cheaper than routing memory
tasks through the standard tool loop.
"""
from __future__ import annotations

import json
import logging
from datetime import datetime, timezone
from pulse.brain.extraction.wants_classify import _classify_domain

from pulse.core.brain_utils import TASK_BOARD_FILE, HIPPOCAMPUS_DIR, now_iso as _now_iso
from pulse.tasks.task_ops import finalize_task as _finalize_task, update_registry


def is_memory_task(task: dict) -> bool:
    """Check if a task is a memory/immune task that should use the fast path."""
    payload = task.get("memory_data", {})
    return payload.get("type") in ("EXTRACT_KNOWLEDGE", "CLEAN_INTENTS", "CHECK_ANTIPATTERN", "SLEEP_CYCLE")


def execute_memory_task(task: dict, worker_id: str, log: logging.Logger) -> None:
    """Execute a memory task via the fast path (no LLM conversation loop)."""
    task_id = task.get("id") or task.get("task_id", "unknown")
    payload = task.get("memory_data", {})
    task_type = payload.get("type", "")

    update_registry(worker_id, status="working", task_id=task_id)
    log.info("MEMORY FAST-PATH: [%s] %s", task_id, task.get("title", "")[:80])

    try:
        if task_type == "EXTRACT_KNOWLEDGE":
            outcome = _execute_extraction(payload, worker_id, log)
        elif task_type == "CLEAN_INTENTS":
            outcome = _execute_intent_cleaning(payload, worker_id, log)
        elif task_type == "CHECK_ANTIPATTERN":
            outcome = _execute_antipattern_check(payload, worker_id, log)
        elif task_type == "SLEEP_CYCLE":
            outcome = _execute_sleep_cycle(payload, worker_id, log)
        else:
            outcome = f"Unknown memory task type: {task_type}"
            _finalize_task(TASK_BOARD_FILE, task_id, worker_id,
                           outcome=outcome, status="done")
            update_registry(worker_id, status="idle", domain="brain")
            return

        _finalize_task(TASK_BOARD_FILE, task_id, worker_id,
                       outcome=outcome, status="done",
                       tokens_in=0, tokens_out=0, cost=0.0)
        log.info("MEMORY DONE: [%s] %s", task_id, outcome[:100])

    except Exception as e:
        outcome = f"Error: {e}"
        log.error("MEMORY ERROR: [%s] %s", task_id, e, exc_info=True)
        _finalize_task(TASK_BOARD_FILE, task_id, worker_id,
                       outcome=outcome, status="open")

    update_registry(worker_id, status="idle", domain="brain")


def _execute_extraction(payload: dict, worker_id: str, log: logging.Logger) -> str:
    """Extract knowledge from a batch of chat interactions via llm_router."""
    from pulse.core.llm_router import dispatch, extract_json
    from pulse.admin.retroactive.retroactive_llm_extractor import SCRIBE_PROMPT, write_to_brain

    batch_text = payload.get("batch_text", "")
    agent_name = payload.get("agent_name", "unknown")
    timestamp = payload.get("timestamp", _now_iso())
    batch_num = payload.get("batch_num", 0)

    if not batch_text:
        return "Empty batch — skipped"

    prompt = SCRIBE_PROMPT + batch_text
    # Adaptive temperature: confidence from task payload (P4-A)
    item_confidence = float(payload.get("confidence", 0.5))
    raw = dispatch(prompt, task_type="fast", confidence=item_confidence)
    items = extract_json(raw)

    if not isinstance(items, dict):
        return f"LLM returned non-dict: {type(items).__name__}"

    written = write_to_brain(items, source_agent=agent_name, timestamp=timestamp)
    total = sum(written.values())

    return (
        f"Batch {batch_num}: {written['lessons']}L/{written['failures']}F/"
        f"{written['patterns']}P/{written.get('facts', 0)}Facts ({total} items)"
    )


def _execute_intent_cleaning(payload: dict, worker_id: str, log: logging.Logger) -> str:
    """Clean a batch of noisy intents via llm_router."""
    from pulse.core.llm_router import dispatch, extract_json
    from pulse.brain.save_writers import append_to_json_list
    from pathlib import Path

    batch = payload.get("batch", [])
    intent_type = payload.get("intent_type", "want")
    file_path = Path(payload.get("file", ""))
    batch_num = payload.get("batch_num", 0)

    if not batch:
        return "Empty batch — skipped"

    _purifier_base = (
        "You are the PULSE Intent Purifier. Distill raw {kind} into ONLY actionable, unique {goal}.\n"
        "REMOVE: duplicates (keep best-worded), fragments <15 chars, code/path fragments, "
        "task-specific requests, contradictions.\n"
        "KEEP: architectural preferences, system behavior rules, tool/workflow prefs, quality standards.\n"
        "OUTPUT: Strict JSON array of cleaned strings. No duplicates. Each a complete, actionable sentence.\n"
        "If NONE are valid, return: []\n\nRaw {kind}:\n"
    )
    _wants_tmpl = _purifier_base.format(kind="wants (user preferences)", goal="preferences")
    _donts_tmpl = _purifier_base.format(kind="dont_wants (prohibitions)", goal="prohibitions")
    prompt_template = _wants_tmpl if intent_type == "want" else _donts_tmpl
    prompt = prompt_template + json.dumps(batch, indent=None)

    raw = dispatch(prompt, task_type="fast", confidence=0.6)  # intent cleaning: moderate focus
    parsed = extract_json(raw)

    if isinstance(parsed, list):
        clean_items = parsed
    elif isinstance(parsed, dict):
        clean_items = parsed.get("wants", parsed.get("dont_wants", []))
        if not isinstance(clean_items, list):
            clean_items = []
    else:
        clean_items = []

    # Filter: must be strings >= 15 chars
    clean_items = [s.strip() for s in clean_items if isinstance(s, str) and len(s.strip()) >= 15]

    # Write via locked append_to_json_list
    written = 0
    key = "want" if intent_type == "want" else "dont_want"
    ts = datetime.now(timezone.utc).isoformat()

    for item_text in clean_items:
        entry = {
            key: item_text,
            "domain": _classify_domain(item_text),
            "visibility": "public",
            "timestamp": ts,
            "source": "swarm_cleaned",
            "session_id": f"memory_worker_{worker_id}",
        }
        if intent_type == "dont_want":
            entry["reason"] = "system_rule"
            entry["evidence_count"] = 1
            entry["status"] = "active"
        else:
            entry["priority"] = 7

        if append_to_json_list(file_path, entry, dedup_key=key):
            written += 1

    return f"Batch {batch_num}: {len(batch)} raw -> {len(clean_items)} clean -> {written} written"


def _execute_antipattern_check(payload: dict, worker_id: str, log: logging.Logger) -> str:
    """Check a file for anti-pattern violations via LLM (V5 Immune System)."""
    from pulse.core.llm_router import dispatch, extract_json
    from pulse.core.brain_io import atomic_update_json
    from pathlib import Path

    file_path = payload.get("file_path", "unknown")
    file_content = payload.get("file_content", "")
    anti_patterns = payload.get("anti_patterns", "")
    source = payload.get("source", "immune_system")

    if not file_content:
        return f"Empty file content for {file_path} — skipped"

    prompt = (
        "You are the PULSE Immune System. Check if this code violates any known anti-patterns.\n\n"
        f"ANTI-PATTERNS:\n{anti_patterns}\n\n"
        f"FILE ({file_path}):\n```\n{file_content[:12000]}\n```\n\n"
        "OUTPUT strict JSON only (no markdown fences):\n"
        '{"violations": [{"anti_pattern": "which pattern", "reason": "why", '
        '"severity": "low|medium|high", "line_hint": "approx line or function"}], "clean": true|false}\n'
        'If no violations: {"violations": [], "clean": true}'
    )

    raw = dispatch(prompt, task_type="fast", confidence=0.8)  # anti-pattern check: high focus
    result = extract_json(raw)

    violations = result.get("violations", [])
    is_clean = result.get("clean", True)

    if not violations or is_clean:
        return f"CLEAN: {file_path} — no violations detected"

    # Write violations to prediction_alerts.json for visibility
    STATE_DIR = Path(__file__).resolve().parents[3] / "state"
    alerts_file = STATE_DIR / "prediction_alerts.json"

    for v in violations:
        if not isinstance(v, dict):
            continue
        alert = {
            "type": "ANTIPATTERN_VIOLATION",
            "file": file_path,
            "anti_pattern": v.get("anti_pattern", "unknown"),
            "reason": v.get("reason", ""),
            "severity": v.get("severity", "medium"),
            "line_hint": v.get("line_hint", ""),
            "source": source,
            "detected_by": worker_id,
            "timestamp": _now_iso(),
            "status": "active",
        }
        try:
            def _append_alert(data):
                if not isinstance(data, list):
                    data = []
                data.append(alert)
                # Keep last 100 alerts
                return data[-100:]
            atomic_update_json(alerts_file, _append_alert, default=[])
        except Exception as e:
            log.warning("Failed to write immune alert: %s", e)

    violation_summary = "; ".join(
        f"{v.get('anti_pattern', '?')} ({v.get('severity', '?')})"
        for v in violations if isinstance(v, dict)
    )
    return f"VIOLATION in {file_path}: {violation_summary}"


def _execute_sleep_cycle(payload: dict, worker_id: str, log: logging.Logger) -> str:
    """Execute the Semantic Sleep Cycle (V5 Phase 3 — Premium tier)."""
    from pulse.daemons.synthesis.sleep.sleep_cycle import run_sleep_cycle

    log.info("SLEEP CYCLE: Starting nightly brain consolidation...")
    result = run_sleep_cycle(dry_run=False, verbose=True)

    status = result.get("status", "unknown")
    if status == "completed":
        orig = result.get("original_items", 0)
        cons = result.get("consolidated_items", 0)
        pct = result.get("reduction_pct", 0)
        return f"Sleep Cycle complete: {orig} -> {cons} items ({pct}% reduction)"
    elif status == "skipped":
        return f"Sleep Cycle skipped: {result.get('reason', 'unknown')}"
    elif status == "refused":
        return f"Sleep Cycle refused: {result.get('reason', 'too aggressive')}"
    else:
        return f"Sleep Cycle {status}: {result.get('reason', 'unknown')}"
