from __future__ import annotations

import typer

from hytop.core.validators import parse_csv_ints
from hytop.gpu.metrics import (
    SHOW_FLAG_HELP,
    SUPPORTED_SHOW_FLAGS,
    is_supported_show_flag,
    normalized_show_flags,
)
from hytop.gpu.service import run_monitor

app = typer.Typer(
    add_completion=False,
    context_settings={"help_option_names": ["-h", "--help"]},
)

SHOW_FLAG_ORDER_KEY = "show_flag_order"


def remember_show_flag_callback(ctx: typer.Context, param: object, value: bool) -> bool:
    """Record --show* flags in parser encounter order."""

    if not value:
        return value
    flag = getattr(param, "name", None)
    if not isinstance(flag, str) or not is_supported_show_flag(flag):
        return value
    ordered = ctx.meta.setdefault(SHOW_FLAG_ORDER_KEY, [])
    if flag not in ordered:
        ordered.append(flag)
    return value


@app.callback(invoke_without_command=True)
def gpu(
    ctx: typer.Context,
    device_filter: str = typer.Option(
        "",
        "--devices",
        "-d",
        help="Comma-separated GPU IDs, e.g. 0,1. Default: all visible GPUs",
    ),
    wait_idle: bool = typer.Option(
        False,
        "--wait-idle",
        help="Exit 0 when all monitored GPUs have zero VRAM/HCU avg.",
    ),
    wait_idle_seconds: float = typer.Option(
        10.0,
        "--wait-idle-seconds",
        help="How long GPUs must stay idle before exiting. Effective only with --wait-idle.",
    ),
    showtemp: bool = typer.Option(
        False,
        "--showtemp",
        callback=remember_show_flag_callback,
        help=SHOW_FLAG_HELP["showtemp"],
    ),
    showpower: bool = typer.Option(
        False,
        "--showpower",
        callback=remember_show_flag_callback,
        help=SHOW_FLAG_HELP["showpower"],
    ),
    showsclk: bool = typer.Option(
        False,
        "--showsclk",
        callback=remember_show_flag_callback,
        help=SHOW_FLAG_HELP["showsclk"],
    ),
    showmemuse: bool = typer.Option(
        False,
        "--showmemuse",
        callback=remember_show_flag_callback,
        help=SHOW_FLAG_HELP["showmemuse"],
    ),
    showuse: bool = typer.Option(
        False,
        "--showuse",
        callback=remember_show_flag_callback,
        help=SHOW_FLAG_HELP["showuse"],
    ),
) -> None:
    """GPU monitoring commands."""

    if ctx.obj is None:
        typer.echo("argument error: global options not available", err=True)
        raise typer.Exit(code=2)

    try:
        host_list = ctx.obj["hosts"]
        interval = ctx.obj["interval"]
        window_value = ctx.obj["window"]
        timeout_value = ctx.obj.get("timeout")
        local_flags = locals()
        selected_show_flags = {
            flag: bool(local_flags.get(flag, False)) for flag in SUPPORTED_SHOW_FLAGS
        }
        requested_order = [
            flag
            for flag in ctx.meta.get(SHOW_FLAG_ORDER_KEY, [])
            if selected_show_flags.get(flag, False)
        ]
        if requested_order:
            show_flags = normalized_show_flags(requested_order)
        else:
            show_flags = normalized_show_flags(
                [flag for flag, enabled in selected_show_flags.items() if enabled]
            )
        parsed_device_filter: set[int] | None = None
        if device_filter:
            parsed_device_filter = set(parse_csv_ints(device_filter, "--devices"))
    except ValueError as exc:
        typer.echo(f"argument error: {exc}", err=True)
        raise typer.Exit(code=2) from exc

    code = run_monitor(
        hosts=host_list,
        device_filter=parsed_device_filter,
        show_flags=show_flags,
        window=window_value,
        interval=interval,
        wait_idle=wait_idle,
        wait_idle_duration=max(wait_idle_seconds, interval),
        timeout=timeout_value,
    )
    raise typer.Exit(code=code)


def main() -> None:
    """Module entrypoint for direct execution."""

    app()
