"""
accuracy tests for filler classifier.
runs 1000 thai customer service sentences through the classifier
and checks that overall accuracy meets the threshold.
"""

import json
from pathlib import Path
from collections import defaultdict

import pytest

from filler_prediction import FillerClassifier

TEST_DATA_PATH = Path(__file__).parent / "test_data.json"
# minimum accuracy threshold (proportion, not percentage)
# v0.5.0: ambiguous tokens (ครับ, ค่ะ etc) now short-circuit to no_filler without cog_level context,
# which shifts a few test items. accuracy threshold adjusted accordingly.
MIN_ACCURACY = 0.81


@pytest.fixture(scope="module")
def classifier():
    return FillerClassifier()


@pytest.fixture(scope="module")
def test_data():
    with open(TEST_DATA_PATH, encoding="utf-8") as f:
        return json.load(f)


@pytest.fixture(scope="module")
def results(classifier, test_data):
    """run all test sentences through classifier, return list of dicts."""
    out = []
    for item in test_data:
        category, confidence, filler = classifier.classify(item["text"])
        out.append({
            "text": item["text"],
            "expected": item["expected"],
            "predicted": category,
            "confidence": confidence,
            "correct": category == item["expected"],
        })
    return out


def test_overall_accuracy(results):
    correct = sum(1 for r in results if r["correct"])
    total = len(results)
    accuracy = correct / total
    print(f"\noverall accuracy: {accuracy:.1%} ({correct}/{total})")
    assert accuracy >= MIN_ACCURACY, f"accuracy {accuracy:.1%} below threshold {MIN_ACCURACY:.0%}"


def test_per_category_recall(results):
    """each category should have reasonable recall."""
    by_cat = defaultdict(lambda: {"correct": 0, "total": 0})
    for r in results:
        by_cat[r["expected"]]["total"] += 1
        if r["correct"]:
            by_cat[r["expected"]]["correct"] += 1

    min_recalls = {
        "complaint": 0.78,   # some rhetorical questions still go to question
        "question": 0.55,
        "default": 0.75,     # some borderline items correctly move to no_filler
        "no_filler": 0.88,   # "wait/think" phrases without เอ่อ are borderline with default
    }

    for cat, counts in by_cat.items():
        recall = counts["correct"] / counts["total"]
        threshold = min_recalls.get(cat, 0.5)
        print(f"  {cat}: recall={recall:.1%} ({counts['correct']}/{counts['total']})")
        assert recall >= threshold, f"{cat} recall {recall:.1%} below {threshold:.0%}"


def test_complaint_precision(results):
    """complaint predictions should be mostly correct (avoid false positives on angry-sounding defaults)."""
    predicted_complaint = [r for r in results if r["predicted"] == "complaint"]
    if not predicted_complaint:
        pytest.skip("no complaint predictions")
    tp = sum(1 for r in predicted_complaint if r["expected"] == "complaint")
    precision = tp / len(predicted_complaint)
    print(f"\ncomplaint precision: {precision:.1%} ({tp}/{len(predicted_complaint)})")
    assert precision >= 0.65, f"complaint precision {precision:.1%} below 65%"


def test_data_loaded(test_data):
    """sanity check that test data loaded correctly."""
    assert len(test_data) >= 900, f"expected ~1000 test sentences, got {len(test_data)}"
    categories = {item["expected"] for item in test_data}
    assert categories == {"complaint", "question", "default", "no_filler"}


# --- cog_level context tests ---

class TestCogLevel:
    """test the cog_level parameter for ambiguous token handling."""

    def test_bare_krap_high_cog_is_no_filler(self, classifier):
        """bare ครับ to a high-bandwidth question = user thinking, no filler."""
        cat, conf, filler = classifier.classify("ครับ", cog_level="high")
        assert cat == "no_filler"
        assert conf == 1.0
        assert filler is None

    def test_bare_ka_high_cog_is_no_filler(self, classifier):
        """bare ค่ะ to a high-bandwidth question = user thinking, no filler."""
        cat, conf, filler = classifier.classify("ค่ะ", cog_level="high")
        assert cat == "no_filler"
        assert filler is None

    def test_bare_krap_low_cog_is_default(self, classifier):
        """bare ครับ to a simple yes/no question = complete answer, play filler."""
        cat, conf, filler = classifier.classify("ครับ", cog_level="low")
        assert cat == "default"
        assert conf == 0.9
        assert filler is not None

    def test_bare_ka_low_cog_is_default(self, classifier):
        """bare ค่ะ to a simple yes/no = complete answer."""
        cat, conf, filler = classifier.classify("ค่ะ", cog_level="low")
        assert cat == "default"
        assert filler is not None

    def test_bare_krap_no_context_is_no_filler(self, classifier):
        """bare ครับ with no context = safe default no_filler."""
        cat, conf, filler = classifier.classify("ครับ", cog_level=None)
        assert cat == "no_filler"
        assert conf == 0.8
        assert filler is None

    def test_hesitation_high_cog_is_no_filler(self, classifier):
        """เอ่อ to a high-bandwidth question = thinking."""
        cat, _, filler = classifier.classify("เอ่อ", cog_level="high")
        assert cat == "no_filler"
        assert filler is None

    def test_hesitation_low_cog_is_default(self, classifier):
        """เอ่อ to a low-bandwidth question = still play filler (odd but safe)."""
        cat, _, filler = classifier.classify("เอ่อ", cog_level="low")
        assert cat == "default"
        assert filler is not None

    def test_longer_text_ignores_cog_level(self, classifier):
        """non-ambiguous text should go to embed classify regardless of cog_level."""
        cat_high, _, _ = classifier.classify("สนใจครับ", cog_level="high")
        cat_low, _, _ = classifier.classify("สนใจครับ", cog_level="low")
        cat_none, _, _ = classifier.classify("สนใจครับ", cog_level=None)
        # all should hit embed classify, not the ambiguous token shortcut
        assert cat_high == cat_low == cat_none

    def test_get_filler_passes_cog_level(self, classifier):
        """get_filler convenience method should forward cog_level."""
        filler_high = classifier.get_filler("ครับ", cog_level="high")
        filler_low = classifier.get_filler("ครับ", cog_level="low")
        assert filler_high is None
        assert filler_low is not None

    def test_all_ambiguous_tokens_respond_to_cog(self, classifier):
        """every token in AMBIGUOUS_TOKENS should respect cog_level."""
        from filler_prediction import AMBIGUOUS_TOKENS
        for token in AMBIGUOUS_TOKENS:
            cat_high, _, _ = classifier.classify(token, cog_level="high")
            cat_low, _, _ = classifier.classify(token, cog_level="low")
            assert cat_high == "no_filler", f"{token} should be no_filler on high_cog"
            assert cat_low == "default", f"{token} should be default on low_cog"
