"""
Tests unitaires pour le gestionnaire de configuration.
"""

import json
import os
import tempfile
from pathlib import Path
import pytest
from unittest.mock import patch

from orchestrator.config import Config


class TestConfig:
    """Tests pour le gestionnaire de configuration."""

    @pytest.fixture
    def temp_config_dir(self):
        """Fixture pour créer un répertoire temporaire de config."""
        with tempfile.TemporaryDirectory() as temp_dir:
            yield Path(temp_dir)

    @pytest.fixture
    def config(self, temp_config_dir):
        """Fixture pour créer une instance Config avec répertoire temporaire."""
        with patch.object(Config, "__init__", lambda self: None):
            config_instance = Config()
            config_instance.config_dir = temp_config_dir
            config_instance.logger = None
            config_instance._config = {}
            config_instance.default_config = {
                "mqtt": {"broker": "localhost", "port": 1883},
                "motion": {"pan_limits": [-90, 90], "speed": 50},
                "fullauto": {"enabled": False, "interval_song_ms": 300000},
            }
            config_instance._load_config()

        return config_instance

    def test_default_config_loading(self, config):
        """Test chargement de la configuration par défaut."""
        assert config.get("mqtt", "broker") == "localhost"
        assert config.get("mqtt", "port") == 1883
        assert config.get("motion", "speed") == 50

    def test_get_section(self, config):
        """Test récupération d'une section complète."""
        mqtt_config = config.get("mqtt")

        assert isinstance(mqtt_config, dict)
        assert mqtt_config["broker"] == "localhost"
        assert mqtt_config["port"] == 1883

    def test_get_key(self, config):
        """Test récupération d'une clé spécifique."""
        broker = config.get("mqtt", "broker")
        assert broker == "localhost"

        port = config.get("mqtt", "port")
        assert port == 1883

    def test_get_default_value(self, config):
        """Test valeur par défaut quand clé inexistante."""
        value = config.get("inexistant", "key", "default")
        assert value == "default"

        value = config.get("mqtt", "inexistant", "default")
        assert value == "default"

    def test_set_value(self, config):
        """Test définition d'une valeur."""
        result = config.set("mqtt", "broker", "192.168.1.100")
        assert result is True

        assert config.get("mqtt", "broker") == "192.168.1.100"

        # Vérifier sauvegarde fichier
        config_file = config.config_dir / "mqtt.json"
        assert config_file.exists()

        with open(config_file, "r") as f:
            saved_config = json.load(f)

        assert saved_config["broker"] == "192.168.1.100"

    def test_update_section(self, config):
        """Test mise à jour d'une section complète."""
        new_motion_config = {
            "pan_limits": [-120, 120],
            "tilt_limits": [-60, 60],
            "speed": 75,
        }

        result = config.update_section("motion", new_motion_config)
        assert result is True

        # Vérifier mise à jour
        assert config.get("motion", "pan_limits") == [-120, 120]
        assert config.get("motion", "tilt_limits") == [-60, 60]
        assert config.get("motion", "speed") == 75

    def test_get_all_config(self, config):
        """Test récupération de toute la configuration."""
        all_config = config.get_all()

        assert isinstance(all_config, dict)
        assert "mqtt" in all_config
        assert "motion" in all_config
        assert "fullauto" in all_config

    def test_get_section_list(self, config):
        """Test liste des sections."""
        sections = config.get_section_list()

        assert isinstance(sections, list)
        assert "mqtt" in sections
        assert "motion" in sections

    def test_reset_section(self, config):
        """Test remise à zéro d'une section."""
        # Modifier une valeur
        config.set("mqtt", "broker", "modifie.com")
        assert config.get("mqtt", "broker") == "modifie.com"

        # Remettre à zéro
        result = config.reset_section("mqtt")
        assert result is True

        # Vérifier restauration
        assert config.get("mqtt", "broker") == "localhost"

    def test_reset_unknown_section(self, config):
        """Test remise à zéro d'une section inconnue."""
        result = config.reset_section("inexistant")
        assert result is False

    def test_validate_motion_config(self, config):
        """Test validation configuration motion."""
        # Configuration valide
        valid_config = {"pan_limits": [-90, 90], "tilt_limits": [-45, 45], "speed": 50}

        valid, errors = config.validate_section("motion", valid_config)
        assert valid is True
        assert len(errors) == 0

        # Configuration invalide - limites inversées
        invalid_config = {"pan_limits": [90, -90]}  # min > max

        valid, errors = config.validate_section("motion", invalid_config)
        assert valid is False
        assert len(errors) > 0
        assert "min doit être < max" in errors[0]

    def test_validate_fullauto_config(self, config):
        """Test validation configuration fullauto."""
        # Configuration valide
        valid_config = {"interval_song_ms": 120000, "enabled": True}

        valid, errors = config.validate_section("fullauto", valid_config)
        assert valid is True
        assert len(errors) == 0

        # Configuration invalide - interval trop petit
        invalid_config = {"interval_song_ms": 500}  # < 1000

        valid, errors = config.validate_section("fullauto", invalid_config)
        assert valid is False
        assert "interval_song_ms doit être >= 1000" in errors[0]

    def test_validate_vision_config(self, config):
        """Test validation configuration vision."""
        # Configuration valide
        valid_config = {"motion_threshold": 0.5}

        valid, errors = config.validate_section("vision", valid_config)
        assert valid is True

        # Configuration invalide - seuil hors limites
        invalid_config = {"motion_threshold": 1.5}  # > 1

        valid, errors = config.validate_section("vision", invalid_config)
        assert valid is False
        assert "motion_threshold doit être entre 0 et 1" in errors[0]

    def test_sync_from_mqtt(self, config):
        """Test synchronisation depuis MQTT."""
        mqtt_payload = json.dumps({"broker": "mqtt.skull.com", "port": 8883})

        result = config.sync_from_mqtt("skull/config/mqtt", mqtt_payload)
        assert result is True

        # Vérifier mise à jour
        assert config.get("mqtt", "broker") == "mqtt.skull.com"
        assert config.get("mqtt", "port") == 8883

    def test_sync_from_mqtt_invalid_topic(self, config):
        """Test sync MQTT avec topic invalide."""
        result = config.sync_from_mqtt("invalid/topic", "{}")
        assert result is False

    def test_sync_from_mqtt_invalid_json(self, config):
        """Test sync MQTT avec JSON invalide."""
        result = config.sync_from_mqtt("skull/config/mqtt", "invalid json")
        assert result is False

    def test_sync_from_mqtt_invalid_config(self, config):
        """Test sync MQTT avec configuration invalide."""
        invalid_payload = json.dumps({"pan_limits": [90, -90]})  # Invalide

        result = config.sync_from_mqtt("skull/config/motion", invalid_payload)
        assert result is False

    def test_get_mqtt_topics(self, config):
        """Test récupération topics MQTT."""
        topics = config.get_mqtt_topics()

        assert isinstance(topics, dict)
        assert "skull/config/mqtt" in topics
        assert "skull/config/motion" in topics

        # Vérifier contenu JSON
        mqtt_topic = topics["skull/config/mqtt"]
        mqtt_data = json.loads(mqtt_topic)
        assert mqtt_data["broker"] == "localhost"

    def test_config_file_loading(self, config):
        """Test chargement depuis fichiers existants."""
        # Créer un fichier de config
        config_file = config.config_dir / "test.json"
        test_config = {"param1": "value1", "param2": 42}

        with open(config_file, "w") as f:
            json.dump(test_config, f)

        # Recharger la config
        config._load_config()

        # Vérifier chargement
        assert config.get("test", "param1") == "value1"
        assert config.get("test", "param2") == 42

    @patch.dict(
        os.environ,
        {
            "SKULL_MQTT_BROKER": "env.broker.com",
            "SKULL_MQTT_PORT": "9883",
            "SKULL_API_PORT": "9080",
        },
    )
    def test_env_override(self, temp_config_dir):
        """Test surcharge par variables d'environnement."""
        with patch.object(Config, "__init__", lambda self: None):
            config_instance = Config()
            config_instance.config_dir = temp_config_dir
            config_instance.logger = None
            config_instance._config = {}
            config_instance.default_config = {
                "mqtt": {"broker": "localhost", "port": 1883},
                "api": {"port": 8080},
            }
            config_instance._load_config()

        # Vérifier surcharges
        assert config_instance.get("mqtt", "broker") == "env.broker.com"
        assert config_instance.get("mqtt", "port") == 9883
        assert config_instance.get("api", "port") == 9080

    def test_create_new_section(self, config):
        """Test création d'une nouvelle section."""
        new_section = {"param1": "value1", "param2": True}

        result = config.update_section("nouvelle_section", new_section)
        assert result is True

        # Vérifier création
        assert config.get("nouvelle_section", "param1") == "value1"
        assert config.get("nouvelle_section", "param2") is True

        # Vérifier sauvegarde
        config_file = config.config_dir / "nouvelle_section.json"
        assert config_file.exists()
