"""Unit tests for config module."""

from typing import Callable, Dict, Optional, Tuple

import pytest

from multilat_solver.config import ConfigBuilder, LocalizationConfig, MessageType
from multilat_solver.input_adapters import InputAdapter
from multilat_solver.output_adapters import ConsoleOutputAdapter, OutputAdapter


class TestConfigBuilder:
    """Test ConfigBuilder."""

    @pytest.fixture
    def config_builder(self):
        """Return a ConfigBuilder instance."""
        return ConfigBuilder()

    @pytest.fixture
    def localization_config(self):
        """Return a LocalizationConfig instance."""
        return LocalizationConfig(
            anchor_positions=[[0, 0, 0], [1, 0, 0], [0, 1, 0]],
            origin_coordinates=[0, 0, 0],
        )

    def test_without_settings(self, config_builder):
        """Test ConfigBuilder without settings."""
        with pytest.raises(ValueError):
            config_builder.build()

    def test_without_input(self, config_builder, localization_config):
        """Test ConfigBuilder without input."""
        config_builder.with_output(
            output_str="console",
            adapter=ConsoleOutputAdapter(format="human"),
        )
        config_builder.with_localization(localization_config)
        with pytest.raises(ValueError, match="Input configuration is required"):
            config_builder.build()

    def test_without_output(self, config_builder, localization_config):
        """Test ConfigBuilder without output."""
        config_builder.with_file_input(filepath="test.txt", poll_interval=1.0)
        config_builder.with_localization(localization_config)
        with pytest.raises(ValueError, match="Output configuration is required"):
            config_builder.build()

    def test_without_localization(self, config_builder):
        """Test ConfigBuilder without localization type."""
        config_builder.with_file_input(filepath="test.txt", poll_interval=1.0)
        config_builder.with_output(
            output_str="mavlink",
            adapter=ConsoleOutputAdapter(message_type=MessageType.POSITION, frequency=10),
        )
        with pytest.raises(ValueError, match="Localization configuration is required"):
            config_builder.build()

    def test_custom_output_adapter(self, config_builder, localization_config):
        """Test custom output adapter."""

        class CustomOutputAdapter(OutputAdapter):
            """Custom output adapter."""

            def __init__(
                self,
                custom_config: str,
                message_type: MessageType = MessageType.POSITION,
                frequency: int = 10,
            ):
                self.custom_config = custom_config
                super().__init__(message_type, frequency)

            def send_position(
                self,
                position: Tuple[float, float, float],
                timestamp: Optional[float] = None,
            ):
                """Send position."""
                print(f"Position: {position}")

            def send_gps(
                self,
                coordinates: Tuple[float, float, float],
                timestamp: Optional[float] = None,
            ):
                """Send GPS coordinates."""
                print(f"GPS coordinates: {coordinates}")

            def close(self):
                """Close adapter."""
                print("Closing custom output adapter.")

        config_builder.with_localization(localization_config)
        config_builder.with_file_input(filepath="test.txt", poll_interval=1.0)
        config_builder.with_output(
            output_str="custom",
            adapter=CustomOutputAdapter(custom_config="custom_config"),
        )
        config = config_builder.build()
        assert config.output.types[0] == "custom"
        assert config.output.adapters["custom"] is not None

    def test_register_input_adapter_with_config(self, config_builder, localization_config):
        """Test register_input_adapter with config."""

        class CustomInputAdapter(InputAdapter):
            """Custom input adapter."""

            def __init__(self, custom_param: int):
                self.custom_param = custom_param

            def start(self, callback: Callable[[Dict[int, Dict[str, float]]], None]):
                pass

            def stop(self):
                pass

            def is_running(self) -> bool:
                return False

            def _read_loop(self):
                pass

        config_builder.with_output(
            output_str="console",
            adapter=ConsoleOutputAdapter(format="human"),
        )
        config_builder.with_localization(localization_config)
        config_builder.with_input(input_str="custom", adapter=CustomInputAdapter(custom_param=1))
        config = config_builder.build()
        assert config.input.type_name == "custom"
        assert config.input.adapter is not None
        assert config.input.adapter.custom_param == 1
