"""Test suite for CLI functionality using Flow SDK test utilities."""

import sys
import pytest
from unittest.mock import Mock, patch, call
from io import StringIO

from mithril.__main__ import (
    format_table, cmd_list, cmd_create, cmd_status, 
    cmd_delete, cmd_ssh, main
)
from mithril.errors import APIError, ConfigError, SSHError

# Import Flow SDK test utilities
from tests.conftest import create_mock_config, test_config_context
from tests.fixtures.environment import mock_fcp_environment, authenticated_flow
from tests.testing.builders import InstanceBuilder, TaskBuilder
from tests.testing.base import IntegrationTest
from tests.testing.helpers import get_available_instance_for_test
from tests.data.generators import TestDataGenerator


class TestFormatTable:
    """Test table formatting functionality."""
    
    def test_empty_data(self):
        """Test formatting empty table."""
        result = format_table([], ["col1", "col2"])
        assert result == "No results"
        
    def test_single_row(self):
        """Test formatting single row."""
        data = [{"name": "test", "value": 123}]
        result = format_table(data, ["name", "value"])
        lines = result.split("\n")
        assert len(lines) == 3  # Header, separator, data
        assert "NAME" in lines[0]
        assert "VALUE" in lines[0]
        assert "test" in lines[2]
        assert "123" in lines[2]
        
    def test_column_alignment(self):
        """Test column width calculation."""
        data = [
            {"short": "a", "long": "very long value"},
            {"short": "bb", "long": "x"}
        ]
        result = format_table(data, ["short", "long"])
        lines = result.split("\n")
        # Check that columns are properly aligned
        assert lines[2].index("very") == lines[3].index("x")


class TestListCommand:
    """Test list command functionality with Flow SDK utilities."""
    
    @pytest.fixture
    def data_generator(self):
        """Test data generator instance."""
        return TestDataGenerator()
        
    @pytest.fixture
    def mock_args(self):
        """Create mock arguments."""
        args = Mock()
        args.mine = False
        args.available = False
        return args
        
    def test_list_available(self, mock_fcp_environment, mock_args, capsys, data_generator):
        """Test listing available instances with generated data."""
        # Use InstanceBuilder and TestDataGenerator for realistic data
        instances = [
            InstanceBuilder().with_type("gpu.a100.1x").with_region("us-west-2").build(),
            InstanceBuilder().with_type("gpu.h100.2x").with_region("us-east-1").build()
        ]
        
        auctions = []
        for inst in instances:
            auction = data_generator.generate_api_response(
                endpoint="spot/availability",
                data={
                    "instance_type": inst["instance_type"],
                    "region": inst["region"],
                    "gpu_count": inst.get("gpu_count", 1),
                    "last_instance_price": "$25.60",
                    "capacity": 5
                }
            )
            auctions.append(auction["data"])
            
        mock_client = Mock()
        mock_client.list_available.return_value = auctions
        mock_client._resolve_instance_type_name.side_effect = ["gpu.a100.1x", "gpu.h100.2x"]
        
        with patch('mithril.__main__.FCPClient', return_value=mock_client):
            cmd_list(mock_args)
            
        captured = capsys.readouterr()
        assert "gpu.a100.1x" in captured.out
        assert "gpu.h100.2x" in captured.out
        assert "us-west-2" in captured.out
        assert "us-east-1" in captured.out
        
    def test_list_mine(self, mock_fcp_environment, mock_args, capsys):
        """Test listing user's instances with Flow SDK task builders."""
        mock_args.mine = True
        
        # Use TaskBuilder to create realistic instance data
        task1 = TaskBuilder().with_status("RUNNING").with_instance_type("gpu.a100.1x").build()
        task2 = TaskBuilder().with_status("PENDING").with_instance_type("gpu.h100.2x").build()
        
        instances = [
            {
                "id": task1["id"],
                "type": task1["instance"]["type"],
                "status": task1["status"],
                "region": task1["instance"]["region"]
            },
            {
                "id": task2["id"],
                "type": task2["instance"]["type"],
                "status": task2["status"],
                "region": task2["instance"]["region"]
            }
        ]
        
        mock_client = Mock()
        mock_client.list_my_instances.return_value = instances
        
        with patch('mithril.__main__.FCPClient', return_value=mock_client):
            cmd_list(mock_args)
            
        captured = capsys.readouterr()
        assert task1["id"] in captured.out
        assert task2["id"] in captured.out
        assert "RUNNING" in captured.out
        assert "PENDING" in captured.out


class TestCreateCommand:
    """Test create command functionality."""
    
    @pytest.fixture
    def mock_args(self):
        """Create mock arguments."""
        args = Mock()
        args.type = "gpu.a100.1x"
        args.region = None
        args.ssh_key = None
        return args
        
    def test_create_success(self, mock_fcp_environment, mock_args, capsys):
        """Test successful instance creation using Flow SDK builders."""
        # Use InstanceBuilder for consistent test data
        instance = InstanceBuilder().with_type("gpu.a100.1x").with_region("us-west-2").build()
        
        auction = {
            "instance_type": instance["instance_type"],
            "region": instance["region"],
            "last_instance_price": "$25.60"
        }
        
        mock_client = Mock()
        mock_client.list_available.return_value = [auction]
        mock_client._resolve_instance_type_name.return_value = "gpu.a100.1x"
        mock_client.create_instance.return_value = "bid_123"
        
        with patch('mithril.__main__.FCPClient', return_value=mock_client):
            with patch('mithril.__main__.time'):
                cmd_create(mock_args)
                
        captured = capsys.readouterr()
        assert "bid_123" in captured.out
        assert "PROVISIONING" in captured.out
        assert "8-12 minutes" in captured.out
        
    def test_create_no_availability(self, mock_fcp_environment, mock_args, capsys):
        """Test creation with no availability."""
        mock_client = Mock()
        mock_client.list_available.return_value = []
        
        with patch('mithril.__main__.FCPClient', return_value=mock_client):
            with pytest.raises(SystemExit):
                cmd_create(mock_args)
                
        captured = capsys.readouterr()
        assert "No availability" in captured.err
        assert "mithril list --available" in captured.err


class TestStatusCommand:
    """Test status command functionality."""
    
    @pytest.fixture
    def mock_args(self):
        """Create mock arguments."""
        args = Mock()
        args.id = "bid_123"
        return args
        
    def test_status_running(self, mock_fcp_environment, mock_args, capsys, data_generator):
        """Test status for running instance with generated data."""
        # Use TestDataGenerator for realistic status progression
        statuses = data_generator.generate_task_status_sequence(
            initial_status="PENDING",
            target_status="RUNNING"
        )
        
        instance = {
            "id": "bid_123",
            "status": statuses[-1],  # Final status
            "type": "gpu.a100.1x",
            "region": "us-west-2",
            "ip": "1.2.3.4",
            "created": "2024-01-01T00:00:00Z"
        }
        
        mock_client = Mock()
        mock_client.get_instance.return_value = instance
        
        with patch('mithril.__main__.FCPClient', return_value=mock_client):
            cmd_status(mock_args)
            
        captured = capsys.readouterr()
        assert "RUNNING" in captured.out
        assert "1.2.3.4" in captured.out
        assert "ubuntu@1.2.3.4" in captured.out


class TestDeleteCommand:
    """Test delete command functionality."""
    
    @pytest.fixture
    def mock_args(self):
        """Create mock arguments."""
        args = Mock()
        args.id = "bid_123"
        return args
        
    def test_delete_success(self, mock_fcp_environment, mock_args, capsys):
        """Test successful deletion."""
        mock_client = Mock()
        mock_client.delete_instance.return_value = True
        
        with patch('mithril.__main__.FCPClient', return_value=mock_client):
            cmd_delete(mock_args)
            
        captured = capsys.readouterr()
        assert "Instance deleted: bid_123" in captured.out


class TestSSHCommand:
    """Test SSH command functionality."""
    
    @pytest.fixture
    def mock_args(self):
        """Create mock arguments."""
        args = Mock()
        args.id = "bid_123"
        args.ssh_key = None
        return args
        
    def test_ssh_success(self, mock_fcp_environment, mock_args):
        """Test successful SSH connection."""
        mock_client = Mock()
        mock_client.get_instance.return_value = {
            "id": "bid_123",
            "ip": "1.2.3.4"
        }
        
        with patch('mithril.__main__.FCPClient', return_value=mock_client):
            with patch('mithril.__main__.ssh_to_instance') as mock_ssh:
                cmd_ssh(mock_args)
                mock_ssh.assert_called_once_with("1.2.3.4", ssh_key=None)


class TestMainFunction:
    """Test main entry point."""
    
    def test_no_command(self, capsys):
        """Test help display with no command."""
        with patch('sys.argv', ['mithril']):
            with pytest.raises(SystemExit):
                main()
                
        captured = capsys.readouterr()
        assert "usage: mithril" in captured.out
        
    def test_environment_check(self, monkeypatch):
        """Test environment variable validation."""
        monkeypatch.delenv("FCP_API_KEY", raising=False)
        
        with patch('sys.argv', ['mithril', 'list']):
            with pytest.raises(SystemExit):
                main()


class TestMithrilIntegration(IntegrationTest):
    """Integration tests for mithril using Flow SDK base class.
    
    These tests will automatically skip if FCP API is not available.
    """
    
    def test_full_instance_lifecycle(self, flow, available_instance):
        """Test complete instance lifecycle with real API."""
        # This test inherits automatic API health checking from IntegrationTest
        # and gets pre-validated available_instance fixture
        
        from mithril.client import FCPClient
        client = FCPClient()
        
        # Create instance using available instance type
        instance_type = available_instance["instance_type"]
        region = available_instance["region"]
        
        # Test creation
        bid_id = client.create_instance(instance_type, region=region)
        assert bid_id is not None
        
        # Test status check
        instance = client.get_instance(bid_id)
        assert instance["id"] == bid_id
        assert instance["status"] in ["PENDING", "PROVISIONING", "RUNNING"]
        
        # Test listing
        my_instances = client.list_my_instances()
        assert any(inst["id"] == bid_id for inst in my_instances)
        
        # Clean up
        deleted = client.delete_instance(bid_id)
        assert deleted is True