"""Tests for KV Cache access statistics collector"""

from __future__ import annotations

import json
import tempfile
import time
from pathlib import Path

from sagellm_kv_cache.profiling import AccessStatsCollector


class TestAccessStatsCollector:
    """测试 AccessStatsCollector 类"""

    def test_init(self) -> None:
        """测试初始化"""
        collector = AccessStatsCollector()
        assert collector.total_accesses == 0
        assert collector.cache_hits == 0
        assert collector.cache_misses == 0
        assert len(collector.access_times) == 0
        assert len(collector.access_count) == 0

    def test_record_access_hit(self) -> None:
        """测试记录命中访问"""
        collector = AccessStatsCollector()
        collector.record_access("block_1", is_hit=True)

        assert collector.total_accesses == 1
        assert collector.cache_hits == 1
        assert collector.cache_misses == 0
        assert collector.access_count["block_1"] == 1
        assert len(collector.access_times["block_1"]) == 1

    def test_record_access_miss(self) -> None:
        """测试记录未命中访问"""
        collector = AccessStatsCollector()
        collector.record_access("block_1", is_hit=False)

        assert collector.total_accesses == 1
        assert collector.cache_hits == 0
        assert collector.cache_misses == 1

    def test_multiple_accesses(self) -> None:
        """测试多次访问"""
        collector = AccessStatsCollector()

        for i in range(10):
            collector.record_access(f"block_{i % 3}", is_hit=True)
            time.sleep(0.001)  # 确保时间间隔

        assert collector.total_accesses == 10
        assert len(collector.access_count) == 3
        assert collector.access_count["block_0"] == 4
        assert collector.access_count["block_1"] == 3
        assert collector.access_count["block_2"] == 3

    def test_get_access_intervals(self) -> None:
        """测试获取访问时间间隔"""
        collector = AccessStatsCollector()

        # 单次访问，无间隔
        collector.record_access("block_1", is_hit=True)
        intervals = collector.get_access_intervals("block_1")
        assert intervals == []

        # 多次访问
        time.sleep(0.01)
        collector.record_access("block_1", is_hit=True)
        time.sleep(0.01)
        collector.record_access("block_1", is_hit=True)

        intervals = collector.get_access_intervals("block_1")
        assert len(intervals) == 2
        assert all(interval > 0 for interval in intervals)

    def test_get_all_intervals(self) -> None:
        """测试获取所有访问间隔"""
        collector = AccessStatsCollector()

        for _ in range(5):
            collector.record_access("block_1", is_hit=True)
            time.sleep(0.001)

        for _ in range(3):
            collector.record_access("block_2", is_hit=True)
            time.sleep(0.001)

        all_intervals = collector.get_all_intervals()
        assert len(all_intervals) == 6  # 4 + 2

    def test_get_hit_rate(self) -> None:
        """测试命中率计算"""
        collector = AccessStatsCollector()

        # 无访问时命中率为 0
        assert collector.get_hit_rate() == 0.0

        # 记录一些访问
        for _ in range(8):
            collector.record_access("block_1", is_hit=True)

        for _ in range(2):
            collector.record_access("block_2", is_hit=False)

        assert collector.get_hit_rate() == 0.8

    def test_get_top_blocks(self) -> None:
        """测试获取访问频率最高的 block"""
        collector = AccessStatsCollector()

        # 生成不同频率的访问
        for i in range(1, 6):
            for _ in range(i):
                collector.record_access(f"block_{i}", is_hit=True)

        top_3 = collector.get_top_blocks(3)
        assert len(top_3) == 3
        assert top_3[0][0] == "block_5"
        assert top_3[0][1] == 5
        assert top_3[1][0] == "block_4"
        assert top_3[1][1] == 4

    def test_get_stats_summary(self) -> None:
        """测试统计摘要"""
        collector = AccessStatsCollector()

        for i in range(10):
            collector.record_access(f"block_{i % 3}", is_hit=i % 2 == 0)
            time.sleep(0.001)

        summary = collector.get_stats_summary()

        assert summary["total_accesses"] == 10
        assert summary["unique_blocks"] == 3
        assert "hit_rate" in summary
        assert "cache_hits" in summary
        assert "cache_misses" in summary
        assert "average_access_interval" in summary
        assert "duration_seconds" in summary
        assert "top_10_blocks" in summary

    def test_export_stats(self) -> None:
        """测试导出统计数据"""
        collector = AccessStatsCollector()

        for i in range(5):
            collector.record_access(f"block_{i % 2}", is_hit=True)
            time.sleep(0.001)

        with tempfile.TemporaryDirectory() as tmpdir:
            output_path = Path(tmpdir) / "test_stats.json"
            collector.export_stats(output_path)

            assert output_path.exists()

            # 验证 JSON 内容
            with open(output_path, encoding="utf-8") as f:
                data = json.load(f)

            assert data["total_accesses"] == 5
            assert data["unique_blocks"] == 2
            assert "access_intervals" in data
            assert "block_details" in data

    def test_reset(self) -> None:
        """测试重置功能"""
        collector = AccessStatsCollector()

        for i in range(10):
            collector.record_access(f"block_{i}", is_hit=True)

        assert collector.total_accesses == 10

        collector.reset()

        assert collector.total_accesses == 0
        assert collector.cache_hits == 0
        assert collector.cache_misses == 0
        assert len(collector.access_times) == 0
        assert len(collector.access_count) == 0

    def test_block_details_export(self) -> None:
        """测试 block 详细信息的导出"""
        collector = AccessStatsCollector()

        # 模拟多次访问
        collector.record_access("block_1", is_hit=True)
        time.sleep(0.01)
        collector.record_access("block_1", is_hit=True)
        time.sleep(0.01)
        collector.record_access("block_2", is_hit=False)

        with tempfile.TemporaryDirectory() as tmpdir:
            output_path = Path(tmpdir) / "test_details.json"
            collector.export_stats(output_path)

            with open(output_path, encoding="utf-8") as f:
                data = json.load(f)

            assert "block_1" in data["block_details"]
            assert "block_2" in data["block_details"]

            block_1_details = data["block_details"]["block_1"]
            assert block_1_details["access_count"] == 2
            assert "first_access" in block_1_details
            assert "last_access" in block_1_details
            assert "intervals" in block_1_details
            assert len(block_1_details["intervals"]) == 1
