"""``decnet canary`` — HTTP + DNS callback receiver for canary tokens.

Two entry points share this module:

* ``decnet canary`` — runs the worker process. Mirrors the shape of
  :mod:`decnet.cli.webhook`. Invoked by the ``decnet-canary.service``
  systemd unit so its argv must stay stable.
* ``decnet canary-install-toolchain`` — provisions the Node side of
  the fingerprint-canary obfuscator. Idempotent; safe to call from
  the API service unit's ``ExecStartPre``.

Not master-only — any host that hosts deckies can run its own
canary worker (the bus events stay local; the webhook worker on
each host fans them out to SIEMs independently per the design
in ``development/let-s-move-to-the-enumerated-pike.md``).
"""
from __future__ import annotations

import shutil
import subprocess  # nosec B404 — npm exec is the whole point of the toolchain installer
from pathlib import Path

import typer

from . import utils as _utils
from .utils import console, log

_TOOLCHAIN_TIMEOUT_S = 180


def register(app: typer.Typer) -> None:
    @app.command(name="canary")
    def canary_cmd(
        daemon: bool = typer.Option(
            False, "--daemon", "-d", help="Detach to background as a daemon process",
        ),
    ) -> None:
        """Run the canary HTTP + DNS callback receiver."""
        import asyncio

        from decnet.canary.worker import run

        if daemon:
            log.info("canary daemonizing")
            _utils._daemonize()

        log.info("canary starting")
        console.print("[bold cyan]Canary callback receiver starting[/]")

        try:
            asyncio.run(run())
        except KeyboardInterrupt:
            console.print("\n[yellow]Canary worker stopped.[/]")

    @app.command(name="canary-install-toolchain")
    def canary_install_toolchain(
        npm_bin: str = typer.Option(
            "npm", "--npm-bin", help="Path to the npm executable. Defaults to PATH lookup.",
        ),
    ) -> None:
        """Install the Node-side toolchain used by fingerprint canaries.

        Runs ``npm install --omit=dev`` under the installed ``decnet/canary/``
        directory so the obfuscator's helper script can ``require()``
        ``javascript-obfuscator`` at mint time. Requires Node >= 18.

        Idempotent: re-running on an already-installed tree is fast
        (npm short-circuits when ``node_modules/`` is up-to-date).
        """
        import decnet.canary as _canary_pkg
        canary_dir = Path(_canary_pkg.__file__).resolve().parent
        if not (canary_dir / "package.json").is_file():
            console.print(
                f"[red]canary package.json not found under {canary_dir}; "
                "wheel may be missing the JS toolchain payload.[/]"
            )
            raise typer.Exit(code=2)
        if shutil.which(npm_bin) is None:
            console.print(
                f"[red]npm executable {npm_bin!r} not found on PATH. "
                "Install Node >= 18 and re-run.[/]"
            )
            raise typer.Exit(code=2)
        console.print(
            f"[cyan]installing canary toolchain[/] in {canary_dir}",
        )
        try:
            proc = subprocess.run(  # nosec B603 — argv-form, no shell, fixed cwd, npm_bin checked above
                [npm_bin, "install", "--omit=dev", "--no-fund", "--no-audit"],
                cwd=str(canary_dir),
                capture_output=True, text=True,
                timeout=_TOOLCHAIN_TIMEOUT_S, check=False,
            )
        except subprocess.TimeoutExpired:
            console.print("[red]npm install timed out after 3 minutes[/]")
            raise typer.Exit(code=3) from None
        if proc.returncode != 0:
            console.print(
                f"[red]npm install failed rc={proc.returncode}[/]\n"
                f"{proc.stderr.strip()}"
            )
            raise typer.Exit(code=proc.returncode)
        console.print("[green]canary toolchain ready[/]")
