import json
import time

from bubblecam import state_lib


def test_write_then_read_round_trips(sim_env):
    state_lib.write_state("camera_primary", "running", segment="cam0_20260101T000000.mkv")
    result = state_lib.read_state("camera_primary")
    assert result["state"] == "running"
    assert result["segment"] == "cam0_20260101T000000.mkv"
    assert isinstance(result["last_update"], float)


def test_read_missing_returns_none(sim_env):
    assert state_lib.read_state("never_written") is None


def test_read_corrupt_file_returns_none(sim_env):
    state_dir = sim_env["state_dir"]
    state_dir.mkdir(parents=True, exist_ok=True)
    (state_dir / "broken.json").write_text("{not valid json")
    assert state_lib.read_state("broken") is None


def test_is_running_true_when_fresh_and_running(sim_env):
    state_lib.write_state("sensors", "running")
    assert state_lib.is_running("sensors") is True


def test_is_running_false_when_state_is_not_running(sim_env):
    state_lib.write_state("sensors", "starting")
    assert state_lib.is_running("sensors") is False


def test_is_running_false_when_stale(sim_env):
    state_lib.write_state("sensors", "running")
    state = state_lib.read_state("sensors")
    state["last_update"] -= 999
    (sim_env["state_dir"] / "sensors.json").write_text(json.dumps(state))
    assert state_lib.is_running("sensors", stale_after=30) is False


def test_has_error_false_for_never_started_service(sim_env):
    assert state_lib.has_error("camera_primary") is False


def test_has_error_true_for_explicit_error(sim_env):
    state_lib.write_state("sensors", "error", detail="i2c fault")
    assert state_lib.has_error("sensors") is True


def test_has_error_true_for_stale_heartbeat(sim_env):
    state_lib.write_state("sensors", "running")
    state = state_lib.read_state("sensors")
    state["last_update"] -= 999
    (sim_env["state_dir"] / "sensors.json").write_text(json.dumps(state))
    assert state_lib.has_error("sensors", stale_after=30) is True


def test_has_error_false_for_fresh_running(sim_env):
    state_lib.write_state("sensors", "running")
    assert state_lib.has_error("sensors") is False


def test_recording_requested_defaults_true_when_no_switch_state(sim_env):
    # No record-switch service installed/running -> fail toward recording.
    assert state_lib.recording_requested() is True


def test_recording_requested_true_on_live_record_reading(sim_env):
    state_lib.write_state("record_switch", "running", recording_requested=True)
    assert state_lib.recording_requested() is True


def test_recording_requested_false_on_live_stop_reading(sim_env):
    state_lib.write_state("record_switch", "running", recording_requested=False)
    assert state_lib.recording_requested() is False


def test_recording_requested_true_when_switch_state_stale(sim_env):
    # A dead switch daemon (stale file) must not keep recording stopped.
    state_lib.write_state("record_switch", "running", recording_requested=False)
    state = state_lib.read_state("record_switch")
    state["last_update"] -= 999
    (sim_env["state_dir"] / "record_switch.json").write_text(json.dumps(state))
    assert state_lib.recording_requested(stale_after=30) is True


def test_state_files_are_world_readable(sim_env):
    # Operators debug over SSH as a non-root user; mkstemp's default 0600
    # made `cat /run/bubblecam/*.json` fail with Permission denied.
    import stat

    state_lib.write_state("sensors", "running")
    mode = (sim_env["state_dir"] / "sensors.json").stat().st_mode
    assert mode & stat.S_IROTH


def test_write_state_is_atomic_no_tmp_files_left_behind(sim_env):
    state_lib.write_state("camera_primary", "running")
    state_lib.write_state("camera_primary", "running", segment="b.mkv")
    leftovers = list(sim_env["state_dir"].glob("*.tmp"))
    assert leftovers == []
