import time
import unittest

from pygeai_orchestration.core.utils.metrics import (
    GlobalMetrics,
    Metric,
    MetricsCollector,
)


class TestMetricsCollector(unittest.TestCase):
    def setUp(self):
        self.collector = MetricsCollector()

    def test_increment_counter(self):
        self.collector.increment("requests")
        self.assertEqual(self.collector.get_counter("requests"), 1.0)

        self.collector.increment("requests", 2.0)
        self.assertEqual(self.collector.get_counter("requests"), 3.0)

    def test_counter_with_labels(self):
        self.collector.increment("requests", labels={"method": "GET"})
        self.collector.increment("requests", labels={"method": "POST"})
        self.collector.increment("requests", labels={"method": "GET"})

        self.assertEqual(
            self.collector.get_counter("requests", labels={"method": "GET"}), 2.0
        )
        self.assertEqual(
            self.collector.get_counter("requests", labels={"method": "POST"}), 1.0
        )

    def test_set_gauge(self):
        self.collector.set_gauge("memory_usage", 1024.0)
        self.assertEqual(self.collector.get_gauge("memory_usage"), 1024.0)

        self.collector.set_gauge("memory_usage", 2048.0)
        self.assertEqual(self.collector.get_gauge("memory_usage"), 2048.0)

    def test_gauge_with_labels(self):
        self.collector.set_gauge("cpu_usage", 50.0, labels={"core": "0"})
        self.collector.set_gauge("cpu_usage", 75.0, labels={"core": "1"})

        self.assertEqual(
            self.collector.get_gauge("cpu_usage", labels={"core": "0"}), 50.0
        )
        self.assertEqual(
            self.collector.get_gauge("cpu_usage", labels={"core": "1"}), 75.0
        )

    def test_record_histogram(self):
        values = [10.0, 20.0, 30.0, 40.0, 50.0]
        for value in values:
            self.collector.record_histogram("response_size", value)

        stats = self.collector.get_histogram_stats("response_size")
        self.assertEqual(stats["count"], 5)
        self.assertEqual(stats["sum"], 150.0)
        self.assertEqual(stats["min"], 10.0)
        self.assertEqual(stats["max"], 50.0)
        self.assertEqual(stats["mean"], 30.0)

    def test_record_timer(self):
        durations = [0.1, 0.2, 0.3, 0.4, 0.5]
        for duration in durations:
            self.collector.record_timer("request_duration", duration)

        stats = self.collector.get_timer_stats("request_duration")
        self.assertEqual(stats["count"], 5)
        self.assertAlmostEqual(stats["sum"], 1.5, places=1)
        self.assertEqual(stats["min"], 0.1)
        self.assertEqual(stats["max"], 0.5)

    def test_timer_context(self):
        with self.collector.timer("operation_duration"):
            time.sleep(0.01)

        stats = self.collector.get_timer_stats("operation_duration")
        self.assertEqual(stats["count"], 1)
        self.assertGreater(stats["min"], 0.0)

    def test_timer_context_with_labels(self):
        with self.collector.timer("db_query", labels={"table": "users"}):
            time.sleep(0.01)

        stats = self.collector.get_timer_stats("db_query", labels={"table": "users"})
        self.assertEqual(stats["count"], 1)

    def test_percentiles(self):
        values = list(range(1, 101))
        for value in values:
            self.collector.record_histogram("test_percentiles", float(value))

        stats = self.collector.get_histogram_stats("test_percentiles")
        self.assertAlmostEqual(stats["p50"], 50.0, delta=5.0)
        self.assertAlmostEqual(stats["p95"], 95.0, delta=5.0)
        self.assertAlmostEqual(stats["p99"], 99.0, delta=5.0)

    def test_get_all_metrics(self):
        self.collector.increment("counter1")
        self.collector.set_gauge("gauge1", 100.0)
        self.collector.record_histogram("histogram1", 50.0)

        metrics = self.collector.get_all_metrics()
        self.assertGreaterEqual(len(metrics), 3)
        self.assertTrue(all(isinstance(m, Metric) for m in metrics))

    def test_get_summary(self):
        self.collector.increment("requests")
        self.collector.set_gauge("memory", 1024.0)
        self.collector.record_histogram("sizes", 100.0)
        self.collector.record_timer("durations", 0.5)

        summary = self.collector.get_summary()
        self.assertIn("counters", summary)
        self.assertIn("gauges", summary)
        self.assertIn("histograms", summary)
        self.assertIn("timers", summary)

    def test_clear(self):
        self.collector.increment("counter")
        self.collector.set_gauge("gauge", 100.0)
        self.collector.record_histogram("histogram", 50.0)

        self.collector.clear()

        self.assertEqual(self.collector.get_counter("counter"), 0.0)
        self.assertIsNone(self.collector.get_gauge("gauge"))
        self.assertEqual(len(self.collector.get_all_metrics()), 0)

    def test_empty_histogram_stats(self):
        stats = self.collector.get_histogram_stats("nonexistent")
        self.assertEqual(stats, {})

    def test_empty_timer_stats(self):
        stats = self.collector.get_timer_stats("nonexistent")
        self.assertEqual(stats, {})


class TestGlobalMetrics(unittest.TestCase):
    def setUp(self):
        GlobalMetrics.reset()

    def tearDown(self):
        GlobalMetrics.reset()

    def test_singleton(self):
        collector1 = GlobalMetrics.get_collector()
        collector2 = GlobalMetrics.get_collector()
        self.assertIs(collector1, collector2)

    def test_global_metrics_persistence(self):
        collector1 = GlobalMetrics.get_collector()
        collector1.increment("global_counter")

        collector2 = GlobalMetrics.get_collector()
        self.assertEqual(collector2.get_counter("global_counter"), 1.0)

    def test_reset(self):
        collector1 = GlobalMetrics.get_collector()
        collector1.increment("counter")

        GlobalMetrics.reset()

        collector2 = GlobalMetrics.get_collector()
        self.assertEqual(collector2.get_counter("counter"), 0.0)


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