import time
import unittest

from pygeai_orchestration.core.utils.cache import CacheEntry, LRUCache, PatternCache


class TestCacheEntry(unittest.TestCase):
    def test_cache_entry_creation(self):
        entry = CacheEntry("test_value")
        self.assertEqual(entry.value, "test_value")
        self.assertEqual(entry.access_count, 0)
        self.assertIsNotNone(entry.created_at)
        self.assertIsNone(entry.ttl)

    def test_cache_entry_with_ttl(self):
        entry = CacheEntry("test_value", ttl=1.0)
        self.assertEqual(entry.ttl, 1.0)
        self.assertFalse(entry.is_expired())

    def test_cache_entry_expiration(self):
        entry = CacheEntry("test_value", ttl=0.1)
        self.assertFalse(entry.is_expired())
        time.sleep(0.2)
        self.assertTrue(entry.is_expired())

    def test_cache_entry_access(self):
        entry = CacheEntry("test_value")
        self.assertEqual(entry.access_count, 0)
        value = entry.access()
        self.assertEqual(value, "test_value")
        self.assertEqual(entry.access_count, 1)
        entry.access()
        self.assertEqual(entry.access_count, 2)


class TestLRUCache(unittest.TestCase):
    def setUp(self):
        self.cache = LRUCache(max_size=3)

    def test_cache_creation(self):
        cache = LRUCache(max_size=10, default_ttl=60.0)
        self.assertEqual(cache.max_size, 10)
        self.assertEqual(cache.default_ttl, 60.0)
        self.assertEqual(cache.size(), 0)

    def test_set_and_get(self):
        self.cache.set("key1", "value1")
        value = self.cache.get("key1")
        self.assertEqual(value, "value1")

    def test_cache_miss(self):
        value = self.cache.get("nonexistent")
        self.assertIsNone(value)

    def test_lru_eviction(self):
        self.cache.set("key1", "value1")
        self.cache.set("key2", "value2")
        self.cache.set("key3", "value3")
        self.cache.set("key4", "value4")

        self.assertIsNone(self.cache.get("key1"))
        self.assertEqual(self.cache.get("key2"), "value2")
        self.assertEqual(self.cache.get("key3"), "value3")
        self.assertEqual(self.cache.get("key4"), "value4")

    def test_lru_ordering(self):
        self.cache.set("key1", "value1")
        self.cache.set("key2", "value2")
        self.cache.set("key3", "value3")

        self.cache.get("key1")

        self.cache.set("key4", "value4")

        self.assertIsNone(self.cache.get("key2"))
        self.assertEqual(self.cache.get("key1"), "value1")

    def test_ttl_expiration(self):
        cache = LRUCache(max_size=10, default_ttl=0.1)
        cache.set("key1", "value1")
        self.assertEqual(cache.get("key1"), "value1")

        time.sleep(0.2)
        self.assertIsNone(cache.get("key1"))

    def test_custom_ttl(self):
        self.cache.set("key1", "value1", ttl=0.1)
        self.cache.set("key2", "value2", ttl=10.0)

        time.sleep(0.2)
        self.assertIsNone(self.cache.get("key1"))
        self.assertEqual(self.cache.get("key2"), "value2")

    def test_invalidate(self):
        self.cache.set("key1", "value1")
        self.assertTrue(self.cache.invalidate("key1"))
        self.assertIsNone(self.cache.get("key1"))
        self.assertFalse(self.cache.invalidate("key1"))

    def test_clear(self):
        self.cache.set("key1", "value1")
        self.cache.set("key2", "value2")
        self.assertEqual(self.cache.size(), 2)

        self.cache.clear()
        self.assertEqual(self.cache.size(), 0)
        self.assertIsNone(self.cache.get("key1"))

    def test_cache_stats(self):
        self.cache.get("miss1")
        self.cache.get("miss2")

        self.cache.set("key1", "value1")
        self.cache.get("key1")
        self.cache.get("key1")

        stats = self.cache.get_stats()
        self.assertEqual(stats["hits"], 2)
        self.assertEqual(stats["misses"], 2)
        self.assertEqual(stats["total_requests"], 4)
        self.assertEqual(stats["hit_rate"], 50.0)

    def test_complex_key(self):
        key = {"param1": "value1", "param2": [1, 2, 3]}
        self.cache.set(key, "result")
        value = self.cache.get(key)
        self.assertEqual(value, "result")

    def test_key_generation_consistency(self):
        key1 = {"b": 2, "a": 1}
        key2 = {"a": 1, "b": 2}

        self.cache.set(key1, "value1")
        value = self.cache.get(key2)
        self.assertEqual(value, "value1")


class TestPatternCache(unittest.TestCase):
    def setUp(self):
        PatternCache.reset()

    def tearDown(self):
        PatternCache.reset()

    def test_singleton_pattern(self):
        cache1 = PatternCache()
        cache2 = PatternCache()
        self.assertIs(cache1, cache2)

    def test_initialize(self):
        pattern_cache = PatternCache()
        pattern_cache.initialize(max_size=50, default_ttl=120.0)

        cache = pattern_cache.get_cache()
        self.assertEqual(cache.max_size, 50)
        self.assertEqual(cache.default_ttl, 120.0)

    def test_auto_initialize(self):
        pattern_cache = PatternCache()
        cache = pattern_cache.get_cache()
        self.assertIsNotNone(cache)
        self.assertEqual(cache.max_size, 100)

    def test_clear(self):
        pattern_cache = PatternCache()
        cache = pattern_cache.get_cache()
        cache.set("key1", "value1")

        pattern_cache.clear()
        self.assertEqual(cache.size(), 0)

    def test_reset(self):
        pattern_cache1 = PatternCache()
        pattern_cache1.initialize(max_size=10)

        PatternCache.reset()

        pattern_cache2 = PatternCache()
        cache = pattern_cache2.get_cache()
        self.assertEqual(cache.max_size, 100)


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