"""Tests for CLI interface."""

import json
from pathlib import Path
from unittest.mock import Mock, patch, PropertyMock

import pytest
from click.testing import CliRunner

from clore_ai.cli import cli, load_config, save_config


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


@pytest.fixture
def temp_config(tmp_path):
    """Create temporary config directory."""
    config_dir = tmp_path / ".clore"
    config_dir.mkdir()
    config_file = config_dir / "config.json"
    return config_file


def test_cli_help(runner):
    """Test CLI help."""
    result = runner.invoke(cli, ["--help"])
    assert result.exit_code == 0
    assert "Clore.ai GPU marketplace CLI" in result.output


def test_config_set(runner, temp_config, monkeypatch):
    """Test config set command."""
    monkeypatch.setattr("clore_ai.cli.CONFIG_FILE", temp_config)

    result = runner.invoke(cli, ["config", "set", "api_key", "test_key"])
    assert result.exit_code == 0
    assert "Set api_key = test_key" in result.output

    # Verify config saved
    with open(temp_config) as f:
        config = json.load(f)
    assert config["api_key"] == "test_key"


def test_config_get(runner, temp_config, monkeypatch):
    """Test config get command."""
    monkeypatch.setattr("clore_ai.cli.CONFIG_FILE", temp_config)

    # Set config first
    save_config({"api_key": "my_key"})

    result = runner.invoke(cli, ["config", "get", "api_key"])
    assert result.exit_code == 0
    assert "api_key = my_key" in result.output


def test_config_show(runner, temp_config, monkeypatch):
    """Test config show command."""
    monkeypatch.setattr("clore_ai.cli.CONFIG_FILE", temp_config)

    # Set config
    test_config = {"api_key": "test", "custom": "value"}
    with open(temp_config, "w") as f:
        json.dump(test_config, f)

    result = runner.invoke(cli, ["config", "show"])
    assert result.exit_code == 0


def _make_marketplace_server_mock():
    """Create a mock MarketplaceServer with correct properties."""
    mock_server = Mock()
    mock_server.id = 123
    # Properties need to be set as return values
    type(mock_server).gpu_model = PropertyMock(return_value="1x NVIDIA GeForce RTX 4090")
    type(mock_server).gpu_count = PropertyMock(return_value=2)
    type(mock_server).ram_gb = PropertyMock(return_value=64.0)
    type(mock_server).price_usd = PropertyMock(return_value=3.5)
    type(mock_server).location = PropertyMock(return_value="EU")

    # specs mock for CPU display
    mock_specs = Mock()
    mock_specs.cpu = "AMD Ryzen 9 7950X 16-Core Processor"
    mock_server.specs = mock_specs

    return mock_server


def _make_my_server_mock():
    """Create a mock MyServer."""
    mock_server = Mock()
    mock_server.id = 76279
    mock_server.name = "5090-test"
    type(mock_server).gpu_model = PropertyMock(return_value="RTX 5090")
    mock_server.gpu_count = 1
    type(mock_server).status = PropertyMock(return_value="Online")
    return mock_server


@patch("clore_ai.cli.get_client")
def test_wallets_command(mock_get_client, runner):
    """Test wallets command."""
    mock_client = Mock()
    mock_wallet = Mock()
    mock_wallet.name = "bitcoin"
    mock_wallet.balance = 0.001
    mock_wallet.deposit = "bc1..."
    mock_client.wallets.return_value = [mock_wallet]
    mock_get_client.return_value = mock_client

    result = runner.invoke(cli, ["wallets"])
    assert result.exit_code == 0
    assert "bitcoin" in result.output


@patch("clore_ai.cli.get_client")
def test_search_command(mock_get_client, runner):
    """Test search command."""
    mock_client = Mock()
    mock_server = _make_marketplace_server_mock()
    mock_client.marketplace.return_value = [mock_server]
    mock_get_client.return_value = mock_client

    result = runner.invoke(cli, ["search", "--limit", "10"])
    assert result.exit_code == 0
    assert "4090" in result.output


@patch("clore_ai.cli.get_client")
def test_servers_command(mock_get_client, runner):
    """Test servers (my_servers) command."""
    mock_client = Mock()
    mock_server = _make_my_server_mock()
    mock_client.my_servers.return_value = [mock_server]
    mock_get_client.return_value = mock_client

    result = runner.invoke(cli, ["servers"])
    assert result.exit_code == 0
    assert "76279" in result.output
    assert "RTX 5090" in result.output


@patch("clore_ai.cli.get_client")
def test_orders_command(mock_get_client, runner):
    """Test orders command."""
    mock_client = Mock()
    mock_order = Mock()
    mock_order.id = 456
    mock_order.server_id = 123
    mock_order.type = "on-demand"
    mock_order.status = "active"
    mock_order.image = "ubuntu"
    mock_order.pub_cluster = "1.2.3.4"
    mock_client.my_orders.return_value = [mock_order]
    mock_get_client.return_value = mock_client

    result = runner.invoke(cli, ["orders"])
    assert result.exit_code == 0
    assert "456" in result.output


@patch("clore_ai.cli.get_client")
def test_cancel_command(mock_get_client, runner):
    """Test cancel command."""
    mock_client = Mock()
    mock_client.cancel_order.return_value = {"code": 0, "message": "Cancelled"}
    mock_get_client.return_value = mock_client

    result = runner.invoke(cli, ["cancel", "123"])
    assert result.exit_code == 0
    assert "cancelled" in result.output.lower()


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