"""
Tests for the configuration module.
"""

import pytest
import os
import tempfile
from pathlib import Path
from esppal.config import (
    DaemonConfig, load_config, load_user_config, save_user_config,
    get_socket_path, get_pid_path, parse_vidpid, get_config_path,
    DEFAULT_BUFFER_SIZE_MB, DEFAULT_IDLE_TIMEOUT_MINUTES, DEFAULT_BAUDRATE
)


class TestDaemonConfig:
    """Tests for DaemonConfig dataclass."""

    def test_default_values(self):
        """Config has correct default values."""
        config = DaemonConfig()

        assert config.buffer_size_bytes == DEFAULT_BUFFER_SIZE_MB * 1024 * 1024
        assert config.idle_timeout_seconds == DEFAULT_IDLE_TIMEOUT_MINUTES * 60
        assert config.baudrate == DEFAULT_BAUDRATE
        assert config.vid is None
        assert config.pid is None
        assert config.device is None

    def test_vid_pid_property(self):
        """VID:PID property returns correct value."""
        config = DaemonConfig()
        assert config.vid_pid is None

        config.vid = "10c4"
        assert config.vid_pid is None  # Need both

        config.pid = "ea60"
        assert config.vid_pid == "10c4:ea60"

    def test_from_dict(self):
        """Config created from dictionary."""
        data = {
            "daemon": {
                "buffer_size_mb": 100,
                "idle_timeout_minutes": 60
            },
            "defaults": {
                "baudrate": 9600
            }
        }

        config = DaemonConfig.from_dict(data)

        assert config.buffer_size_bytes == 100 * 1024 * 1024
        assert config.idle_timeout_seconds == 60 * 60
        assert config.baudrate == 9600

    def test_from_empty_dict(self):
        """Config from empty dict uses defaults."""
        config = DaemonConfig.from_dict({})

        assert config.buffer_size_bytes == DEFAULT_BUFFER_SIZE_MB * 1024 * 1024
        assert config.baudrate == DEFAULT_BAUDRATE


class TestLoadConfig:
    """Tests for load_config function."""

    def test_load_with_overrides(self):
        """Explicit arguments override defaults."""
        config = load_config(
            vid="abcd",
            pid="1234",
            device="/dev/ttyUSB0",
            buffer_size_mb=25,
            idle_timeout_minutes=15,
            baudrate=9600
        )

        assert config.vid == "abcd"
        assert config.pid == "1234"
        assert config.device == "/dev/ttyUSB0"
        assert config.buffer_size_bytes == 25 * 1024 * 1024
        assert config.idle_timeout_seconds == 15 * 60
        assert config.baudrate == 9600

    def test_load_partial_overrides(self):
        """Partial overrides keep defaults."""
        config = load_config(baudrate=115200)

        assert config.baudrate == 115200
        assert config.buffer_size_bytes == DEFAULT_BUFFER_SIZE_MB * 1024 * 1024


class TestParseVidPid:
    """Tests for parse_vidpid function."""

    def test_valid_format(self):
        """Parse valid VID:PID string."""
        vid, pid = parse_vidpid("10c4:ea60")
        assert vid == "10c4"
        assert pid == "ea60"

    def test_uppercase(self):
        """Parse uppercase VID:PID."""
        vid, pid = parse_vidpid("10C4:EA60")
        assert vid == "10C4"
        assert pid == "EA60"

    def test_missing_colon(self):
        """Missing colon raises ValueError."""
        with pytest.raises(ValueError) as exc_info:
            parse_vidpid("10c4ea60")

        assert "Invalid VID:PID format" in str(exc_info.value)

    def test_empty_vid(self):
        """Empty VID raises ValueError."""
        with pytest.raises(ValueError) as exc_info:
            parse_vidpid(":ea60")

        assert "empty component" in str(exc_info.value)

    def test_empty_pid(self):
        """Empty PID raises ValueError."""
        with pytest.raises(ValueError) as exc_info:
            parse_vidpid("10c4:")

        assert "empty component" in str(exc_info.value)


class TestSocketPaths:
    """Tests for socket/PID path generation."""

    def test_socket_path_with_vidpid(self):
        """Socket path includes VID:PID."""
        path = get_socket_path(vid="10c4", pid="ea60")
        assert path == "/tmp/esppal-10c4:ea60.sock"

    def test_socket_path_with_device(self):
        """Socket path uses device hash as fallback."""
        path = get_socket_path(device="/dev/ttyUSB0")
        assert path.startswith("/tmp/esppal-")
        assert path.endswith(".sock")
        assert "10c4" not in path  # Not a VID:PID format

    def test_socket_path_default(self):
        """Default socket path when no identifiers."""
        path = get_socket_path()
        assert path == "/tmp/esppal-default.sock"

    def test_pid_path_matches_socket(self):
        """PID path derived from socket path."""
        socket_path = get_socket_path(vid="10c4", pid="ea60")
        pid_path = get_pid_path(vid="10c4", pid="ea60")

        assert pid_path == socket_path.replace(".sock", ".pid")
        assert pid_path == "/tmp/esppal-10c4:ea60.pid"


class TestUserConfig:
    """Tests for user config file handling."""

    def test_save_and_load_config(self, monkeypatch, tmp_path):
        """Save and load user config."""
        # Use temp directory for config
        config_dir = tmp_path / ".config" / "esppal"
        monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / ".config"))

        # Save config
        data = {
            "daemon": {"buffer_size_mb": 100},
            "defaults": {"baudrate": 9600}
        }
        assert save_user_config(data)

        # Load config
        loaded = load_user_config()
        assert loaded["daemon"]["buffer_size_mb"] == 100
        assert loaded["defaults"]["baudrate"] == 9600

    def test_load_nonexistent_config(self, monkeypatch, tmp_path):
        """Load returns empty dict if no config file."""
        monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / ".config"))

        loaded = load_user_config()
        assert loaded == {}

    def test_invalid_yaml_config(self, monkeypatch, tmp_path):
        """Invalid YAML returns empty dict."""
        config_dir = tmp_path / ".config" / "esppal"
        config_dir.mkdir(parents=True)
        config_file = config_dir / "config.yaml"
        config_file.write_text("invalid: yaml: content: [")

        monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / ".config"))

        loaded = load_user_config()
        assert loaded == {}
