"""Tests for Clore.ai client."""

import pytest
from unittest.mock import Mock, patch

from clore_ai import CloreAI
from clore_ai.exceptions import AuthError, RateLimitError


# ── Real API-shaped fixtures ────────────────────────────


MARKETPLACE_SERVER_RAW = {
    "id": 99173,
    "owner": 24169,
    "autoprice": "usd",
    "mrl": 720,
    "price": {
        "on_demand": {"bitcoin": 0.0000441, "CLORE-Blockchain": 1108.85, "USD-Blockchain": 3},
        "spot": {"bitcoin": 0.0000441, "CLORE-Blockchain": 1108.85, "USD-Blockchain": 3},
        "usd": {
            "on_demand_btc": 3.0,
            "on_demand_clore": 3.0,
            "on_demand_usd": 3,
            "spot": 0,
        },
        "original_usd": {
            "CLORE-Blockchain": {"on_demand": 3, "spot": 3},
            "bitcoin": {"on_demand": 3, "spot": 3},
        },
    },
    "rented": False,
    "specs": {
        "mb": "TUF GAMING B650-PLUS WIFI",
        "cpu": "AMD Ryzen 9 7950X 16-Core Processor",
        "cpus": "16/32",
        "ram": 31.8111,
        "disk": "KINGSTON SNV2S500G 394.1GB",
        "disk_speed": 1857.97,
        "gpu": "1x NVIDIA GeForce RTX 4090",
        "gpuram": 23,
        "net": {"cc": "NO", "down": 875.58, "up": 866.40},
        "pcie_rev": 4,
        "pcie_width": 16,
        "pl": [450],
        "stock_pl": [450],
    },
    "reliability": 0,
    "allowed_coins": ["bitcoin", "CLORE-Blockchain", "USD-Blockchain"],
    "rating": {"avg": 0, "cnt": 0},
    "gpu_array": ["RTX 4090"],
    "gigaspot": True,
    "cuda_version": "13.0",
}

MARKETPLACE_SERVER_3090 = {
    "id": 55555,
    "owner": 11111,
    "mrl": 100,
    "price": {
        "on_demand": {"bitcoin": 0.0000200},
        "spot": {"bitcoin": 0.0000100},
        "usd": {"on_demand_btc": 1.2, "on_demand_clore": 1.2, "on_demand_usd": 1.2, "spot": 0},
    },
    "rented": False,
    "specs": {
        "cpu": "Intel i9-13900K",
        "ram": 32.0,
        "gpu": "1x NVIDIA GeForce RTX 3090",
        "gpuram": 24,
        "net": {"cc": "DE"},
    },
    "gpu_array": ["RTX 3090"],
}

WALLET_RAW = {
    "name": "bitcoin",
    "deposit": "bc1qvwdy37u0eyt8yhly8kx5qa7qnpm7zcttwrgzgd",
    "balance": 0.04191964,
    "withdrawal_fee": 0.00005,
}

MY_SERVER_RAW = {
    "id": 76279,
    "name": "5090-6_CL_HIVE_8939078",
    "connected": True,
    "visibility": "public",
    "pricing": {"bitcoin": 0.00003675, "CLORE-Blockchain": 924.04},
    "min_spot_pricing": {"bitcoin": 0.0000294, "CLORE-Blockchain": 739.23},
    "online": False,
    "is_vpn_enabled": False,
    "gpu_array": ["RTX 5090"],
    "max_fair_price_usd": 65,
    "allowed_currencies": ["CLORE-Blockchain"],
    "gpu_count": 1,
    "usd_pricing": {
        "CLORE-Blockchain": {"on_demand": 2.5, "spot": 2},
        "bitcoin": {"on_demand": 2.5, "spot": 2},
    },
    "working_properly": False,
    "monitoring": 1,
}


# ── Fixtures ────────────────────────────────


@pytest.fixture
def mock_response():
    """Mock HTTP response."""
    response = Mock()
    response.status_code = 200
    response.raise_for_status = Mock()
    return response


@pytest.fixture
def client():
    """Create test client."""
    return CloreAI(api_key="test_key")


# ── Basic client tests ────────────────────────────────


def test_client_initialization():
    """Test client can be initialized."""
    client = CloreAI(api_key="test_key")
    assert client.api_key == "test_key"
    assert client.base_url == "https://api.clore.ai/v1"


def test_client_no_api_key():
    """Test client works without API key for public endpoints."""
    client = CloreAI()
    assert client.api_key is None


def test_auth_required(client):
    """Test authentication error when API key missing."""
    client.api_key = None
    with pytest.raises(AuthError):
        client.wallets()


def test_context_manager():
    """Test client context manager."""
    with CloreAI(api_key="test") as client:
        assert client.api_key == "test"
    # Client should be closed after context exit


# ── Wallets ────────────────────────────────


@patch("httpx.Client.get")
def test_wallets_success(mock_get, mock_response, client):
    """Test successful wallets retrieval with real API structure."""
    mock_response.json = Mock(
        return_value={
            "code": 0,
            "wallets": [
                WALLET_RAW,
                {
                    "name": "CLORE-Blockchain",
                    "deposit": "0x2d42e5e7c791fe8c16bf8ac8780c01889ccea38b",
                    "balance": 976765.53173784,
                    "withdrawal_fee": 50,
                },
            ],
        }
    )
    mock_get.return_value = mock_response

    wallets = client.wallets()
    assert len(wallets) == 2
    assert wallets[0].name == "bitcoin"
    assert wallets[0].balance == 0.04191964
    assert wallets[0].deposit == "bc1qvwdy37u0eyt8yhly8kx5qa7qnpm7zcttwrgzgd"
    assert wallets[0].withdrawal_fee == 0.00005
    assert wallets[1].name == "CLORE-Blockchain"
    assert wallets[1].balance == 976765.53173784


# ── Marketplace ────────────────────────────────


@patch("httpx.Client.get")
def test_marketplace_public(mock_get, mock_response):
    """Test marketplace works without auth."""
    mock_response.json = Mock(return_value={"code": 0, "servers": [], "my_servers": []})
    mock_get.return_value = mock_response

    client = CloreAI()  # No API key
    servers = client.marketplace()
    assert isinstance(servers, list)


@patch("httpx.Client.get")
def test_marketplace_parses_nested_structure(mock_get, mock_response, client):
    """Test marketplace correctly parses nested server data."""
    mock_response.json = Mock(
        return_value={
            "code": 0,
            "servers": [MARKETPLACE_SERVER_RAW],
            "my_servers": [],
        }
    )
    mock_get.return_value = mock_response

    servers = client.marketplace(available_only=False)
    assert len(servers) == 1
    s = servers[0]

    # Direct fields
    assert s.id == 99173
    assert s.owner == 24169
    assert s.mrl == 720
    assert s.rented is False
    assert s.cuda_version == "13.0"

    # Nested specs
    assert s.specs is not None
    assert s.specs.gpu == "1x NVIDIA GeForce RTX 4090"
    assert s.specs.ram == 31.8111
    assert s.specs.cpu == "AMD Ryzen 9 7950X 16-Core Processor"
    assert s.specs.gpuram == 23

    # Nested price
    assert s.price is not None
    assert s.price.usd is not None
    assert s.price.usd.on_demand_usd == 3

    # Convenience properties
    assert s.gpu_model == "1x NVIDIA GeForce RTX 4090"
    assert s.gpu_count == 1
    assert s.ram_gb == 31.8111
    assert s.price_usd == 3
    assert s.available is True
    assert s.location == "NO"


@patch("httpx.Client.get")
def test_marketplace_with_gpu_filter(mock_get, mock_response, client):
    """Test marketplace GPU filtering against nested data."""
    mock_response.json = Mock(
        return_value={
            "code": 0,
            "servers": [MARKETPLACE_SERVER_RAW, MARKETPLACE_SERVER_3090],
            "my_servers": [],
        }
    )
    mock_get.return_value = mock_response

    servers = client.marketplace(gpu="RTX 4090")
    assert len(servers) == 1
    assert servers[0].id == 99173


@patch("httpx.Client.get")
def test_marketplace_with_price_filter(mock_get, mock_response, client):
    """Test marketplace price filtering against nested data."""
    mock_response.json = Mock(
        return_value={
            "code": 0,
            "servers": [MARKETPLACE_SERVER_RAW, MARKETPLACE_SERVER_3090],
            "my_servers": [],
        }
    )
    mock_get.return_value = mock_response

    servers = client.marketplace(max_price_usd=2.0)
    assert len(servers) == 1
    assert servers[0].id == 55555  # Only the RTX 3090 at $1.2


@patch("httpx.Client.get")
def test_marketplace_with_ram_filter(mock_get, mock_response, client):
    """Test marketplace RAM filtering."""
    mock_response.json = Mock(
        return_value={
            "code": 0,
            "servers": [MARKETPLACE_SERVER_RAW, MARKETPLACE_SERVER_3090],
            "my_servers": [],
        }
    )
    mock_get.return_value = mock_response

    servers = client.marketplace(min_ram_gb=32.0)
    # Only MARKETPLACE_SERVER_3090 has ram=32.0 (>= 32), MARKETPLACE_SERVER_RAW has 31.8111 (< 32)
    assert len(servers) == 1
    assert servers[0].id == 55555


@patch("httpx.Client.get")
def test_marketplace_available_only(mock_get, mock_response, client):
    """Test marketplace filters out rented servers by default."""
    rented_server = dict(MARKETPLACE_SERVER_3090, id=77777, rented=True)
    mock_response.json = Mock(
        return_value={
            "code": 0,
            "servers": [MARKETPLACE_SERVER_RAW, rented_server],
            "my_servers": [],
        }
    )
    mock_get.return_value = mock_response

    servers = client.marketplace(available_only=True)
    assert all(s.available for s in servers)
    assert 77777 not in [s.id for s in servers]


# ── My Servers ────────────────────────────────


@patch("httpx.Client.get")
def test_my_servers(mock_get, mock_response, client):
    """Test my_servers returns MyServer objects."""
    mock_response.json = Mock(
        return_value={"code": 0, "servers": [MY_SERVER_RAW]}
    )
    mock_get.return_value = mock_response

    servers = client.my_servers()
    assert len(servers) == 1
    s = servers[0]

    assert s.id == 76279
    assert s.name == "5090-6_CL_HIVE_8939078"
    assert s.connected is True
    assert s.online is False
    assert s.gpu_count == 1
    assert s.gpu_array == ["RTX 5090"]
    assert s.gpu_model == "RTX 5090"  # From gpu_array (specs.gpu is different)
    assert s.status == "Offline"
    assert s.working_properly is False


# ── Orders ────────────────────────────────


@patch("httpx.Client.get")
def test_my_orders_empty(mock_get, mock_response, client):
    """Test empty orders response."""
    mock_response.json = Mock(
        return_value={"code": 0, "orders": [], "limit": 999999}
    )
    mock_get.return_value = mock_response

    orders = client.my_orders()
    assert orders == []


@patch("httpx.Client.post")
def test_create_order(mock_post, mock_response, client):
    """Test order creation."""
    mock_response.json = Mock(
        return_value={
            "code": 0,
            "order": {
                "id": 123,
                "renting_server": 456,
                "type": "on-demand",
                "status": "active",
                "pub_cluster": "1.2.3.4",
            },
        }
    )
    mock_post.return_value = mock_response

    order = client.create_order(
        server_id=456,
        image="ubuntu",
        type="on-demand",
        currency="bitcoin",
        ssh_password="secret",
    )

    assert order.id == 123
    assert order.server_id == 456
    assert order.pub_cluster == "1.2.3.4"


@patch("httpx.Client.post")
def test_cancel_order(mock_post, mock_response, client):
    """Test order cancellation."""
    mock_response.json = Mock(return_value={"code": 0, "message": "Order cancelled"})
    mock_post.return_value = mock_response

    result = client.cancel_order(order_id=123, issue="Test cancellation")
    assert result["code"] == 0


# ── Spot Marketplace ────────────────────────────────


@patch("httpx.Client.get")
def test_spot_marketplace(mock_get, mock_response, client):
    """Test spot marketplace returns SpotMarket object."""
    mock_response.json = Mock(
        return_value={
            "code": 0,
            "market": {
                "currency_rates_in_usd": {
                    "bitcoin": 68022.87,
                    "CLORE-Blockchain": 0.002705,
                    "USD-Blockchain": 1,
                },
                "offers": [],
                "server": {
                    "min_pricing": {"CLORE-Blockchain": 739.23},
                    "mrl": 2520000,
                    "visibility": "public",
                    "online": False,
                },
            },
            "exists": True,
        }
    )
    mock_get.return_value = mock_response

    market = client.spot_marketplace(76279)
    assert market.offers == []
    assert market.server is not None
    assert market.server.online is False
    assert market.server.mrl == 2520000
    assert market.currency_rates_in_usd["bitcoin"] == 68022.87


# ── Rate Limit ────────────────────────────────


@patch("httpx.Client.get")
def test_rate_limit_error(mock_get, mock_response, client):
    """Test rate limit handling."""
    mock_response.json = Mock(return_value={"code": 5, "message": "Rate limit exceeded"})
    mock_get.return_value = mock_response

    with pytest.raises(RateLimitError):
        client.wallets()
