#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Reflex LLM -- Public API (thin shell).

Entry point for callers.  Heavy logic lives in:
  reflex_llm_queue.py  -- _HourlyRateLimiter, enqueue_for_llm, _ensure_worker_started
  reflex_llm_worker.py -- _worker_loop, _process_item

Public surface:
  enqueue_for_llm()     -- non-blocking; called by pulse_bridge
  analyze_now()         -- synchronous; manual/backfill use
  backfill_from_logs()  -- process existing capture logs
"""
from __future__ import annotations

import logging
import time

# Re-export for callers that import from this module directly
from pulse.daemons.bridge.reflex_llm_queue import (
    _MIN_INTERACTION_CHARS,
    _TEXT_TRUNCATE,
    enqueue_for_llm,  # noqa: F401  -- re-exported
)
from pulse.daemons.bridge.reflex_llm_worker import _process_item

logger = logging.getLogger("pulse.reflex_llm")


# ---------------------------------------------------------------------------
# Synchronous API (for manual use / backfill)
# ---------------------------------------------------------------------------

def analyze_now(user_text: str, agent_text: str, source: str = "MANUAL") -> int:
    """Synchronous LLM analysis -- bypasses queue and rate limiter.

    Returns count of entries written to reflex_patterns.json.
    Use for manual testing or backfill scripts.
    """
    from pulse.daemons.bridge.reflex_arc import detect_mistakes

    combined = f"{user_text}\n{agent_text}" if agent_text else user_text
    regex_hits = detect_mistakes(combined)

    item = {
        "user_text": (user_text or "")[:_TEXT_TRUNCATE],
        "agent_text": (agent_text or "")[:_TEXT_TRUNCATE],
        "source": source,
        "regex_hits": regex_hits,
        "enqueued_at": time.time(),
    }
    return _process_item(item) or 0


def backfill_from_logs(log_dir: str = None, limit: int = 0, verbose: bool = True):
    """Process existing capture logs through LLM reflex analysis.

    Pairs USER+AGENT messages, feeds each through analyze_now().
    Skips trivial interactions (<200 chars combined).

    Args:
        log_dir: Directory with pulse_captured_*.log files. Default: state/
        limit: Max interactions to process (0 = all).
        verbose: Print progress to stdout.
    """
    import re
    from pathlib import Path
    from pulse.core.brain_utils import STATE_DIR

    state_dir = Path(log_dir) if log_dir else STATE_DIR

    _LOG_LINE = re.compile(
        r"^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]\s+"
        r"(USER|CLAUDE|GEMINI|CODEX|GROK):\s+"
        r"(.+?)(?:\s*\|\s*SIG:\s*\S+)?\s*$"
    )

    log_files = sorted(state_dir.glob("pulse_captured_*.log"))
    if not log_files:
        if verbose:
            print(f"No capture logs found in {state_dir}")
        return

    total_processed = 0
    total_written = 0

    for log_file in log_files:
        # Derive source from filename: pulse_captured_claude.log -> CLAUDE
        stem = log_file.stem  # pulse_captured_claude
        source = stem.replace("pulse_captured_", "").upper()
        if not source or len(source) > 30:
            source = "UNKNOWN"

        if verbose:
            print(f"\n[BACKFILL] Processing {log_file.name} (source: {source})")

        # Parse into USER/AGENT pairs
        current_user = None
        agent_lines = []

        with open(log_file, "r", encoding="utf-8", errors="replace") as f:
            for line in f:
                m = _LOG_LINE.match(line)
                if not m:
                    # Continuation line
                    if agent_lines:
                        agent_lines.append(line.rstrip())
                    continue

                _ts, role, text = m.group(1), m.group(2), m.group(3)

                if role == "USER":
                    # Flush previous pair if we have one
                    if current_user and agent_lines:
                        agent_text = " ".join(agent_lines)
                        combined_len = len(current_user) + len(agent_text)
                        if combined_len >= _MIN_INTERACTION_CHARS:
                            written = analyze_now(current_user, agent_text, source)
                            total_written += written
                            total_processed += 1
                            if verbose and written:
                                print(f"  [{total_processed}] +{written} entries")
                            if limit and total_processed >= limit:
                                break
                    current_user = text
                    agent_lines = []
                else:
                    agent_lines.append(text)

            # Flush last pair
            if current_user and agent_lines and (not limit or total_processed < limit):
                agent_text = " ".join(agent_lines)
                if len(current_user) + len(agent_text) >= _MIN_INTERACTION_CHARS:
                    written = analyze_now(current_user, agent_text, source)
                    total_written += written
                    total_processed += 1
                    if verbose and written:
                        print(f"  [{total_processed}] +{written} entries")

        if limit and total_processed >= limit:
            break

    if verbose:
        print(f"\n[BACKFILL] Done: {total_processed} interactions processed, "
              f"{total_written} entries written to reflex_patterns.json")


# ---------------------------------------------------------------------------
# CLI entry point
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    import sys
    import os
    from pathlib import Path

    # Ensure pulse package is importable when run directly
    ROOT_DIR = Path(__file__).resolve().parent.parent.parent.parent
    if str(ROOT_DIR) not in sys.path:
        sys.path.insert(0, str(ROOT_DIR))

    if os.name == "nt":
        import io
        if hasattr(sys.stdout, 'reconfigure'):
            sys.stdout.reconfigure(encoding='utf-8', errors='replace')
        if hasattr(sys.stderr, 'reconfigure'):
            sys.stderr.reconfigure(encoding='utf-8', errors='replace')

    logging.basicConfig(level=logging.INFO, format="%(message)s")

    args = sys.argv[1:]
    if "--backfill" in args:
        limit_val = 0
        for i, a in enumerate(args):
            if a == "--limit" and i + 1 < len(args):
                limit_val = int(args[i + 1])
        print("[REFLEX LLM] Starting backfill from capture logs...")
        backfill_from_logs(limit=limit_val)
    elif "--test" in args:
        user = "The bridge keeps crashing when I run it with multiple workers"
        agent = (
            "I found the issue. The filelock timeout was set to 10 seconds "
            "which is too low for 24 concurrent agents. When multiple workers "
            "try to write to the same JSON file simultaneously, they all "
            "timeout and crash. The fix is to increase the timeout to 60 "
            "seconds in brain_io.py."
        )
        print("[REFLEX LLM] Running test analysis...")
        n = analyze_now(user, agent, "TEST")
        print(f"[REFLEX LLM] Wrote {n} entries")
    else:
        print("Usage:")
        print("  python reflex_llm.py --backfill [--limit N]  Process capture logs")
        print("  python reflex_llm.py --test                  Run a test extraction")
