"""Tests for CLI commands."""

import json
import tempfile
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest
import yaml
from click.testing import CliRunner

from ai_qa.cli.main import cli


@pytest.fixture
def cli_runner():
    """Provide Click test runner."""
    return CliRunner()


@pytest.fixture
def mock_validate():
    """Mock validate function."""
    mock_check = MagicMock()
    mock_check.name = "factuality"
    mock_check.score = 0.95
    mock_check.passed = True
    mock_check.threshold = 0.8
    mock_check.evidence = "Output is factually accurate"
    mock_check.cost_usd = 0.01
    mock_check.tokens_used = {"input": 100, "output": 50}
    mock_check.cached = False
    mock_check.check_version = "1.0.0"

    mock_result = MagicMock()
    mock_result.passed = True
    mock_result.total_cost = 0.01
    mock_result.checks = [mock_check]
    return mock_result


class TestValidateCommand:
    """Test validate command."""

    def test_validate_basic(self, cli_runner, mock_validate):
        """Test basic validate command."""
        with patch("ai_qa.cli.commands.validate") as mock_val:
            mock_val.return_value = mock_validate

            result = cli_runner.invoke(
                cli,
                [
                    "validate",
                    "--output",
                    "Paris is the capital of France",
                    "--checks",
                    "factuality",
                ],
            )

            assert result.exit_code == 0
            assert "Paris" in str(mock_val.call_args)

    def test_validate_multiple_checks(self, cli_runner, mock_validate):
        """Test validate with multiple checks."""
        with patch("ai_qa.cli.commands.validate") as mock_val:
            mock_val.return_value = mock_validate

            result = cli_runner.invoke(
                cli,
                [
                    "validate",
                    "--output",
                    "Test output",
                    "--checks",
                    "factuality,relevance,hallucination",
                ],
            )

            assert result.exit_code == 0

    def test_validate_with_input(self, cli_runner, mock_validate):
        """Test validate with input parameter."""
        with patch("ai_qa.cli.commands.validate") as mock_val:
            mock_val.return_value = mock_validate

            result = cli_runner.invoke(
                cli,
                [
                    "validate",
                    "--output",
                    "Paris is in France",
                    "--input",
                    "Where is Paris?",
                    "--checks",
                    "relevance",
                ],
            )

            assert result.exit_code == 0
            call_kwargs = mock_val.call_args.kwargs
            assert call_kwargs["input"] == "Where is Paris?"

    def test_validate_with_threshold(self, cli_runner, mock_validate):
        """Test validate with threshold override."""
        with patch("ai_qa.cli.commands.validate") as mock_val:
            mock_val.return_value = mock_validate

            result = cli_runner.invoke(
                cli,
                [
                    "validate",
                    "--output",
                    "Test",
                    "--checks",
                    "factuality",
                    "--threshold",
                    "0.9",
                ],
            )

            assert result.exit_code == 0
            call_kwargs = mock_val.call_args.kwargs
            assert call_kwargs["threshold"] == 0.9

    def test_validate_with_ground_truth(self, cli_runner, mock_validate):
        """Test validate with ground truth."""
        with patch("ai_qa.cli.commands.validate") as mock_val:
            mock_val.return_value = mock_validate

            result = cli_runner.invoke(
                cli,
                [
                    "validate",
                    "--output",
                    "Output text",
                    "--checks",
                    "factuality",
                    "--ground-truth",
                    "Expected answer",
                ],
            )

            assert result.exit_code == 0
            call_kwargs = mock_val.call_args.kwargs
            assert call_kwargs["ground_truth"] == "Expected answer"

    def test_validate_format_json(self, cli_runner, mock_validate):
        """Test validate with JSON format."""
        with patch("ai_qa.cli.commands.validate") as mock_val:
            mock_val.return_value = mock_validate

            result = cli_runner.invoke(
                cli,
                [
                    "validate",
                    "--output",
                    "Test",
                    "--checks",
                    "factuality",
                    "--format",
                    "json",
                ],
            )

            assert result.exit_code == 0
            # Should contain JSON
            assert "passed_rate" in result.output

    def test_validate_format_table(self, cli_runner, mock_validate):
        """Test validate with table format."""
        with patch("ai_qa.cli.commands.validate") as mock_val:
            mock_val.return_value = mock_validate

            result = cli_runner.invoke(
                cli,
                [
                    "validate",
                    "--output",
                    "Test",
                    "--checks",
                    "factuality",
                    "--format",
                    "table",
                ],
            )

            assert result.exit_code == 0
            assert "SUMMARY" in result.output

    def test_validate_output_file(self, cli_runner, mock_validate):
        """Test validate with output file."""
        with tempfile.TemporaryDirectory() as tmpdir:
            output_file = Path(tmpdir) / "report.json"

            with patch("ai_qa.cli.commands.validate") as mock_val:
                mock_val.return_value = mock_validate

                result = cli_runner.invoke(
                    cli,
                    [
                        "validate",
                        "--output",
                        "Test",
                        "--checks",
                        "factuality",
                        "--format",
                        "json",
                        "--output-file",
                        str(output_file),
                    ],
                )

                assert result.exit_code == 0
                assert output_file.exists()

    def test_validate_missing_required(self, cli_runner):
        """Test validate missing required parameters."""
        result = cli_runner.invoke(cli, ["validate"])

        assert result.exit_code != 0

    def test_validate_caching(self, cli_runner, mock_validate):
        """Test validate with caching option."""
        with patch("ai_qa.cli.commands.validate") as mock_val:
            mock_val.return_value = mock_validate

            result = cli_runner.invoke(
                cli,
                [
                    "validate",
                    "--output",
                    "Test",
                    "--checks",
                    "factuality",
                    "--cache",
                ],
            )

            assert result.exit_code == 0
            call_kwargs = mock_val.call_args.kwargs
            assert call_kwargs["cache"] is True


class TestChecksCommand:
    """Test checks list command."""

    def test_checks_list(self, cli_runner):
        """Test checks list command."""
        with patch("ai_qa.cli.commands.list_checks") as mock_list, patch(
            "ai_qa.cli.commands.get_check"
        ) as mock_get:
            check1 = MagicMock()
            check1.name = "factuality"
            check1.description = "Check for factual accuracy"
            check1.requires_llm = True
            check1.version = "1.0.0"
            check1.threshold = 0.8

            check2 = MagicMock()
            check2.name = "relevance"
            check2.description = "Check for relevance"
            check2.requires_llm = True
            check2.version = "1.0.0"
            check2.threshold = 0.75

            mock_list.return_value = ["factuality", "relevance"]
            mock_get.side_effect = lambda name: check1 if name == "factuality" else check2

            result = cli_runner.invoke(cli, ["checks"])

            assert result.exit_code == 0
            assert "factuality" in result.output
            assert "relevance" in result.output

    def test_checks_list_json(self, cli_runner):
        """Test checks list with JSON format."""
        with patch("ai_qa.cli.commands.list_checks") as mock_list, patch(
            "ai_qa.cli.commands.get_check"
        ) as mock_get:
            check = MagicMock()
            check.name = "test"
            check.description = "Test"
            check.requires_llm = False
            check.version = "1.0.0"
            check.threshold = 0.5

            mock_list.return_value = ["test"]
            mock_get.return_value = check

            result = cli_runner.invoke(cli, ["checks", "--format", "json"])

            assert result.exit_code == 0
            # Should be valid JSON
            output_json = json.loads(result.output)
            assert isinstance(output_json, list)


class TestSuiteCommand:
    """Test suite run command."""

    def test_suite_run_json(self, cli_runner, mock_validate):
        """Test suite run with JSON file."""
        test_cases = [
            {
                "test_id": "T001",
                "output": "Test output",
                "checks": ["factuality"],
                "category": "test",
            }
        ]

        with tempfile.TemporaryDirectory() as tmpdir:
            suite_file = Path(tmpdir) / "suite.json"
            suite_file.write_text(json.dumps(test_cases))

            with patch("ai_qa.cli.commands.validate") as mock_val:
                mock_val.return_value = mock_validate

                result = cli_runner.invoke(cli, ["suite", str(suite_file)])

                assert result.exit_code == 0
                assert "Results" in result.output

    def test_suite_run_yaml(self, cli_runner, mock_validate):
        """Test suite run with YAML file."""
        test_cases = [
            {
                "test_id": "T001",
                "output": "Test output",
                "checks": ["factuality"],
                "category": "test",
            }
        ]

        with tempfile.TemporaryDirectory() as tmpdir:
            suite_file = Path(tmpdir) / "suite.yaml"
            suite_file.write_text(yaml.dump(test_cases))

            with patch("ai_qa.cli.commands.validate") as mock_val:
                mock_val.return_value = mock_validate

                result = cli_runner.invoke(cli, ["suite", str(suite_file)])

                assert result.exit_code == 0

    def test_suite_run_with_output(self, cli_runner, mock_validate):
        """Test suite run saving to file."""
        test_cases = [
            {
                "test_id": "T001",
                "output": "Test",
                "checks": ["factuality"],
            }
        ]

        with tempfile.TemporaryDirectory() as tmpdir:
            suite_file = Path(tmpdir) / "suite.json"
            suite_file.write_text(json.dumps(test_cases))
            output_file = Path(tmpdir) / "report.json"

            with patch("ai_qa.cli.commands.validate") as mock_val:
                mock_val.return_value = mock_validate

                result = cli_runner.invoke(
                    cli,
                    [
                        "suite",
                        str(suite_file),
                        "--output",
                        str(output_file),
                    ],
                )

                assert result.exit_code == 0
                assert output_file.exists()

    def test_suite_run_html_format(self, cli_runner, mock_validate):
        """Test suite run with HTML format."""
        test_cases = [
            {
                "test_id": "T001",
                "output": "Test",
                "checks": ["factuality"],
            }
        ]

        with tempfile.TemporaryDirectory() as tmpdir:
            suite_file = Path(tmpdir) / "suite.json"
            suite_file.write_text(json.dumps(test_cases))

            with patch("ai_qa.cli.commands.validate") as mock_val:
                mock_val.return_value = mock_validate

                result = cli_runner.invoke(
                    cli,
                    ["suite", str(suite_file), "--format", "html"],
                )

                assert result.exit_code == 0

    def test_suite_run_invalid_file(self, cli_runner):
        """Test suite run with invalid file."""
        result = cli_runner.invoke(
            cli,
            ["suite", "nonexistent.json"],
        )

        assert result.exit_code != 0


class TestCLIHelp:
    """Test CLI help and version."""

    def test_cli_help(self, cli_runner):
        """Test CLI help."""
        result = cli_runner.invoke(cli, ["--help"])

        assert result.exit_code == 0
        assert "Validate AI outputs" in result.output

    def test_validate_help(self, cli_runner):
        """Test validate help."""
        result = cli_runner.invoke(cli, ["validate", "--help"])

        assert result.exit_code == 0
        assert "output" in result.output

    def test_checks_help(self, cli_runner):
        """Test checks help."""
        result = cli_runner.invoke(cli, ["checks", "--help"])

        assert result.exit_code == 0

    def test_suite_help(self, cli_runner):
        """Test suite help."""
        result = cli_runner.invoke(cli, ["suite", "--help"])

        assert result.exit_code == 0
