"""Derived Watch host state: thresholds, hysteresis and offline handling."""
import pytest

from device_gateway.collectors.host_state import (
    ACTIVE,
    ACTIVE_CPU_PERCENT,
    BUSY,
    BUSY_CPU_PERCENT,
    BUSY_MEMORY_PERCENT,
    IDLE,
    OFFLINE,
    host_state,
)


def test_offline_when_gateway_or_snapshot_invalid():
    assert host_state(10.0, 20.0, gateway_online=False) == OFFLINE
    assert host_state(10.0, 20.0, valid=False) == OFFLINE
    assert host_state(None, 20.0) == OFFLINE
    assert host_state(10.0, "high") == OFFLINE
    # bool must not be coerced to 0/1.
    assert host_state(True, 20.0) == OFFLINE


def test_idle_active_busy_boundaries():
    assert host_state(0.0, 0.0) == IDLE
    # Just under Active.
    assert host_state(ACTIVE_CPU_PERCENT - 0.1, 10.0) == IDLE
    # Exactly at the Active boundary.
    assert host_state(ACTIVE_CPU_PERCENT, 10.0) == ACTIVE
    # Just under Busy.
    assert host_state(BUSY_CPU_PERCENT - 0.1, 10.0) == ACTIVE
    # Exactly at the Busy boundary.
    assert host_state(BUSY_CPU_PERCENT, 10.0) == BUSY
    # CPU is low but memory is saturated.
    assert host_state(1.0, BUSY_MEMORY_PERCENT) == BUSY
    assert host_state(1.0, BUSY_MEMORY_PERCENT - 0.1) == IDLE


def test_hysteresis_prevents_boundary_flapping():
    # Hovering at 69/71 must not oscillate once Busy is established.
    assert host_state(71.0, 10.0) == BUSY
    assert host_state(69.0, 10.0, previous=BUSY) == BUSY
    # Below the hysteresis band it is allowed to leave Busy.
    assert host_state(64.0, 10.0, previous=BUSY) == ACTIVE
    # A fresh Idle reading enters Busy only at the nominal threshold.
    assert host_state(69.0, 10.0) == ACTIVE
    # The same band applies to the Active boundary.
    assert host_state(19.0, 10.0, previous=ACTIVE) == ACTIVE
    assert host_state(19.0, 10.0) == IDLE
    assert host_state(14.0, 10.0, previous=ACTIVE) == IDLE


@pytest.mark.parametrize("cpu,memory,expected", [
    (0.0, 0.0, IDLE),
    (35.0, 40.0, ACTIVE),
    (95.0, 40.0, BUSY),
    (5.0, 99.0, BUSY),
])
def test_representative_states(cpu, memory, expected):
    assert host_state(cpu, memory) == expected