"""Tests for router_core wrapper (Python fallback path — always runs without Rust)."""

from __future__ import annotations

import time

from llmhosts.router._core import RUST_AVAILABLE, classify_privacy, extract_features, knn_search, score_confidence


class TestKnnSearch:
    def test_returns_top_k(self):
        vectors = [[1.0, 0.0], [0.0, 1.0], [0.5, 0.5]]
        labels = ["a", "b", "c"]
        result = knn_search([1.0, 0.0], vectors, labels, k=2)
        assert len(result) == 2
        assert result[0][0] == "a"  # closest to [1, 0]

    def test_empty_index(self):
        assert knn_search([1.0, 0.0], [], [], k=5) == []

    def test_k_clamped_to_index_size(self):
        vectors = [[1.0, 0.0]]
        labels = ["a"]
        result = knn_search([1.0, 0.0], vectors, labels, k=10)
        assert len(result) == 1

    def test_sorted_by_distance(self):
        vectors = [[10.0, 0.0], [1.0, 0.0], [5.0, 0.0]]
        labels = ["far", "near", "mid"]
        result = knn_search([0.0, 0.0], vectors, labels, k=3)
        distances = [r[1] for r in result]
        assert distances == sorted(distances)

    def test_latency_p99_under_50ms(self):
        """kNN P99 latency < 50ms (Rust) or < 500ms (Python fallback) on 1000-vector, 128-dim index."""
        dim = 128
        n = 1000
        import random

        random.seed(42)
        index_vectors = [[random.gauss(0, 1) for _ in range(dim)] for _ in range(n)]
        labels = [f"label_{i}" for i in range(n)]
        query = [random.gauss(0, 1) for _ in range(dim)]

        latencies = []
        for _ in range(100):
            t0 = time.perf_counter()
            knn_search(query, index_vectors, labels, k=5)
            latencies.append((time.perf_counter() - t0) * 1000)

        latencies.sort()
        p99 = latencies[98]
        # Rust extension: tight 50ms contract. Python fallback: lenient 500ms on slow CI runners.
        threshold = 50.0 if RUST_AVAILABLE else 500.0
        assert p99 < threshold, f"P99 latency {p99:.1f}ms exceeds {threshold:.0f}ms"


class TestExtractFeatures:
    def test_returns_dict(self):
        features = extract_features("How do I write a hello world in Python?")
        assert isinstance(features, dict)
        assert "estimated_tokens" in features

    def test_code_detected(self):
        features = extract_features("```python\ndef hello():\n    pass\n```")
        assert features["code_marker_count"] > 0

    def test_complexity_detected(self):
        features = extract_features("Compare and analyze the pros and cons of multiple approaches")
        assert features["complexity_indicators"] > 0

    def test_question_detected(self):
        features = extract_features("What is the meaning of life?")
        assert features["is_question"] == 1

    def test_no_question(self):
        features = extract_features("Write a poem about the ocean.")
        assert features["is_question"] == 0


class TestScoreConfidence:
    def test_returns_float_in_range(self):
        score = score_confidence({"complexity_indicators": 2, "code_marker_count": 1, "estimated_tokens": 100})
        assert 0.0 <= score <= 1.0

    def test_more_signals_higher_confidence(self):
        low = score_confidence({"complexity_indicators": 0, "code_marker_count": 0, "estimated_tokens": 10})
        high = score_confidence({"complexity_indicators": 3, "code_marker_count": 2, "estimated_tokens": 200})
        assert high > low

    def test_empty_features_returns_base(self):
        score = score_confidence({})
        assert 0.5 <= score <= 0.7  # base confidence


class TestClassifyPrivacy:
    def test_personal_pii(self):
        assert classify_privacy("My name is John and my address is 123 Main St") == "personal"

    def test_medical_pii(self):
        assert classify_privacy("My medical records show I have diabetes") == "personal"

    def test_confidential_business(self):
        assert classify_privacy("This is internal and confidential to our company") == "private"

    def test_external_default(self):
        assert classify_privacy("What is the capital of France?") == "external"

    def test_latency_p99_under_5ms(self):
        """Privacy classifier P99 latency < 5ms."""
        texts = [
            "What is the capital of France?",
            "My name is Alice and I need help",
            "This is internal confidential data",
            "Write a function to reverse a string in Python",
        ]
        import random

        latencies = []
        for _ in range(200):
            text = random.choice(texts)
            t0 = time.perf_counter()
            classify_privacy(text)
            latencies.append((time.perf_counter() - t0) * 1000)

        latencies.sort()
        p99 = latencies[197]
        assert p99 < 5.0, f"Privacy classifier P99 {p99:.2f}ms exceeds 5ms"
