"""Tests for configuration system."""

import json
import os
import tempfile
import unittest
from pathlib import Path

from pygeai_orchestration.core.config import (
    CacheConfig,
    ConfigManager,
    ConfigProfile,
    ExecutionConfig,
    LogLevel,
    MetricsConfig,
    OrchestrationConfig,
)


class TestCacheConfig(unittest.TestCase):
    def test_default_config(self):
        config = CacheConfig()
        self.assertTrue(config.enabled)
        self.assertEqual(config.max_size, 1000)
        self.assertEqual(config.ttl, 3600)
        self.assertEqual(config.backend, "memory")

    def test_custom_config(self):
        config = CacheConfig(
            enabled=False,
            max_size=500,
            ttl=1800,
            backend="redis"
        )
        self.assertFalse(config.enabled)
        self.assertEqual(config.max_size, 500)
        self.assertEqual(config.ttl, 1800)
        self.assertEqual(config.backend, "redis")

    def test_invalid_max_size(self):
        with self.assertRaises(ValueError):
            CacheConfig(max_size=0)

        with self.assertRaises(ValueError):
            CacheConfig(max_size=-1)

    def test_invalid_ttl(self):
        with self.assertRaises(ValueError):
            CacheConfig(ttl=-1)


class TestMetricsConfig(unittest.TestCase):
    def test_default_config(self):
        config = MetricsConfig()
        self.assertTrue(config.enabled)
        self.assertEqual(config.export_interval, 60)
        self.assertEqual(config.export_format, "json")
        self.assertTrue(config.include_labels)

    def test_custom_config(self):
        config = MetricsConfig(
            enabled=False,
            export_interval=30,
            export_format="prometheus",
            include_labels=False
        )
        self.assertFalse(config.enabled)
        self.assertEqual(config.export_interval, 30)
        self.assertEqual(config.export_format, "prometheus")
        self.assertFalse(config.include_labels)

    def test_invalid_export_interval(self):
        with self.assertRaises(ValueError):
            MetricsConfig(export_interval=0)


class TestExecutionConfig(unittest.TestCase):
    def test_default_config(self):
        config = ExecutionConfig()
        self.assertEqual(config.max_iterations, 10)
        self.assertEqual(config.timeout, 300)
        self.assertEqual(config.retry_attempts, 3)
        self.assertEqual(config.retry_delay, 1.0)
        self.assertEqual(config.parallel_workers, 4)

    def test_custom_config(self):
        config = ExecutionConfig(
            max_iterations=20,
            timeout=600,
            retry_attempts=5,
            retry_delay=2.0,
            parallel_workers=8
        )
        self.assertEqual(config.max_iterations, 20)
        self.assertEqual(config.timeout, 600)
        self.assertEqual(config.retry_attempts, 5)
        self.assertEqual(config.retry_delay, 2.0)
        self.assertEqual(config.parallel_workers, 8)

    def test_invalid_values(self):
        with self.assertRaises(ValueError):
            ExecutionConfig(max_iterations=0)

        with self.assertRaises(ValueError):
            ExecutionConfig(timeout=-1)

        with self.assertRaises(ValueError):
            ExecutionConfig(retry_attempts=0)


class TestOrchestrationConfig(unittest.TestCase):
    def test_default_config(self):
        config = OrchestrationConfig()
        self.assertEqual(config.profile, ConfigProfile.DEVELOPMENT)
        self.assertEqual(config.log_level, LogLevel.INFO)
        self.assertFalse(config.debug)
        self.assertIsInstance(config.cache, CacheConfig)
        self.assertIsInstance(config.metrics, MetricsConfig)
        self.assertIsInstance(config.execution, ExecutionConfig)

    def test_custom_config(self):
        config = OrchestrationConfig(
            profile=ConfigProfile.PRODUCTION,
            log_level=LogLevel.ERROR,
            debug=True,
            cache=CacheConfig(enabled=False),
            custom={"key": "value"}
        )
        self.assertEqual(config.profile, ConfigProfile.PRODUCTION)
        self.assertEqual(config.log_level, LogLevel.ERROR)
        self.assertTrue(config.debug)
        self.assertFalse(config.cache.enabled)
        self.assertEqual(config.custom["key"], "value")

    def test_from_env(self):
        os.environ["PYGEAI_PROFILE"] = "production"
        os.environ["PYGEAI_LOG_LEVEL"] = "ERROR"
        os.environ["PYGEAI_DEBUG"] = "true"
        os.environ["PYGEAI_CACHE_ENABLED"] = "false"
        os.environ["PYGEAI_CACHE_MAX_SIZE"] = "2000"
        os.environ["PYGEAI_MAX_ITERATIONS"] = "15"

        try:
            config = OrchestrationConfig.from_env()
            self.assertEqual(config.profile, ConfigProfile.PRODUCTION)
            self.assertEqual(config.log_level, LogLevel.ERROR)
            self.assertTrue(config.debug)
            self.assertFalse(config.cache.enabled)
            self.assertEqual(config.cache.max_size, 2000)
            self.assertEqual(config.execution.max_iterations, 15)
        finally:
            del os.environ["PYGEAI_PROFILE"]
            del os.environ["PYGEAI_LOG_LEVEL"]
            del os.environ["PYGEAI_DEBUG"]
            del os.environ["PYGEAI_CACHE_ENABLED"]
            del os.environ["PYGEAI_CACHE_MAX_SIZE"]
            del os.environ["PYGEAI_MAX_ITERATIONS"]

    def test_from_file(self):
        config_data = {
            "profile": "production",
            "log_level": "WARNING",
            "debug": False,
            "cache": {"enabled": True, "max_size": 500},
            "metrics": {"enabled": False},
            "execution": {"max_iterations": 25}
        }

        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump(config_data, f)
            temp_path = f.name

        try:
            config = OrchestrationConfig.from_file(temp_path)
            self.assertEqual(config.profile, ConfigProfile.PRODUCTION)
            self.assertEqual(config.log_level, LogLevel.WARNING)
            self.assertFalse(config.debug)
            self.assertEqual(config.cache.max_size, 500)
            self.assertFalse(config.metrics.enabled)
            self.assertEqual(config.execution.max_iterations, 25)
        finally:
            os.unlink(temp_path)

    def test_from_file_not_found(self):
        with self.assertRaises(FileNotFoundError):
            OrchestrationConfig.from_file("nonexistent.json")

    def test_to_file(self):
        config = OrchestrationConfig(
            profile=ConfigProfile.TEST,
            log_level=LogLevel.DEBUG
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            path = Path(tmpdir) / "config.json"
            config.to_file(path)

            self.assertTrue(path.exists())

            loaded = OrchestrationConfig.from_file(path)
            self.assertEqual(loaded.profile, ConfigProfile.TEST)
            self.assertEqual(loaded.log_level, LogLevel.DEBUG)

    def test_merge(self):
        base = OrchestrationConfig(
            profile=ConfigProfile.DEVELOPMENT,
            cache=CacheConfig(max_size=500)
        )

        override = OrchestrationConfig(
            log_level=LogLevel.ERROR,
            cache=CacheConfig(enabled=False)
        )

        merged = base.merge(override)

        self.assertEqual(merged.profile, ConfigProfile.DEVELOPMENT)
        self.assertEqual(merged.log_level, LogLevel.ERROR)
        self.assertFalse(merged.cache.enabled)

    def test_production_profile(self):
        config = OrchestrationConfig(profile=ConfigProfile.PRODUCTION)
        profile_config = config.get_profile_config()

        self.assertEqual(profile_config.log_level, LogLevel.WARNING)
        self.assertFalse(profile_config.debug)
        self.assertEqual(profile_config.execution.max_iterations, 20)

    def test_test_profile(self):
        config = OrchestrationConfig(profile=ConfigProfile.TEST)
        profile_config = config.get_profile_config()

        self.assertEqual(profile_config.log_level, LogLevel.DEBUG)
        self.assertTrue(profile_config.debug)
        self.assertFalse(profile_config.cache.enabled)
        self.assertFalse(profile_config.metrics.enabled)
        self.assertEqual(profile_config.execution.max_iterations, 5)


class TestConfigManager(unittest.TestCase):
    def tearDown(self):
        ConfigManager.reset()

    def test_singleton(self):
        manager1 = ConfigManager()
        manager2 = ConfigManager()
        self.assertIs(manager1, manager2)

    def test_initialize_default(self):
        manager = ConfigManager.initialize(from_env=False)
        config = manager.get_config()

        self.assertIsInstance(config, OrchestrationConfig)
        self.assertEqual(config.profile, ConfigProfile.DEVELOPMENT)

    def test_initialize_with_config(self):
        custom_config = OrchestrationConfig(
            profile=ConfigProfile.PRODUCTION,
            log_level=LogLevel.ERROR
        )

        manager = ConfigManager.initialize(config=custom_config)
        config = manager.get_config()

        self.assertEqual(config.log_level, LogLevel.ERROR)

    def test_initialize_from_file(self):
        config_data = {"profile": "test", "log_level": "DEBUG"}

        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump(config_data, f)
            temp_path = f.name

        try:
            manager = ConfigManager.initialize(config_file=temp_path)
            config = manager.get_config()

            self.assertEqual(config.profile, ConfigProfile.TEST)
            self.assertEqual(config.log_level, LogLevel.DEBUG)
        finally:
            os.unlink(temp_path)

    def test_set_config(self):
        manager = ConfigManager.initialize(from_env=False)

        new_config = OrchestrationConfig(log_level=LogLevel.CRITICAL)
        manager.set_config(new_config)

        config = manager.get_config()
        self.assertEqual(config.log_level, LogLevel.CRITICAL)

    def test_reset(self):
        manager = ConfigManager.initialize(from_env=False)
        manager.reset()

        config = manager.get_config()
        self.assertIsInstance(config, OrchestrationConfig)


if __name__ == "__main__":
    unittest.main()
