"""
Unit tests for ApplicationConfig class.

This module tests the ApplicationConfig which manages application-level
settings like max_parallel_tasks, task fetch interval, and debug mode.
"""

import unittest

from pydantic import ValidationError

from data_exchange_agent.config.sections.application import ApplicationConfig


class TestApplicationConfig(unittest.TestCase):
    """Test suite for ApplicationConfig class."""

    def test_initialization_with_all_none(self):
        """Test ApplicationConfig initialization with all None values."""
        config = ApplicationConfig()

        self.assertIsNone(config.max_parallel_tasks)
        self.assertIsNone(config.task_fetch_interval)
        self.assertIsNone(config.debug_mode)

    def test_initialization_with_values(self):
        """Test ApplicationConfig initialization with actual values."""
        config = ApplicationConfig(max_parallel_tasks=8, task_fetch_interval=60, debug_mode=True)

        self.assertEqual(config.max_parallel_tasks, 8)
        self.assertEqual(config.task_fetch_interval, 60)
        self.assertTrue(config.debug_mode)

    def test_initialization_with_partial_values(self):
        """Test ApplicationConfig initialization with some None values."""
        config = ApplicationConfig(max_parallel_tasks=4, task_fetch_interval=None, debug_mode=False)

        self.assertEqual(config.max_parallel_tasks, 4)
        self.assertIsNone(config.task_fetch_interval)
        self.assertFalse(config.debug_mode)

    def test_valid_max_parallel_tasks_value(self):
        """Test ApplicationConfig with valid max_parallel_tasks value."""
        config = ApplicationConfig(max_parallel_tasks=10)

        self.assertEqual(config.max_parallel_tasks, 10)

    def test_max_parallel_tasks_minimum_boundary(self):
        """Test max_parallel_tasks at minimum valid boundary (1)."""
        config = ApplicationConfig(max_parallel_tasks=1)

        self.assertEqual(config.max_parallel_tasks, 1)

    def test_max_parallel_tasks_maximum_boundary(self):
        """Test max_parallel_tasks at maximum valid boundary (100)."""
        config = ApplicationConfig(max_parallel_tasks=100)

        self.assertEqual(config.max_parallel_tasks, 100)

    def test_max_parallel_tasks_below_minimum_raises_error(self):
        """Test that max_parallel_tasks below 1 raises ValidationError."""
        with self.assertRaises(ValidationError) as context:
            ApplicationConfig(max_parallel_tasks=0)

        errors = context.exception.errors()
        self.assertEqual(len(errors), 1)
        self.assertIn("max_parallel_tasks", errors[0]["loc"])

    def test_max_parallel_tasks_negative_raises_error(self):
        """Test that negative max_parallel_tasks raises ValidationError."""
        with self.assertRaises(ValidationError) as context:
            ApplicationConfig(max_parallel_tasks=-5)

        errors = context.exception.errors()
        self.assertEqual(len(errors), 1)
        self.assertIn("max_parallel_tasks", errors[0]["loc"])

    def test_max_parallel_tasks_above_maximum_raises_error(self):
        """Test that max_parallel_tasks above 100 raises ValidationError."""
        with self.assertRaises(ValidationError) as context:
            ApplicationConfig(max_parallel_tasks=101)

        errors = context.exception.errors()
        self.assertEqual(len(errors), 1)
        self.assertIn("max_parallel_tasks", errors[0]["loc"])

    def test_max_parallel_tasks_invalid_type_raises_error(self):
        """Test that non-integer max_parallel_tasks raises ValidationError."""
        with self.assertRaises(ValidationError):
            ApplicationConfig(max_parallel_tasks="not_an_int")

    def test_valid_task_fetch_interval(self):
        """Test ApplicationConfig with valid task_fetch_interval."""
        config = ApplicationConfig(task_fetch_interval=120)

        self.assertEqual(config.task_fetch_interval, 120)

    def test_task_fetch_interval_minimum_boundary(self):
        """Test task_fetch_interval at minimum valid boundary (1)."""
        config = ApplicationConfig(task_fetch_interval=1)

        self.assertEqual(config.task_fetch_interval, 1)

    def test_task_fetch_interval_below_minimum_raises_error(self):
        """Test that task_fetch_interval below 1 raises ValidationError."""
        with self.assertRaises(ValidationError) as context:
            ApplicationConfig(task_fetch_interval=0)

        errors = context.exception.errors()
        self.assertEqual(len(errors), 1)
        self.assertIn("task_fetch_interval", errors[0]["loc"])

    def test_task_fetch_interval_negative_raises_error(self):
        """Test that negative task_fetch_interval raises ValidationError."""
        with self.assertRaises(ValidationError) as context:
            ApplicationConfig(task_fetch_interval=-10)

        errors = context.exception.errors()
        self.assertEqual(len(errors), 1)
        self.assertIn("task_fetch_interval", errors[0]["loc"])

    def test_task_fetch_interval_invalid_type_raises_error(self):
        """Test that non-integer task_fetch_interval raises ValidationError."""
        with self.assertRaises(ValidationError):
            ApplicationConfig(task_fetch_interval="not_an_int")

    def test_valid_debug_mode_true(self):
        """Test ApplicationConfig with debug_mode=True."""
        config = ApplicationConfig(debug_mode=True)

        self.assertTrue(config.debug_mode)

    def test_valid_debug_mode_false(self):
        """Test ApplicationConfig with debug_mode=False."""
        config = ApplicationConfig(debug_mode=False)

        self.assertFalse(config.debug_mode)

    def test_debug_mode_invalid_type_raises_error(self):
        """Test that non-boolean debug_mode raises ValidationError."""
        with self.assertRaises(ValidationError):
            ApplicationConfig(debug_mode="not_a_bool")

    def test_multiple_valid_parameters(self):
        """Test ApplicationConfig with multiple valid parameters."""
        config = ApplicationConfig(max_parallel_tasks=25, task_fetch_interval=90, debug_mode=True)

        self.assertEqual(config.max_parallel_tasks, 25)
        self.assertEqual(config.task_fetch_interval, 90)
        self.assertTrue(config.debug_mode)

    def test_repr(self):
        """Test string representation of ApplicationConfig."""
        config = ApplicationConfig(max_parallel_tasks=4, task_fetch_interval=120, debug_mode=False)

        repr_str = repr(config)

        self.assertIn("ApplicationConfig", repr_str)
        self.assertIn("max_parallel_tasks=4", repr_str)
        self.assertIn("task_fetch_interval=120", repr_str)
        self.assertIn("debug_mode=False", repr_str)

    def test_repr_with_none_values(self):
        """Test repr with None values."""
        config = ApplicationConfig(max_parallel_tasks=None, task_fetch_interval=None, debug_mode=None)

        repr_str = repr(config)

        self.assertIn("ApplicationConfig", repr_str)
        self.assertIn("max_parallel_tasks=None", repr_str)
        self.assertIn("task_fetch_interval=None", repr_str)
        self.assertIn("debug_mode=None", repr_str)

    def test_getitem_access(self):
        """Test dictionary-style access via __getitem__."""
        config = ApplicationConfig(max_parallel_tasks=8, task_fetch_interval=60, debug_mode=True)

        self.assertEqual(config["max_parallel_tasks"], 8)
        self.assertEqual(config["task_fetch_interval"], 60)
        self.assertTrue(config["debug_mode"])

    def test_getitem_with_missing_attribute(self):
        """Test __getitem__ raises KeyError for missing attributes."""
        config = ApplicationConfig()

        with self.assertRaises(KeyError):
            _ = config["nonexistent"]

    def test_inherits_from_base_section_config(self):
        """Test that ApplicationConfig inherits from BaseConfig."""
        from data_exchange_agent.config.sections.base_section_config import BaseSectionConfig

        config = ApplicationConfig()

        self.assertIsInstance(config, BaseSectionConfig)

    def test_none_values_pass_validation(self):
        """Test that None values don't trigger validation errors."""
        # This should not raise any errors
        config = ApplicationConfig(max_parallel_tasks=None, task_fetch_interval=None, debug_mode=None)

        self.assertIsNone(config.max_parallel_tasks)
        self.assertIsNone(config.task_fetch_interval)
        self.assertIsNone(config.debug_mode)

    def test_affinity_validation(self):
        """Test affinity field validation."""
        # Valid affinity
        config = ApplicationConfig(affinity="blue")
        self.assertEqual(config.affinity, "blue")

        # Whitespace-only affinity should fail
        with self.assertRaises(ValidationError):
            ApplicationConfig(affinity="   ")


class TestApplicationConfigEdgeCases(unittest.TestCase):
    """Test edge cases for ApplicationConfig."""

    def test_max_parallel_tasks_with_zero(self):
        """Test max_parallel_tasks value of exactly 0."""
        with self.assertRaises(ValidationError):
            ApplicationConfig(max_parallel_tasks=0)

    def test_max_parallel_tasks_large_valid_value(self):
        """Test max_parallel_tasks with large valid value."""
        config = ApplicationConfig(max_parallel_tasks=99)

        self.assertEqual(config.max_parallel_tasks, 99)

    def test_task_fetch_interval_large_value(self):
        """Test task_fetch_interval with very large value."""
        config = ApplicationConfig(task_fetch_interval=86400)  # 24 hours

        self.assertEqual(config.task_fetch_interval, 86400)

    def test_multiple_invalid_parameters(self):
        """Test that multiple invalid parameters are all reported."""
        with self.assertRaises(ValidationError) as context:
            ApplicationConfig(max_parallel_tasks=-1, task_fetch_interval=-1)

        # Pydantic collects all validation errors
        errors = context.exception.errors()
        self.assertGreaterEqual(len(errors), 2)

    def test_agent_id_is_auto_generated(self):
        """Test that agent_id is auto-generated."""
        config1 = ApplicationConfig()
        config2 = ApplicationConfig()

        # Both should have agent IDs
        self.assertIsNotNone(config1.agent_id)
        self.assertIsNotNone(config2.agent_id)

        # They should be different UUIDs
        self.assertNotEqual(config1.agent_id, config2.agent_id)


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