"""Tests for quber.cli module (non-async parts)."""

from unittest.mock import MagicMock, patch

import pytest
from click.testing import CliRunner

from quber.cli import cli


@pytest.fixture
def runner() -> CliRunner:
    """Create Click CLI test runner."""
    return CliRunner()


def test_cli_help(runner: CliRunner):
    """Test CLI help output."""
    result = runner.invoke(cli, ["--help"])

    assert result.exit_code == 0
    assert "Quber" in result.output
    assert "Advanced table extraction" in result.output


def test_cli_version(runner: CliRunner):
    """Test CLI version output."""
    result = runner.invoke(cli, ["--version"])

    assert result.exit_code == 0
    assert "version" in result.output.lower()


def test_analyze_help(runner: CliRunner):
    """Test analyze command help."""
    result = runner.invoke(cli, ["analyze", "--help"])

    assert result.exit_code == 0
    assert "PDF" in result.output
    assert "--output" in result.output
    assert "--provider" in result.output


def test_batch_help(runner: CliRunner):
    """Test batch command help."""
    result = runner.invoke(cli, ["batch", "--help"])

    assert result.exit_code == 0
    assert "directory" in result.output
    assert "--output-dir" in result.output
    assert "--pattern" in result.output


def test_models_help(runner: CliRunner):
    """Test models command help."""
    result = runner.invoke(cli, ["models", "--help"])

    assert result.exit_code == 0
    assert "available models" in result.output.lower()
    assert "--provider" in result.output


def test_models_command_basic(runner: CliRunner):
    """Test models command displays provider list."""
    with patch("quber.agents.factory.AgentFactory") as mock_factory_class:
        mock_factory = MagicMock()
        mock_factory.get_available_providers.return_value = ["anthropic"]
        mock_factory.list_available_models.return_value = {
            "anthropic": ["claude-3-5-sonnet-latest", "claude-3-5-haiku-latest"]
        }
        mock_factory_class.return_value = mock_factory

        result = runner.invoke(cli, ["models"])

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


def test_models_command_specific_provider(runner: CliRunner):
    """Test models command with specific provider."""
    with patch("quber.agents.factory.AgentFactory") as mock_factory_class:
        mock_factory = MagicMock()
        mock_factory.get_available_providers.return_value = ["anthropic"]
        mock_factory.list_available_models.return_value = {"anthropic": ["claude-3-5-sonnet-latest"]}
        mock_factory_class.return_value = mock_factory

        result = runner.invoke(cli, ["models", "--provider", "anthropic"])

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


def test_models_command_no_api_keys(runner: CliRunner):
    """Test models command shows model providers."""
    result = runner.invoke(cli, ["models"])

    assert result.exit_code == 0
    assert "Model Providers" in result.output
    # Should show at least one provider
    assert "ANTHROPIC" in result.output or "OPENAI" in result.output or "OLLAMA" in result.output


def test_models_command_with_all_flag(runner: CliRunner):
    """Test models command with --all flag runs successfully."""
    result = runner.invoke(cli, ["models", "--all"])

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


def test_models_command_truncates_long_list(runner: CliRunner):
    """Test models command may truncate long model lists."""
    result = runner.invoke(cli, ["models"])

    assert result.exit_code == 0
    # Should either show truncation or full list
    assert "Model Providers" in result.output


def test_analyze_missing_pdf_path(runner: CliRunner):
    """Test analyze command without PDF path."""
    result = runner.invoke(cli, ["analyze"])

    assert result.exit_code != 0
    assert "Missing argument" in result.output


def test_batch_missing_directory(runner: CliRunner):
    """Test batch command without directory."""
    result = runner.invoke(cli, ["batch"])

    assert result.exit_code != 0
    assert "Missing argument" in result.output


def test_analyze_invalid_provider(runner: CliRunner):
    """Test analyze command with invalid provider."""
    with runner.isolated_filesystem():
        with open("test.pdf", "w") as f:
            f.write("dummy")

        result = runner.invoke(cli, ["analyze", "test.pdf", "--provider", "invalid"])

        assert result.exit_code != 0


def test_batch_nonexistent_directory(runner: CliRunner):
    """Test batch command with non-existent directory."""
    result = runner.invoke(cli, ["batch", "/nonexistent/directory"])

    assert result.exit_code != 0


def test_cli_has_three_commands(runner: CliRunner):
    """Test that CLI has expected commands."""
    result = runner.invoke(cli, ["--help"])

    assert result.exit_code == 0
    assert "analyze" in result.output
    assert "batch" in result.output
    assert "models" in result.output


def test_analyze_default_options(runner: CliRunner):
    """Test analyze command has correct default options."""
    result = runner.invoke(cli, ["analyze", "--help"])

    assert result.exit_code == 0
    assert "Output directory (default: output)" in result.output
    assert "default: 5" in result.output


def test_batch_default_options(runner: CliRunner):
    """Test batch command has correct default options."""
    result = runner.invoke(cli, ["batch", "--help"])

    assert result.exit_code == 0
    assert "Output directory for results (default: output)" in result.output
    assert "*.pdf" in result.output


def test_models_truncates_long_model_names(runner: CliRunner):
    """Test models command truncates very long model names."""
    with patch("quber.agents.factory.AgentFactory") as mock_factory_class:
        mock_factory = MagicMock()
        mock_factory.get_available_providers.return_value = ["anthropic"]

        long_model = "a" * 65
        mock_factory.list_available_models.return_value = {"anthropic": [long_model]}
        mock_factory_class.return_value = mock_factory

        result = runner.invoke(cli, ["models"])

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


def test_document_is_alias_of_parse():
    """`document` and `parse` resolve to the same command callback."""
    assert cli.commands["document"].callback is cli.commands["parse"].callback


def test_table_is_alias_of_extract():
    """`table` and `extract` resolve to the same command callback."""
    assert cli.commands["table"].callback is cli.commands["extract"].callback


def test_document_and_table_in_root_help(runner: CliRunner):
    """Canonical and back-compat names are all listed in root help."""
    result = runner.invoke(cli, ["--help"])

    assert result.exit_code == 0
    for name in ("document", "table", "parse", "extract"):
        assert name in result.output


def test_document_help_matches_parse(runner: CliRunner):
    """`document --help` exposes the same options as `parse --help`."""
    document_help = runner.invoke(cli, ["document", "--help"])
    parse_help = runner.invoke(cli, ["parse", "--help"])

    assert document_help.exit_code == 0
    assert parse_help.exit_code == 0
    assert "--preset" in document_help.output
    assert "--image-mode" in document_help.output


def test_table_help_matches_extract(runner: CliRunner):
    """`table --help` exposes the same options as `extract --help`."""
    table_help = runner.invoke(cli, ["table", "--help"])
    extract_help = runner.invoke(cli, ["extract", "--help"])

    assert table_help.exit_code == 0
    assert extract_help.exit_code == 0
    assert "--engine" in table_help.output
    assert "--review" in table_help.output
