"""Test suite for FCP API client using Flow SDK test utilities."""

import os
import pytest
from unittest.mock import Mock, patch, mock_open

from mithril.client import FCPClient
from mithril.errors import APIError, ConfigError

# Import Flow SDK test utilities
from tests.conftest import (
    mock_env, 
    mock_http_client,
    sample_instance,
    sample_volume
)
from tests.fixtures.environment import (
    mock_fcp_environment,
    sample_bid_response,
    mock_api_responses
)
from tests.testing.builders import (
    InstanceBuilder,
    TaskBuilder
)
from tests.data.generators import TestDataGenerator


class TestFCPClient:
    """Test API client functionality using Flow SDK test infrastructure."""
    
    @pytest.fixture
    def client(self, mock_fcp_environment):
        """Create test client with mocked environment."""
        # mock_fcp_environment provides complete isolation
        return FCPClient()
        
    @pytest.fixture
    def data_generator(self):
        """Test data generator instance."""
        return TestDataGenerator()
        
    def test_init_missing_api_key(self, monkeypatch):
        """Test initialization without API key."""
        monkeypatch.delenv("FCP_API_KEY", raising=False)
        with pytest.raises(ConfigError, match="FCP_API_KEY not set"):
            FCPClient()
            
    def test_init_missing_project(self, monkeypatch):
        """Test initialization without project ID."""
        monkeypatch.setenv("FCP_API_KEY", "test-key")
        monkeypatch.delenv("FCP_PROJECT", raising=False)
        with pytest.raises(ConfigError, match="FCP_PROJECT not set"):
            FCPClient()
            
    def test_request_success(self, client, mock_http_client):
        """Test successful API request using Flow SDK mock."""
        expected = {"status": "ok"}
        mock_http_client.request.return_value.json.return_value = expected
        
        with patch.object(client.session, 'request', mock_http_client.request):
            result = client._request("GET", "/test")
            assert result == expected
            
    def test_request_api_error(self, client, data_generator):
        """Test API error handling with generated error response."""
        error_response = data_generator.generate_api_response(
            status_code=400,
            error_message="Invalid request"
        )
        
        with patch.object(client.session, 'request') as mock_request:
            mock_request.return_value.status_code = 400
            mock_request.return_value.json.return_value = error_response
            
            with pytest.raises(APIError) as exc_info:
                client._request("GET", "/test")
            assert exc_info.value.status_code == 400
            assert "Invalid request" in str(exc_info.value)
            
    def test_list_available(self, client):
        """Test listing available instances using builders."""
        # Use InstanceBuilder to create test data
        instance1 = InstanceBuilder().with_type("gpu.a100.1x").with_region("us-west-2").build()
        instance2 = InstanceBuilder().with_type("gpu.h100.2x").with_region("us-east-1").build()
        
        auctions = [
            {
                "instance_type": instance1["instance_type"],
                "region": instance1["region"],
                "gpu_count": 1,
                "last_instance_price": "$25.60",
                "capacity": 5
            },
            {
                "instance_type": instance2["instance_type"],
                "region": instance2["region"],
                "gpu_count": 2,
                "last_instance_price": "$50.00",
                "capacity": 3
            }
        ]
        
        with patch.object(client, '_request') as mock_request:
            mock_request.return_value = auctions
            result = client.list_available()
            assert len(result) == 2
            assert result[0]["region"] == "us-west-2"
            mock_request.assert_called_with("GET", "/spot/availability")
            
    def test_create_instance_success(self, client, sample_bid_response):
        """Test successful instance creation with Flow SDK fixtures."""
        ssh_key = "ssh-rsa AAAAB3... user@host"
        
        # Use sample_bid_response from Flow SDK fixtures
        auction_data = [{
            "instance_type": "it_abc123",
            "region": "us-west-2",
            "last_instance_price": "$25.60",
            "fid": "auction_123"
        }]
        
        with patch('builtins.open', mock_open(read_data=ssh_key)):
            with patch.object(client, '_request') as mock_request:
                with patch.object(client, '_resolve_instance_type_name') as mock_resolve:
                    mock_resolve.return_value = "gpu.a100.1x"
                    
                    mock_request.side_effect = [
                        [],  # SSH key list
                        {"fid": "key_456"},  # SSH key creation
                        auction_data,  # Available auctions
                        sample_bid_response  # Use Flow SDK fixture
                    ]
                    
                    result = client.create_instance("gpu.a100.1x")
                    assert result == sample_bid_response["fid"]
                    
    def test_create_instance_no_availability(self, client):
        """Test creation with no available instances."""
        ssh_key = "ssh-rsa AAAAB3... user@host"
        
        with patch('builtins.open', mock_open(read_data=ssh_key)):
            with patch.object(client, '_request') as mock_request:
                with patch.object(client, '_resolve_instance_type_name') as mock_resolve:
                    mock_resolve.return_value = "gpu.a100.1x"
                    mock_request.side_effect = [
                        [],  # SSH key list
                        {"fid": "key_456"},  # SSH key creation
                        []  # No available auctions
                    ]
                    
                    with pytest.raises(APIError, match="No availability"):
                        client.create_instance("gpu.a100.1x")
                        
    def test_get_instance_with_details(self, client, data_generator):
        """Test getting instance using generated task data."""
        # Use TestDataGenerator to create realistic task/bid data
        task_data = data_generator.generate_task_config(
            instance_type="gpu.a100.1x",
            region="us-west-2"
        )
        
        bid_data = {
            "id": "bid_123",
            "status": "RUNNING",
            "instance_type": task_data["instance"]["type"],
            "region": task_data["instance"]["region"],
            "created_at": "2024-01-01T00:00:00Z",
            "instance": {
                "status": "RUNNING",
                "public_ip": "1.2.3.4",
                "instance_type": task_data["instance"]["type"],
                "region": task_data["instance"]["region"]
            }
        }
        
        with patch.object(client, '_request') as mock_request:
            mock_request.return_value = bid_data
            result = client.get_instance("bid_123")
            
            assert result["id"] == "bid_123"
            assert result["status"] == "RUNNING"
            assert result["ip"] == "1.2.3.4"
            
    def test_delete_instance_success(self, client):
        """Test successful instance deletion."""
        with patch.object(client, '_request') as mock_request:
            result = client.delete_instance("bid_123")
            assert result is True
            mock_request.assert_called_with("DELETE", "/spot/bids/bid_123")
            
    def test_delete_instance_not_found(self, client):
        """Test deletion of non-existent instance."""
        with patch.object(client, '_request') as mock_request:
            mock_request.side_effect = APIError(404, "Not found")
            result = client.delete_instance("bid_123")
            assert result is False
            
    def test_list_my_instances(self, client, data_generator):
        """Test listing user's instances with generated data."""
        # Generate varied task statuses
        statuses = data_generator.generate_task_status_sequence(
            initial_status="PENDING",
            target_status="RUNNING"
        )
        
        bids_data = {
            "bids": [
                {
                    "id": "bid_123",
                    "status": "RUNNING",
                    "instance_type": "gpu.a100.1x",
                    "region": "us-west-2"
                },
                {
                    "id": "bid_456",
                    "status": "TERMINATED"
                },
                {
                    "id": "bid_789",
                    "status": "PENDING",
                    "instance_type": "gpu.h100.2x",
                    "region": "us-east-1"
                }
            ]
        }
        
        with patch.object(client, '_request') as mock_request:
            mock_request.return_value = bids_data
            result = client.list_my_instances()
            
            assert len(result) == 2  # Only PENDING and RUNNING
            assert all(r["status"] in ["PENDING", "RUNNING"] for r in result)
            
    def test_ensure_ssh_key_exists(self, client):
        """Test SSH key deduplication."""
        public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ user@host"
        existing_keys = [
            {
                "fid": "key_existing",
                "name": "old-key",
                "public_key": public_key
            }
        ]
        
        with patch.object(client, '_request') as mock_request:
            mock_request.return_value = existing_keys
            result = client._ensure_ssh_key(public_key)
            assert result == "key_existing"
            mock_request.assert_called_once()  # Only list, no create


class TestInstanceResolver:
    """Test instance type resolution."""
    
    def test_resolve_without_flow_sdk(self):
        """Test resolution when Flow SDK unavailable."""
        from mithril.catalog import InstanceResolver
        
        resolver = InstanceResolver()
        result = resolver.resolve("it_abc123")
        assert result == "it_abc123"  # Returns as-is
        
    def test_populate_from_api(self, mock_api_responses):
        """Test cache population using Flow SDK mock responses."""
        from mithril.catalog import InstanceResolver
        
        # Use mock_api_responses fixture to get realistic data
        instance_types_response = mock_api_responses("v2024-01-01")["instance_types"]
        
        resolver = InstanceResolver()
        resolver.populate_from_api(instance_types_response)
        
        # Verify populated data from mock responses
        for it in instance_types_response:
            if it.get("fid") and it.get("name"):
                assert resolver.resolve(it["fid"]) == it["name"]