"""Typer commands for the Pulse scheduler.

Imported and wired by `cli.py` via `register(app)`. Kept as a separate module
so the main CLI surface stays grep-friendly and the scheduler can grow its own
verbs without bloating cli.py.
"""

from __future__ import annotations

from datetime import datetime, timezone
from typing import Optional

import typer

from psyc import db
from psyc.lines import pulse


def _relative(dt: Optional[datetime]) -> str:
    if dt is None:
        return "—"
    now = datetime.now(timezone.utc)
    if dt.tzinfo is None:
        dt = dt.replace(tzinfo=timezone.utc)
    delta = int((dt - now).total_seconds())
    past = delta < 0
    secs = abs(delta)
    if secs < 5:
        return "now"
    if secs < 60:
        unit = f"{secs}s"
    elif secs < 3600:
        unit = f"{secs // 60}m"
    elif secs < 86400:
        unit = f"{secs // 3600}h"
    else:
        unit = f"{secs // 86400}d"
    return f"{unit} ago" if past else f"in {unit}"


def register(typer_app: typer.Typer) -> None:
    """Add pulse-* commands to the given Typer app."""

    @typer_app.command("pulse-status")
    def pulse_status() -> None:
        """Print the pipeline table (mode · cadence · last-fired · next-fire · last-result)."""
        db.init_db()
        ks = pulse.kill_switch_state()
        typer.echo(f"kill switch: {'ARMED' if ks else 'OFF'}")
        rows = pulse.state()
        if not rows:
            typer.echo("(no pipelines registered)")
            return
        typer.echo(f"{'name':<16} {'mode':<14} {'cadence':>8}  {'enabled':<7} {'last':<10} {'next':<10}  result")
        for p in rows:
            typer.echo(
                f"{p.name:<16} {p.mode.value:<14} {p.cadence_seconds:>6}s   "
                f"{'yes' if p.enabled else 'no':<7} "
                f"{_relative(p.last_fired):<10} {_relative(p.next_fire):<10}  "
                f"{(p.last_result or '')[:60]}"
            )

    @typer_app.command("pulse-tick")
    def pulse_tick() -> None:
        """Run one scheduler heartbeat and print the per-pipeline outcomes."""
        db.init_db()
        results = pulse.tick()
        for name, outcome, result in results:
            marker = {"ok": "✓", "err": "✗", "skipped": "⊘"}.get(outcome, "·")
            typer.echo(f"  {marker} {name:<16} {outcome:<8} {result[:120]}")

    @typer_app.command("pulse-set-mode")
    def pulse_set_mode(
        name: str = typer.Argument(..., help="pipeline name"),
        mode: str = typer.Argument(..., help="manual | auto-propose | auto-execute"),
    ) -> None:
        db.init_db()
        try:
            pulse.set_mode(name, pulse.PulseMode(mode))
        except ValueError as exc:
            typer.echo(f"error: {exc}", err=True)
            raise typer.Exit(1)
        typer.echo(f"{name} mode → {mode}")

    @typer_app.command("pulse-set-cadence")
    def pulse_set_cadence(
        name: str = typer.Argument(..., help="pipeline name"),
        seconds: int = typer.Argument(..., help="cadence in seconds (>0)"),
    ) -> None:
        db.init_db()
        try:
            pulse.set_cadence(name, seconds)
        except ValueError as exc:
            typer.echo(f"error: {exc}", err=True)
            raise typer.Exit(1)
        typer.echo(f"{name} cadence → {seconds}s")

    @typer_app.command("pulse-run")
    def pulse_run(name: str = typer.Argument(..., help="pipeline name")) -> None:
        """Manually fire one pipeline (bypasses cadence; honors kill switch)."""
        db.init_db()
        try:
            outcome, result = pulse.run_now(name)
        except ValueError as exc:
            typer.echo(f"error: {exc}", err=True)
            raise typer.Exit(1)
        marker = {"ok": "✓", "err": "✗", "skipped": "⊘"}.get(outcome, "·")
        typer.echo(f"  {marker} {name}: {outcome} — {result}")

    @typer_app.command("pulse-kill")
    def pulse_kill() -> None:
        """Arm the kill switch — every pipeline halts on the next tick."""
        db.init_db()
        pulse.set_kill_switch(True)
        typer.echo("kill switch ARMED — all pipelines halted")

    @typer_app.command("pulse-unkill")
    def pulse_unkill() -> None:
        """Disarm the kill switch — pulse resumes on the next tick."""
        db.init_db()
        pulse.set_kill_switch(False)
        typer.echo("kill switch disarmed — pulse resumes")
