"""Tests for config.py - settings, local state, PID helpers."""

from __future__ import annotations

import json

import pytest

from cli.config import (
    LocalBeacon,
    LocalState,
    Settings,
    clear_pid,
    read_pid,
    write_pid,
)
from cli.exceptions import BeaconConfigError


# ---------------------------------------------------------------------------
# Settings
# ---------------------------------------------------------------------------


class TestSettings:
    def test_from_env_reads_vars(self, monkeypatch):
        monkeypatch.setenv("ETHIACK_API_KEY", "k")
        monkeypatch.setenv("ETHIACK_API_SECRET", "s")
        monkeypatch.setenv("ETHIACK_API_URL", "https://custom.api.com/")
        s = Settings.from_env()
        assert s.api_key == "k"
        assert s.api_secret == "s"
        assert s.api_url == "https://custom.api.com"  # trailing slash stripped

    def test_from_env_default_url(self, monkeypatch):
        monkeypatch.delenv("ETHIACK_API_URL", raising=False)
        s = Settings.from_env()
        assert s.api_url == "https://api.ethiack.com"

    def test_validate_raises_when_key_missing(self, monkeypatch):
        monkeypatch.delenv("ETHIACK_API_KEY", raising=False)
        s = Settings.from_env()
        with pytest.raises(BeaconConfigError, match="ETHIACK_API_KEY"):
            s.validate()

    def test_validate_raises_when_secret_missing(self, monkeypatch):
        monkeypatch.delenv("ETHIACK_API_SECRET", raising=False)
        s = Settings.from_env()
        with pytest.raises(BeaconConfigError, match="ETHIACK_API_SECRET"):
            s.validate()

    def test_validate_passes_when_both_set(self):
        s = Settings(api_key="k", api_secret="s")
        s.validate()  # should not raise


# ---------------------------------------------------------------------------
# LocalState persistence
# ---------------------------------------------------------------------------


class TestLocalState:
    def test_load_returns_empty_when_no_file(self, state_dir):
        state = LocalState.load()
        assert state.beacons == {}

    def test_save_and_load_roundtrip(self, state_dir):
        beacon = LocalBeacon(
            beacon_id="abc123",
            name="my-beacon",
            interface="wg0",
            wg_port=51820,
        )
        state = LocalState()
        state.add(beacon)

        loaded = LocalState.load()
        assert "abc123" in loaded.beacons
        assert loaded.beacons["abc123"].name == "my-beacon"
        assert loaded.beacons["abc123"].wg_port == 51820

    def test_remove_deletes_entry(self, state_dir):
        state = LocalState()
        state.add(LocalBeacon(beacon_id="x", name="n", interface="wg0", wg_port=51820))
        state.remove("x")
        loaded = LocalState.load()
        assert "x" not in loaded.beacons

    def test_get_returns_none_for_missing(self, state_dir):
        state = LocalState.load()
        assert state.get("nonexistent") is None

    def test_update_pid(self, state_dir):
        state = LocalState()
        state.add(LocalBeacon(beacon_id="y", name="n", interface="wg0", wg_port=51820))
        state.update_pid("y", 12345)
        loaded = LocalState.load()
        assert loaded.beacons["y"].rathole_pid == 12345

    def test_load_raises_on_corrupt_file(self, state_dir):
        import cli.config as cfg

        cfg.STATE_FILE.write_text("not valid json {{{")
        with pytest.raises(BeaconConfigError, match="Corrupt state file"):
            LocalState.load()


# ---------------------------------------------------------------------------
# PID helpers
# ---------------------------------------------------------------------------


class TestPidHelpers:
    def test_write_and_read_pid(self, state_dir):
        write_pid("beacon-abc", 42000)
        assert read_pid("beacon-abc") == 42000

    def test_read_returns_none_when_missing(self, state_dir):
        assert read_pid("no-such-beacon") is None

    def test_clear_pid_removes_file(self, state_dir):
        write_pid("beacon-xyz", 99)
        clear_pid("beacon-xyz")
        assert read_pid("beacon-xyz") is None

    def test_clear_pid_no_error_when_missing(self, state_dir):
        clear_pid("ghost")  # must not raise
