"""End-to-end tests for the materia CLI."""

from unittest.mock import patch

from typer.testing import CliRunner

from materia.cli import app

runner = CliRunner()


def test_cli_help():
    """Test that the CLI shows help."""
    result = runner.invoke(app, ["--help"])
    assert result.exit_code == 0
    assert "BeanFlows.coffee data platform management CLI" in result.stdout


def test_cli_version():
    """Test version command."""
    result = runner.invoke(app, ["version"])
    assert result.exit_code == 0
    assert "Materia CLI" in result.stdout


def test_secrets_test_command(mock_secrets):
    """Test secrets test command."""
    result = runner.invoke(app, ["secrets", "test"])
    assert result.exit_code == 0
    assert "SOPS decryption successful" in result.stdout


def test_secrets_list_command(mock_secrets):
    """Test secrets list command."""
    result = runner.invoke(app, ["secrets", "list"])
    assert result.exit_code == 0
    assert "HETZNER_API_TOKEN" in result.stdout


def test_worker_list_empty(mock_secrets, mock_hcloud_client):
    """Test worker list with no active workers."""
    result = runner.invoke(app, ["worker", "list"])
    assert result.exit_code == 0
    assert "No active workers" in result.stdout


def test_worker_list_with_workers(mock_secrets, mock_hcloud_client):
    """Test worker list with active workers."""
    mock_server = mock_hcloud_client.servers.get_all.return_value[0:1]
    mock_server = [
        type(
            "Server",
            (),
            {
                "id": 12345,
                "name": "test-worker",
                "status": "running",
                "public_net": type("Net", (), {"ipv4": type("IP", (), {"ip": "192.0.2.1"})()})(),
                "server_type": type("Type", (), {"name": "ccx12"})(),
            },
        )()
    ]
    mock_hcloud_client.servers.get_all.return_value = mock_server

    result = runner.invoke(app, ["worker", "list"])
    assert result.exit_code == 0
    assert "test-worker" in result.stdout
    assert "192.0.2.1" in result.stdout


def test_worker_create(mock_secrets, mock_hcloud_client, mock_ssh_wait):
    """Test worker creation."""
    result = runner.invoke(app, ["worker", "create", "test-worker", "--type", "ccx12"])
    assert result.exit_code == 0
    assert "Worker created" in result.stdout
    assert "192.0.2.1" in result.stdout

    mock_hcloud_client.servers.create.assert_called_once()


def test_worker_destroy(mock_secrets, mock_hcloud_client):
    """Test worker destruction."""
    mock_server = type(
        "Server",
        (),
        {
            "id": 12345,
            "name": "test-worker",
            "status": "running",
            "public_net": type("Net", (), {"ipv4": type("IP", (), {"ip": "192.0.2.1"})()})(),
            "server_type": type("Type", (), {"name": "ccx12"})(),
            "delete": lambda: None,
        },
    )()
    mock_hcloud_client.servers.get_all.return_value = [mock_server]

    result = runner.invoke(app, ["worker", "destroy", "test-worker", "--force"])
    assert result.exit_code == 0
    assert "Worker destroyed" in result.stdout


def test_pipeline_list():
    """Test pipeline list command."""
    result = runner.invoke(app, ["pipeline", "list"])
    assert result.exit_code == 0
    assert "extract" in result.stdout
    assert "transform" in result.stdout
    assert "1800" in result.stdout
    assert "3600" in result.stdout


def test_pipeline_run_extract():
    """Test running extract pipeline end-to-end."""
    with patch("materia.pipelines.subprocess.run") as mock_run:
        mock_run.return_value.returncode = 0
        mock_run.return_value.stdout = "Extracted successfully\n"
        mock_run.return_value.stderr = ""

        result = runner.invoke(app, ["pipeline", "run", "extract"])

        assert result.exit_code == 0
        assert "Running pipeline" in result.stdout
        assert "Pipeline completed successfully" in result.stdout

        mock_run.assert_called_once()
        call_args = mock_run.call_args
        assert call_args[0][0] == ["uv", "run", "--package", "psdonline", "extract_psd"]
        assert call_args[1]["timeout"] == 1800


def test_pipeline_run_transform():
    """Test running transform pipeline end-to-end."""
    with patch("materia.pipelines.subprocess.run") as mock_run:
        mock_run.return_value.returncode = 0
        mock_run.return_value.stdout = "Transform complete\n"
        mock_run.return_value.stderr = ""

        result = runner.invoke(app, ["pipeline", "run", "transform"])

        assert result.exit_code == 0
        assert "Running pipeline" in result.stdout
        assert "Pipeline completed successfully" in result.stdout

        mock_run.assert_called_once()
        call_args = mock_run.call_args
        assert "sqlmesh" in call_args[0][0]
        assert call_args[1]["timeout"] == 3600


def test_pipeline_run_invalid():
    """Test running an invalid pipeline."""
    result = runner.invoke(app, ["pipeline", "run", "invalid-pipeline"])

    assert result.exit_code != 0
    assert "Unknown pipeline" in result.stdout or "Unknown pipeline" in result.stderr


def test_worker_lifecycle_e2e(mock_secrets, mock_hcloud_client, mock_ssh_wait):
    """Test complete worker lifecycle: create -> list -> destroy."""
    create_result = runner.invoke(
        app, ["worker", "create", "lifecycle-test", "--type", "ccx12"]
    )
    assert create_result.exit_code == 0
    assert "Worker created" in create_result.stdout

    mock_server = type(
        "Server",
        (),
        {
            "id": 12345,
            "name": "lifecycle-test",
            "status": "running",
            "public_net": type("Net", (), {"ipv4": type("IP", (), {"ip": "192.0.2.1"})()})(),
            "server_type": type("Type", (), {"name": "ccx12"})(),
        },
    )()
    mock_hcloud_client.servers.get_all.return_value = [mock_server]

    list_result = runner.invoke(app, ["worker", "list"])
    assert list_result.exit_code == 0
    assert "lifecycle-test" in list_result.stdout

    destroy_result = runner.invoke(app, ["worker", "destroy", "lifecycle-test", "--force"])
    assert destroy_result.exit_code == 0
    assert "Worker destroyed" in destroy_result.stdout
