#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Lifecycle commands: start, stop, clean, dashboard."""
from __future__ import annotations

import json
import os
import subprocess
import sys
import atexit
import time
from datetime import datetime, timezone

from pulse.cli import DAEMONS_DIR, STATE_DIR, TASK_BOARD
from pulse.tasks.task_ops import (
    is_process_running, kill_process, load_json,
    PID_FILE, DAEMON_PIDS,
)


def _purge_runtime_state() -> int:
    """Auto-delete disposable runtime state. Returns count of items removed."""
    import shutil

    state_files = [
        STATE_DIR / "brain_compliance.json",
        STATE_DIR / "reward_signals.json",
        # agent_registry.json is NOT deleted here — worker cleanup is handled
        # by deregister_stale_workers() in swarm_stop, preserving agent entries
        STATE_DIR / "prediction_alerts.json",
        STATE_DIR / "prediction_metrics.json",
        STATE_DIR / "skill_usage.json",
    ]
    state_dirs = [
        STATE_DIR / "heartbeats",
        STATE_DIR / "worker_sessions",
    ]
    context_files = list(STATE_DIR.glob("pulse_context_*.md"))

    deleted = 0
    for f in state_files + context_files:
        if f.exists():
            f.unlink()
            deleted += 1
    for d in state_dirs:
        if d.exists():
            shutil.rmtree(d)
            deleted += 1
    return deleted


def cmd_start():
    """Start the orchestrator as a true background daemon (survives terminal close)."""
    # Fresh session — purge stale runtime state
    purged = _purge_runtime_state()
    if purged:
        print(f"[PULSE] Purged {purged} stale state files.")
    orch = DAEMONS_DIR / "orchestrator.py"
    STATE_DIR.mkdir(parents=True, exist_ok=True)
    log_out = open(STATE_DIR / "pulse_orchestrator.log", "a", encoding="utf-8")
    log_err = open(STATE_DIR / "pulse_orchestrator.err", "a", encoding="utf-8")
    print("[PULSE] Starting orchestrator (headless daemon)...")
    if os.name == "nt":
        subprocess.Popen(
            [sys.executable, str(orch), "--verbose"],
            stdout=log_out, stderr=log_err,
            creationflags=subprocess.CREATE_NO_WINDOW,
        )
    else:
        subprocess.Popen(
            [sys.executable, str(orch), "--verbose"],
            stdout=log_out, stderr=log_err,
            start_new_session=True,
        )
    time.sleep(1)
    print("[PULSE] Orchestrator launched (headless). Logs: state/pulse_orchestrator.log")
    print("[PULSE] Use 'pulse status' to check, 'pulse stop' to kill.")


def cmd_stop():
    """Stop orchestrator and all daemons."""
    killed = 0

    if PID_FILE.exists():
        try:
            pid = int(PID_FILE.read_text().strip())
            if is_process_running(pid):
                kill_process(pid)
                killed += 1
        except Exception:
            pass

    if DAEMON_PIDS.exists():
        try:
            pids = json.loads(DAEMON_PIDS.read_text())
            for pid in pids:
                if is_process_running(pid):
                    try:
                        kill_process(pid)
                        killed += 1
                    except Exception:
                        pass
            DAEMON_PIDS.unlink(missing_ok=True)
        except Exception:
            pass

    try:
        PID_FILE.unlink(missing_ok=True)
    except Exception:
        pass

    # Auto-purge disposable runtime state
    purged = _purge_runtime_state()
    print(f"[PULSE] Stopped {killed} process(es). Purged {purged} state files.")


def cmd_clean():
    """Remove old tasks and reset stale claims."""
    board = load_json(TASK_BOARD)
    dispatches = board.get("dispatches", [])
    now = datetime.now(timezone.utc)

    removed_count = 0
    reset_count = 0

    for dispatch in dispatches:
        tasks = dispatch.get("tasks", [])
        new_tasks = []
        for t in tasks:
            status = t.get("status", "")

            if status in ("done", "completed"):
                comp_at = t.get("completed_at")
                if comp_at:
                    try:
                        comp_dt = datetime.fromisoformat(comp_at.replace("Z", "+00:00"))
                        if (now - comp_dt).days >= 7:
                            removed_count += 1
                            continue
                    except ValueError:
                        pass

            if status in ("active", "claimed"):
                claim_at = t.get("claimed_at")
                if claim_at:
                    try:
                        claim_dt = datetime.fromisoformat(claim_at.replace("Z", "+00:00"))
                        if (now - claim_dt).total_seconds() >= 3600:
                            t["status"] = "open"
                            t["claimed_by"] = None
                            t["claimed_at"] = None
                            reset_count += 1
                    except ValueError:
                        pass

            new_tasks.append(t)
        dispatch["tasks"] = new_tasks

    board["dispatches"] = [d for d in dispatches if d.get("tasks")]

    with open(TASK_BOARD, "w", encoding="utf-8") as f:
        json.dump(board, f, indent=2)

    print("[PULSE] Cleanup complete:")
    print(f"  - Removed {removed_count} old tasks (>7 days)")
    print(f"  - Reset {reset_count} stale claims (>1 hour)")


def cmd_dashboard(args: list[str]):
    """Start the web monitoring dashboard."""
    port = 8000
    if args:
        try:
            port = int(args[0])
        except ValueError:
            pass
    dashboard_script = DAEMONS_DIR / "pulse_dashboard.py"
    print(f"[PULSE] Starting dashboard on http://localhost:{port}...")
    subprocess.run([sys.executable, str(dashboard_script), "--port", str(port)])


@atexit.register
def cleanup_browser():
    try:
        from pulse.browser.core import shutdown_browser
        shutdown_browser()
    except ImportError:
        pass

