from pathlib import Path

from conftest import load_bin_module

watchdog = load_bin_module("watchdog")


def test_camera_service_names_single_vs_dual():
    assert watchdog.camera_service_names({"camera": {"mode": "sbs_h264"}}) == ["camera_primary"]
    assert watchdog.camera_service_names({"camera": {"mode": "dual_node_mjpeg"}}) == [
        "camera_left",
        "camera_right",
    ]


def test_camera_systemd_units_single_vs_dual():
    assert watchdog.camera_systemd_units({"camera": {"mode": "sbs_mjpeg"}}) == [
        "bubblecam-camera@primary.service"
    ]
    assert watchdog.camera_systemd_units({"camera": {"mode": "dual_node_h264"}}) == [
        "bubblecam-camera@left.service",
        "bubblecam-camera@right.service",
    ]


def test_stop_camera_services_in_sim_mode_does_not_raise(sim_env):
    # In BUBBLECAM_SIM=1 mode this only prints -- must not shell out to a
    # real systemctl (which doesn't exist on a dev machine).
    watchdog.stop_camera_services({"camera": {"mode": "sbs_mjpeg"}})


def test_trigger_reboot_in_sim_mode_does_not_raise(sim_env):
    watchdog.trigger_reboot()


def test_force_reboot_in_sim_mode_does_not_raise(sim_env):
    # Must not actually write /proc/sysrq-trigger on a dev machine.
    watchdog.force_reboot()


def test_storage_healthy_true_for_a_writable_dir(sim_env, monkeypatch, tmp_path):
    monkeypatch.delenv("BUBBLECAM_SIM", raising=False)
    assert watchdog.storage_healthy(tmp_path) is True


def test_storage_healthy_false_when_dir_is_unwritable(sim_env, monkeypatch, tmp_path):
    monkeypatch.delenv("BUBBLECAM_SIM", raising=False)
    assert watchdog.storage_healthy(tmp_path / "does-not-exist") is False


def test_storage_healthy_false_when_write_hangs(sim_env, monkeypatch, tmp_path):
    """The real failure mode: writes to a dead card never return at all."""
    import time

    monkeypatch.delenv("BUBBLECAM_SIM", raising=False)
    monkeypatch.setattr(watchdog.bounded_io, "DEFAULT_TIMEOUT_SECONDS", 0.1)

    def hang(*args, **kwargs):
        time.sleep(30)

    monkeypatch.setattr("builtins.open", hang)
    started = time.monotonic()
    assert watchdog.storage_healthy(tmp_path) is False
    assert time.monotonic() - started < 3.0


def test_storage_probe_leaves_no_file_behind(sim_env, monkeypatch, tmp_path):
    monkeypatch.delenv("BUBBLECAM_SIM", raising=False)
    watchdog.storage_healthy(tmp_path)
    assert list(tmp_path.iterdir()) == []


def test_storage_healthy_is_a_noop_in_sim_mode(sim_env):
    # Dev machines have no /data; simulate mode must not report a fault.
    assert watchdog.storage_healthy(Path("/definitely/not/here")) is True


def test_stale_noncritical_finds_wedged_service(sim_env):
    """Regression guard: the LED service froze solid (alive per systemd,
    heartbeat stale) -- only the state file's age reveals that."""
    import json

    from bubblecam import state_lib

    state_lib.write_state("led", "red")
    state = state_lib.read_state("led")
    state["last_update"] -= 999
    (sim_env["state_dir"] / "led.json").write_text(json.dumps(state))

    stale = watchdog.stale_noncritical_units(stale_after=120)
    assert ("led", "bubblecam-led.service") in stale


def test_stale_noncritical_ignores_fresh_and_never_started(sim_env):
    from bubblecam import state_lib

    state_lib.write_state("led", "red")  # fresh
    # epaper/lumen never wrote state at all -- deliberately not running is
    # not a fault to remediate.
    assert watchdog.stale_noncritical_units(stale_after=120) == []
