#!/usr/bin/env python3
"""PULSE CLI entry point — used by `python -m pulse` and `pip install` console_scripts."""

import sys
from pathlib import Path

# Ensure project root is on path (for dev mode)
ROOT_DIR = Path(__file__).resolve().parent.parent.parent
if str(ROOT_DIR) not in sys.path:
    sys.path.insert(0, str(ROOT_DIR))

# Load .env from project root (dev) or per-project dir (installed)
try:
    from dotenv import load_dotenv
    from pulse.core.paths import get_root_dir
    load_dotenv(get_root_dir() / ".env")
except ImportError:
    pass

from pulse.cli.status import cmd_status, cmd_watch, cmd_brain
from pulse.cli.cmd_costs import cmd_costs, cmd_history
from pulse.cli.cmd_manage_lifecycle import cmd_start, cmd_stop, cmd_clean, cmd_dashboard
from pulse.cli.cmd_manage_data import cmd_post, cmd_relay, cmd_logs, cmd_brain_export, cmd_brain_import
from pulse.cli.cmd_swarm import cmd_swarm
from pulse.cli.cmd_agent import cmd_agent, cmd_chat_picker, AGENT_NAMES
from pulse.cli.cmd_proposals import cmd_proposals
from pulse.cli.cmd_license import cmd_activate, cmd_license_info
from pulse.daemons.synthesis.dmn_daemon import cmd_dream
from pulse.daemons.synthesis.epistemic_forager import cmd_forage
from pulse.daemons.synthesis.skill_dependency_synthesizer import cmd_synthesize
from pulse.daemons.synthesis.ghost_resonance import cmd_ghost
from pulse.daemons.synthesis.hebbian_balancer import cmd_hebbian
from pulse.daemons.synthesis.homeostatic_scheduler import cmd_homeostatic
from pulse.daemons.synthesis.procedural_sleep_healer import cmd_healer
from pulse.brain.brain_query import run_query

USAGE = """\
PULSE — Neural Operating System for AI agent governance.

Usage:
  pulse                     Interactive agent picker (provider → model → chat)
  pulse status              Show running daemons, task board, and system health
  pulse watch               Auto-refreshing status dashboard (3s)
  pulse brain               Brain usage dashboard
  pulse costs               Show cost and token usage stats
  pulse history [--all]     Show timeline of completed tasks

  pulse swarm               Interactive worker launcher
  pulse swarm stop          Kill all workers
  pulse swarm status        Show running workers

  pulse claude|gemini|codex|grok|ollama   Launch agent (Interactive Chat)

  pulse start               Start the orchestrator (all daemons)
  pulse stop                Stop all PULSE processes
  pulse post "task"         Post a task to the board
  pulse relay ...           Pass-through to pulse_relay.py
  pulse clean               Remove old tasks and reset stale claims
  pulse logs [worker_id]    Tail worker logs
  pulse init                Initialize ~/.pulse/ for a new user
  pulse dashboard [port]    Start the web monitoring dashboard

  pulse dream                 Interactive dream (pick files, mode, count)
  pulse dream [N]             N dream sessions (5 models in parallel each)
  pulse dream --files 3 --mode chaos   Custom: 3 files, chaos mode
  pulse proposals             List DMN sessions with idea summaries
  pulse proposals view S      View all ideas in session S
  pulse proposals view S.I    View idea I in session S (full detail)
  pulse proposals promote S.I Move idea to task board for execution
  pulse proposals reject S.I  Reject an idea
  pulse proposals archive S   Move session to Archive/ (subconscious backlog)
  pulse proposals clear       Delete fully-rejected sessions

  pulse forage              Verify stale knowledge against the web
  pulse forage --dry-run    Audit + rank only (no web search)
  pulse forage status       Show foraging run stats

  pulse synthesize          Detect proven skill chains & propose composites
  pulse synthesize --dry-run  Analyze without LLM synthesis
  pulse synthesize status   Show synthesizer stats

  pulse ghost               Process dead worker ghosts into procedures
  pulse ghost --dry-run     Detect deaths without LLM synthesis
  pulse ghost status        Show ghost resonance stats

  pulse hebbian             Sweep task outcomes & update model weights
  pulse hebbian --dry-run   Sweep without updating weights
  pulse hebbian status      Show model-domain performance matrix

  pulse homeostatic           Monitor stress & adjust daemon intervals
  pulse homeostatic --dry-run Compute without applying overrides
  pulse homeostatic status    Show stress metrics + active overrides

  pulse healer              Pre-sleep evidence quality sweep
  pulse healer --dry-run    Analyze without modifying files
  pulse healer status       Show healer stats + promoted workflows

  pulse activate <KEY>       Activate a license key
  pulse license              Show current license and plan info

  pulse uninstall            Remove ~/.pulse/ data for this project
  pulse uninstall --all      Remove entire ~/.pulse/ directory
"""


def _brain_check(command: str, args: list) -> bool:
    """Show brain context before critical commands. Never blocks."""
    # Read-only commands and subcommands never need checking
    if command not in ("post",):
        return True
    # swarm status/stop are read-only, swarm itself is interactive (has its own picker)
    if command == "swarm" and args and args[0] in ("status", "stop"):
        return True

    query = f"{command} {' '.join(args[:5])}".strip()
    briefing = run_query(query)

    # Show brain context but don't block — Law 9 (Silent Governance)
    if briefing and "!! PREDICTION:" in briefing:
        print(f"\n[BRAIN] {briefing}\n")

    return True


def _ensure_initialized():
    """Auto-init on first use if brain doesn't exist."""
    from pulse.core.paths import _is_dev_mode, get_brain_dir, ConsentDeclined, NoProjectRoot
    if _is_dev_mode():
        return
    brain = get_brain_dir()
    if not (brain / "Lessons").exists():
        print("[PULSE] First run detected — initializing brain...")
        try:
            from pulse.core.paths import init_user_brain
            init_user_brain()
        except NoProjectRoot as e:
            print(f"  {e}")
            sys.exit(0)
        except ConsentDeclined as e:
            print(f"  {e}")
            print("  Run 'pulse' in an interactive terminal to give consent.")
            sys.exit(0)


def _license_hint():
    """Non-blocking hint if no license found (installed mode only)."""
    from pulse.core.paths import _is_dev_mode, get_license_file
    if _is_dev_mode():
        return
    if not get_license_file().exists():
        print("[PULSE] No license found. Run 'pulse activate <KEY>' or visit https://pulseos.dev/pricing")


def main():
    # These commands don't need brain init — run them immediately
    _NO_INIT_CMDS = {"activate", "license", "init", "uninstall"}
    if len(sys.argv) >= 2 and sys.argv[1].lower() not in _NO_INIT_CMDS:
        _ensure_initialized()
    _license_hint()

    if len(sys.argv) < 2:
        return cmd_chat_picker()

    cmd = sys.argv[1].lower()
    rest = sys.argv[2:]

    routes = {
        "status":    lambda: cmd_status(),
        "watch":     lambda: cmd_watch(),
        "brain":     lambda: _brain_subcommand(rest),
        "costs":     lambda: cmd_costs(),
        "history":   lambda: cmd_history(rest),
        "swarm":     lambda: cmd_swarm(rest),
        "start":     lambda: cmd_start(),
        "stop":      lambda: cmd_stop(),
        "post":      lambda: cmd_post(rest),
        "relay":     lambda: cmd_relay(rest),
        "clean":     lambda: cmd_clean(),
        "logs":      lambda: cmd_logs(rest),
        "dashboard": lambda: cmd_dashboard(rest),
        "init":      lambda: cmd_init(),
        "dream":     lambda: cmd_dream(rest),
        "proposals": lambda: cmd_proposals(rest),
        "forage":    lambda: cmd_forage(rest),
        "synthesize": lambda: cmd_synthesize(rest),
        "ghost": lambda: cmd_ghost(rest),
        "hebbian": lambda: cmd_hebbian(rest),
        "homeostatic": lambda: cmd_homeostatic(rest),
        "healer": lambda: cmd_healer(rest),
        "uninstall": lambda: cmd_uninstall(rest),
        "activate": lambda: cmd_activate(rest),
        "license":  lambda: cmd_license_info(rest),
    }

    if cmd in routes:
        # Enforce brain check for critical ops
        if not _brain_check(cmd, rest):
            return
        return routes[cmd]()
    elif cmd in AGENT_NAMES:
        # Interactive agents also get checked
        if not _brain_check(cmd, rest):
            return
        return cmd_agent(cmd, rest)
    elif cmd in ("-h", "--help", "help"):
        print(USAGE)
    elif cmd in ("-v", "--version", "version"):
        from importlib.metadata import version as _v
        try:
            print(f"pulse-os {_v('pulse-os')}")
        except Exception:
            print("pulse-os (version unknown)")
    else:
        print(f"Unknown command: {cmd}")
        print(USAGE)


def _brain_subcommand(args: list):
    """Route 'pulse brain [export|import]' or show dashboard."""
    if args and args[0] == "export":
        return cmd_brain_export(args[1:])
    elif args and args[0] == "import":
        return cmd_brain_import(args[1:])
    return cmd_brain()


def cmd_init():
    """Initialize ~/.pulse/ brain scaffold for new users."""
    from pulse.core.paths import init_user_brain, ConsentDeclined, get_project_root
    try:
        init_user_brain(force=True)
        # Create .pulse.yaml anchor if not in a git repo
        root = get_project_root()
        yaml_file = root / ".pulse.yaml"
        if not yaml_file.exists():
            yaml_file.write_text(
                "# PULSE project config\n# See https://pulseos.dev/docs/config\n",
                encoding="utf-8",
            )
            print(f"  Anchor:     {yaml_file}")
    except ConsentDeclined:
        print("[PULSE] Setup cancelled. Run 'pulse init' when you're ready.")
        raise SystemExit(0)



def cmd_uninstall(args: list):
    """Remove PULSE data. Default: current project only. --all: entire ~/.pulse/."""
    import shutil
    from pulse.core.paths import get_pulse_home, get_project_dir, _is_dev_mode

    if _is_dev_mode():
        print("[PULSE] Running in dev mode — nothing to uninstall.")
        print("  Your brain data lives in the repo itself.")
        return

    remove_all = "--all" in args

    if remove_all:
        pulse_home = get_pulse_home()
        if not pulse_home.exists():
            print(f"[PULSE] {pulse_home} does not exist — nothing to remove.")
            return
        confirm = input(f"Remove ALL PULSE data at {pulse_home}? [y/N] ").strip().lower()
        if confirm != "y":
            print("Cancelled.")
            return
        shutil.rmtree(pulse_home)
        print(f"[PULSE] Removed {pulse_home}")
    else:
        proj_dir = get_project_dir()
        if not proj_dir.exists():
            print(f"[PULSE] No project data at {proj_dir} — nothing to remove.")
            return
        confirm = input(f"Remove project data at {proj_dir}? [y/N] ").strip().lower()
        if confirm != "y":
            print("Cancelled.")
            return
        shutil.rmtree(proj_dir)
        print(f"[PULSE] Removed {proj_dir}")

    print("[PULSE] Uninstall complete. Run `pip uninstall pulse-os` to remove the package.")


if __name__ == "__main__":
    main()
