"""Unit tests for evalkit CLI (evalkit.cli)."""

from __future__ import annotations

import json

from click.testing import CliRunner

from evalkit.cli import app

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _make_report_dict() -> dict:
    """Return a minimal valid EvaluationReport as a Python dict."""
    return {
        "suite_name": "rag_qa",
        "results": [
            {
                "test_case": {
                    "question": "What is the capital of France?",
                    "context": ["Paris is the capital of France."],
                    "answer": "Paris",
                    "expected_answer": "Paris",
                    "metadata": {},
                },
                "scores": [
                    {
                        "metric_name": "faithfulness",
                        "score": 0.9,
                        "reason": "Answer is faithful to context.",
                        "passed": True,
                    },
                    {
                        "metric_name": "answer_relevancy",
                        "score": 0.85,
                        "reason": "Answer is relevant.",
                        "passed": True,
                    },
                ],
                "overall_score": 0.875,
            }
        ],
        "summary": {
            "total_cases": 1,
            "passed": 1,
            "failed": 0,
            "average_score": 0.875,
            "score_by_metric": {
                "faithfulness": 0.9,
                "answer_relevancy": 0.85,
            },
        },
        "failures": [],
    }


# ---------------------------------------------------------------------------
# Basic CLI tests
# ---------------------------------------------------------------------------


def test_version():
    runner = CliRunner()
    result = runner.invoke(app, ["--version"])
    assert result.exit_code == 0
    assert "0.1.0" in result.output


def test_help():
    runner = CliRunner()
    result = runner.invoke(app, ["--help"])
    assert result.exit_code == 0
    assert "evalkit" in result.output.lower()


# ---------------------------------------------------------------------------
# evalkit run
# ---------------------------------------------------------------------------


def test_run_help():
    runner = CliRunner()
    result = runner.invoke(app, ["run", "--help"])
    assert result.exit_code == 0
    assert "--input" in result.output or "-i" in result.output


def test_run_no_args_exits_nonzero():
    """run with no --input or --endpoint should fail with UsageError."""
    runner = CliRunner()
    result = runner.invoke(app, ["run"])
    assert result.exit_code != 0


def test_run_missing_input_file():
    """--input with a non-existent file should exit non-zero."""
    runner = CliRunner()
    result = runner.invoke(app, ["run", "--input", "missing_file.csv"])
    assert result.exit_code != 0


def test_run_both_input_and_endpoint_exits_nonzero():
    """Providing both --input and --endpoint should fail."""
    runner = CliRunner()
    with runner.isolated_filesystem():
        # Create a dummy CSV file so --input passes the exists check
        with open("dummy.csv", "w") as f:
            f.write("question,context,answer\n")
        result = runner.invoke(
            app,
            ["run", "--input", "dummy.csv", "--endpoint", "http://localhost:9999"],
        )
    assert result.exit_code != 0


def test_run_endpoint_without_questions_exits_nonzero():
    """--endpoint without --questions should fail."""
    runner = CliRunner()
    result = runner.invoke(app, ["run", "--endpoint", "http://localhost:9999"])
    assert result.exit_code != 0


# ---------------------------------------------------------------------------
# evalkit serve
# ---------------------------------------------------------------------------


def test_serve_help():
    runner = CliRunner()
    result = runner.invoke(app, ["serve", "--help"])
    assert result.exit_code == 0
    assert "--host" in result.output or "--port" in result.output


# ---------------------------------------------------------------------------
# evalkit report
# ---------------------------------------------------------------------------


def test_report_help():
    runner = CliRunner()
    result = runner.invoke(app, ["report", "--help"])
    assert result.exit_code == 0
    assert "report_file" in result.output.lower() or "REPORT_FILE" in result.output


def test_report_json_format(tmp_path):
    """evalkit report --format json should pretty-print the report JSON."""
    report_path = tmp_path / "report.json"
    report_path.write_text(json.dumps(_make_report_dict()), encoding="utf-8")

    runner = CliRunner()
    result = runner.invoke(app, ["report", str(report_path), "--format", "json"])
    assert result.exit_code == 0, result.output
    # Should be parseable JSON
    parsed = json.loads(result.output)
    assert parsed["suite_name"] == "rag_qa"
    assert parsed["summary"]["total_cases"] == 1


def test_report_text_format(tmp_path):
    """evalkit report (default text format) should display a human-readable summary."""
    report_path = tmp_path / "report.json"
    report_path.write_text(json.dumps(_make_report_dict()), encoding="utf-8")

    runner = CliRunner()
    result = runner.invoke(app, ["report", str(report_path)])
    assert result.exit_code == 0, result.output
    output = result.output
    # Check key elements appear
    assert "rag_qa" in output
    assert "1/1" in output or "1 / 1" in output or "passed" in output.lower()
    assert "faithfulness" in output
    assert "answer_relevancy" in output


def test_report_text_format_with_failures(tmp_path):
    """Text report should display failure groups when present."""
    report_dict = _make_report_dict()
    report_dict["summary"]["passed"] = 0
    report_dict["summary"]["failed"] = 1
    report_dict["failures"] = [
        {
            "failure_type": "hallucination",
            "count": 1,
            "examples": [],
            "suggestion": "High hallucination rate detected.",
        }
    ]
    report_path = tmp_path / "report_failures.json"
    report_path.write_text(json.dumps(report_dict), encoding="utf-8")

    runner = CliRunner()
    result = runner.invoke(app, ["report", str(report_path)])
    assert result.exit_code == 0, result.output
    assert "hallucination" in result.output
    assert "High hallucination rate detected." in result.output


def test_report_missing_file():
    """evalkit report with a non-existent file should exit non-zero."""
    runner = CliRunner()
    result = runner.invoke(app, ["report", "no_such_report.json"])
    assert result.exit_code != 0
