"""
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)
MIN_ACCURACY = 0.84


@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.85,
        "question": 0.55,
        "default": 0.85,
    }

    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"}
