"""Derived host load state for the wearable Watch page.

The Watch shows one word, so the gateway classifies the existing 2 s server
snapshot instead of shipping new metrics. Thresholds are intentionally small
and testable; the same values are mirrored in the firmware helper so a device
that loses the link still derives a sensible state from cached values.
"""
from __future__ import annotations

from typing import Any

# Mirror of firmware `boards/common/include/watch_status.h`. Keep in sync.
BUSY_CPU_PERCENT = 70.0
BUSY_MEMORY_PERCENT = 90.0
ACTIVE_CPU_PERCENT = 20.0

# Small hysteresis band so 69 <-> 71 sampling noise cannot flip Busy/Active.
HYSTERESIS_PERCENT = 5.0

OFFLINE = "Offline"
IDLE = "Idle"
ACTIVE = "Active"
BUSY = "Busy"


def host_state(cpu_percent: Any, memory_percent: Any, *, valid: bool = True,
               gateway_online: bool = True, previous: str | None = None) -> str:
    """Classify one server snapshot into the Watch ``Host`` row.

    ``previous`` is the last reported state; when it exists the Busy boundary is
    lowered by the hysteresis band so a value hovering at the threshold keeps
    its current classification instead of oscillating.
    """
    if not gateway_online or not valid:
        return OFFLINE
    if not isinstance(cpu_percent, (int, float)) or not isinstance(memory_percent, (int, float)):
        return OFFLINE
    if isinstance(cpu_percent, bool) or isinstance(memory_percent, bool):
        return OFFLINE
    busy_cpu = BUSY_CPU_PERCENT - HYSTERESIS_PERCENT if previous == BUSY else BUSY_CPU_PERCENT
    active_cpu = ACTIVE_CPU_PERCENT - HYSTERESIS_PERCENT if previous == ACTIVE else ACTIVE_CPU_PERCENT
    if cpu_percent >= busy_cpu or memory_percent >= BUSY_MEMORY_PERCENT:
        return BUSY
    if cpu_percent >= active_cpu:
        return ACTIVE
    return IDLE