"""
tests/test_debug.py — Tests for the debug logging module.

Covers:
  - Ring buffer append / eviction / query / clear
  - Query filters (session_id, endpoint, last, min_latency_ms, event_type)
  - Stats computation
  - Disabled = no-op behavior
  - Config integration
  - Debug routes (via buffer directly)
"""

from __future__ import annotations

import time
import pytest

from cortex.debug import (
    DebugLogEntry,
    DebugRingBuffer,
    get_debug_buffer,
    init_debug_logging,
    is_debug_enabled,
    set_debug_enabled,
    log_extraction_event,
    log_retrieval_event,
)


# ---------------------------------------------------------------------------
# Ring Buffer Unit Tests
# ---------------------------------------------------------------------------

class TestDebugRingBuffer:
    """Tests for the DebugRingBuffer class."""

    def test_append_and_len(self):
        buf = DebugRingBuffer(max_entries=100)
        assert len(buf) == 0
        buf.append(DebugLogEntry(timestamp=time.time(), event_type="http_request"))
        assert len(buf) == 1

    def test_max_size_eviction(self):
        """Ring buffer evicts oldest entries when full."""
        buf = DebugRingBuffer(max_entries=5)
        for i in range(10):
            buf.append(DebugLogEntry(
                timestamp=float(i),
                event_type="http_request",
                data={"index": i},
            ))
        assert len(buf) == 5
        entries = buf.query(last=10)
        indices = [e["index"] for e in entries]
        assert indices == [9, 8, 7, 6, 5]

    def test_clear(self):
        buf = DebugRingBuffer(max_entries=100)
        for i in range(5):
            buf.append(DebugLogEntry(timestamp=float(i), event_type="http_request"))
        cleared = buf.clear()
        assert cleared == 5
        assert len(buf) == 0

    def test_query_last(self):
        buf = DebugRingBuffer(max_entries=100)
        for i in range(20):
            buf.append(DebugLogEntry(
                timestamp=float(i),
                event_type="http_request",
                data={"index": i},
            ))
        entries = buf.query(last=3)
        assert len(entries) == 3
        assert entries[0]["index"] == 19

    def test_query_filter_session_id(self):
        buf = DebugRingBuffer(max_entries=100)
        buf.append(DebugLogEntry(
            timestamp=1.0, event_type="http_request",
            data={"session_id": "abc", "path": "/api/v1/remember"},
        ))
        buf.append(DebugLogEntry(
            timestamp=2.0, event_type="http_request",
            data={"session_id": "xyz", "path": "/api/v1/retrieve"},
        ))
        buf.append(DebugLogEntry(
            timestamp=3.0, event_type="http_request",
            data={"session_id": "abc", "path": "/api/v1/health"},
        ))
        results = buf.query(session_id="abc")
        assert len(results) == 2
        assert all(e["session_id"] == "abc" for e in results)

    def test_query_filter_endpoint(self):
        buf = DebugRingBuffer(max_entries=100)
        buf.append(DebugLogEntry(
            timestamp=1.0, event_type="http_request",
            data={"path": "/api/v1/remember"},
        ))
        buf.append(DebugLogEntry(
            timestamp=2.0, event_type="http_request",
            data={"path": "/api/v1/retrieve"},
        ))
        results = buf.query(endpoint="/api/v1/remember")
        assert len(results) == 1
        assert results[0]["path"] == "/api/v1/remember"

    def test_query_filter_min_latency(self):
        buf = DebugRingBuffer(max_entries=100)
        buf.append(DebugLogEntry(
            timestamp=1.0, event_type="http_request",
            data={"latency_ms": 5.0},
        ))
        buf.append(DebugLogEntry(
            timestamp=2.0, event_type="http_request",
            data={"latency_ms": 150.0},
        ))
        buf.append(DebugLogEntry(
            timestamp=3.0, event_type="http_request",
            data={"latency_ms": 200.0},
        ))
        results = buf.query(min_latency_ms=100)
        assert len(results) == 2
        assert all(e["latency_ms"] >= 100 for e in results)

    def test_query_filter_event_type(self):
        buf = DebugRingBuffer(max_entries=100)
        buf.append(DebugLogEntry(timestamp=1.0, event_type="http_request"))
        buf.append(DebugLogEntry(timestamp=2.0, event_type="extraction"))
        buf.append(DebugLogEntry(timestamp=3.0, event_type="retrieval"))
        results = buf.query(event_type="extraction")
        assert len(results) == 1
        assert results[0]["event_type"] == "extraction"

    def test_stats_empty(self):
        buf = DebugRingBuffer(max_entries=100)
        stats = buf.stats()
        assert stats["total_entries"] == 0
        assert stats["http_requests"] == 0
        assert stats["pipeline_events"] == 0

    def test_stats_with_data(self):
        buf = DebugRingBuffer(max_entries=100)
        buf.append(DebugLogEntry(
            timestamp=1.0, event_type="http_request",
            data={"path": "/api/v1/remember", "latency_ms": 10.0, "status": 200},
        ))
        buf.append(DebugLogEntry(
            timestamp=2.0, event_type="http_request",
            data={"path": "/api/v1/remember", "latency_ms": 20.0, "status": 500},
        ))
        buf.append(DebugLogEntry(
            timestamp=3.0, event_type="http_request",
            data={"path": "/api/v1/retrieve", "latency_ms": 5.0, "status": 200},
        ))
        buf.append(DebugLogEntry(
            timestamp=4.0, event_type="extraction",
            data={"claims_extracted": 3, "claims_stored": 2},
        ))
        buf.append(DebugLogEntry(
            timestamp=5.0, event_type="retrieval",
            data={"items_returned": 5},
        ))
        stats = buf.stats()
        assert stats["total_entries"] == 5
        assert stats["http_requests"] == 3
        assert stats["pipeline_events"] == 2
        assert stats["error_count"] == 1
        assert stats["extraction_events"] == 1
        assert stats["retrieval_events"] == 1
        assert "/api/v1/remember" in stats["endpoints"]
        assert stats["endpoints"]["/api/v1/remember"]["count"] == 2
        assert stats["endpoints"]["/api/v1/remember"]["avg_latency_ms"] == 15.0

    def test_stats_extraction_success_rate(self):
        buf = DebugRingBuffer(max_entries=100)
        buf.append(DebugLogEntry(
            timestamp=1.0, event_type="extraction",
            data={"claims_stored": 3},
        ))
        buf.append(DebugLogEntry(
            timestamp=2.0, event_type="extraction",
            data={"claims_stored": 0},
        ))
        stats = buf.stats()
        assert stats["extraction_success_rate"] == 0.5


# ---------------------------------------------------------------------------
# Global State / Config Toggle Tests
# ---------------------------------------------------------------------------

class TestDebugGlobalState:

    def test_init_and_toggle(self):
        buf = init_debug_logging(enabled=False, max_entries=500)
        assert not is_debug_enabled()
        assert buf.max_entries == 500
        set_debug_enabled(True)
        assert is_debug_enabled()
        set_debug_enabled(False)
        assert not is_debug_enabled()

    def test_disabled_no_op(self):
        """When disabled, log functions should not add entries."""
        init_debug_logging(enabled=False, max_entries=100)
        buf = get_debug_buffer()
        initial_len = len(buf)
        log_extraction_event(session_id="test", claims_extracted=5)
        log_retrieval_event(session_id="test", query="hello")
        assert len(buf) == initial_len

    def test_enabled_logs_events(self):
        """When enabled, log functions should add entries."""
        init_debug_logging(enabled=True, max_entries=100)
        buf = get_debug_buffer()
        log_extraction_event(
            session_id="test-session",
            claims_extracted=5,
            claims_stored=3,
            entities_resolved=2,
            noise_filtered=1,
        )
        log_retrieval_event(
            session_id="test-session",
            query="what is cortex",
            mode="hybrid",
            items_returned=4,
            top_score=0.95,
            tokens_used=150,
        )
        assert len(buf) == 2
        entries = buf.query(last=10)
        assert entries[0]["event_type"] == "retrieval"
        assert entries[0]["session_id"] == "test-session"
        assert entries[0]["query"] == "what is cortex"
        assert entries[1]["event_type"] == "extraction"
        assert entries[1]["claims_extracted"] == 5


# ---------------------------------------------------------------------------
# Debug Log Entry Tests
# ---------------------------------------------------------------------------

class TestDebugLogEntry:
    def test_to_dict(self):
        entry = DebugLogEntry(
            timestamp=1234567890.0,
            event_type="http_request",
            data={"method": "GET", "path": "/test", "status": 200},
        )
        d = entry.to_dict()
        assert d["timestamp"] == 1234567890.0
        assert d["event_type"] == "http_request"
        assert d["method"] == "GET"
        assert d["path"] == "/test"
        assert d["status"] == 200


# ---------------------------------------------------------------------------
# Config Integration Tests
# ---------------------------------------------------------------------------

class TestDebugConfig:
    def test_defaults(self):
        from cortex.config import CortexConfig
        config = CortexConfig()
        assert config.debug_logging_enabled is False
        assert config.debug_log_max_entries == 10000

    def test_from_toml_defaults(self):
        from cortex.config import CortexConfig
        config = CortexConfig.from_toml()
        assert config.debug_logging_enabled is False
        assert config.debug_log_max_entries == 10000

    def test_overlay(self):
        from cortex.config import CortexConfig
        config = CortexConfig.from_toml(
            overlay={"cortex": {"debug": {"enabled": True, "max_entries": 500}}}
        )
        assert config.debug_logging_enabled is True
        assert config.debug_log_max_entries == 500


# ---------------------------------------------------------------------------
# Buffer + Stats integration
# ---------------------------------------------------------------------------

class TestDebugStatsIntegration:
    @pytest.fixture
    def setup_debug(self):
        init_debug_logging(enabled=True, max_entries=1000)
        buf = get_debug_buffer()
        buf.clear()
        return buf

    def test_get_logs_empty(self, setup_debug):
        assert setup_debug.query(last=50) == []

    def test_get_logs_with_data(self, setup_debug):
        buf = setup_debug
        buf.append(DebugLogEntry(
            timestamp=time.time(),
            event_type="http_request",
            data={"method": "POST", "path": "/api/v1/remember", "status": 200, "latency_ms": 15.5},
        ))
        entries = buf.query(last=50)
        assert len(entries) == 1
        assert entries[0]["method"] == "POST"

    def test_stats_endpoint(self, setup_debug):
        buf = setup_debug
        buf.append(DebugLogEntry(
            timestamp=time.time(),
            event_type="http_request",
            data={"path": "/api/v1/remember", "latency_ms": 10.0, "status": 200},
        ))
        stats = buf.stats()
        assert stats["total_entries"] == 1
        assert stats["http_requests"] == 1

    def test_clear_logs(self, setup_debug):
        buf = setup_debug
        for i in range(10):
            buf.append(DebugLogEntry(timestamp=float(i), event_type="http_request"))
        assert len(buf) == 10
        cleared = buf.clear()
        assert cleared == 10
        assert len(buf) == 0

    def test_disabled_returns_empty(self):
        init_debug_logging(enabled=False, max_entries=100)
        assert is_debug_enabled() is False
