"""Tests for Celery tasks."""

import pytest
from unittest.mock import Mock, patch, MagicMock, AsyncMock


class TestAsyncToSync:
    """Tests for async_to_sync helper."""
    
    def test_runs_awaitable(self):
        """Test that async_to_sync runs awaitable and returns result."""
        from src.worker.tasks import async_to_sync
        
        async def async_func():
            return "test_result"
        
        result = async_to_sync(async_func())
        assert result == "test_result"
    
    def test_handles_exception(self):
        """Test that async_to_sync propagates exceptions."""
        from src.worker.tasks import async_to_sync
        
        async def async_error():
            raise ValueError("test error")
        
        with pytest.raises(ValueError, match="test error"):
            async_to_sync(async_error())


class TestTrainModelTask:
    """Tests for train_model_task."""
    
    @patch('src.worker.tasks.get_strategy_selector')
    @patch('src.worker.tasks.async_to_sync')
    def test_train_model_basic(self, mock_async_to_sync, mock_get_selector):
        """Test basic model training task."""
        # Setup mocks
        mock_selector = Mock()
        mock_selector.bootstrap_symbols = ["BTC/USD"]
        mock_get_selector.return_value = mock_selector
        
        mock_async_to_sync.side_effect = [
            {"X": [1, 2, 3]},  # prepare_training_data result
            {"accuracy": 0.9}  # train_model result
        ]
        
        from src.worker.tasks import train_model_task
        
        # Call the task directly - Celery will bind self automatically
        # For testing, we need to access the underlying function
        result = train_model_task.run(force_retrain=True, bootstrap=False)
        
        assert result == {"accuracy": 0.9}
        mock_get_selector.assert_called_once()
    
    @patch('src.worker.tasks.get_strategy_selector')
    @patch('src.worker.tasks.async_to_sync')
    def test_train_model_with_bootstrap(self, mock_async_to_sync, mock_get_selector):
        """Test model training with bootstrapping."""
        mock_selector = Mock()
        mock_selector.bootstrap_symbols = ["BTC/USD", "ETH/USD"]
        mock_get_selector.return_value = mock_selector
        
        # First call returns empty data, triggering bootstrap
        mock_async_to_sync.side_effect = [
            {"X": []},  # Empty training data
            {"total_samples": 100},  # First symbol bootstrap
            {"total_samples": 50},  # Second symbol bootstrap
            {"accuracy": 0.85}  # Final training
        ]
        
        from src.worker.tasks import train_model_task
        
        result = train_model_task.run(force_retrain=False, bootstrap=True)
        
        assert result == {"accuracy": 0.85}


class TestBootstrapTask:
    """Tests for bootstrap_task."""
    
    @patch('src.worker.tasks.get_strategy_selector')
    @patch('src.worker.tasks.async_to_sync')
    def test_bootstrap_basic(self, mock_async_to_sync, mock_get_selector):
        """Test basic bootstrap task."""
        mock_selector = Mock()
        mock_get_selector.return_value = mock_selector
        mock_async_to_sync.return_value = {"total_samples": 200}
        
        from src.worker.tasks import bootstrap_task
        
        result = bootstrap_task.run(days=90, timeframe="1h")
        
        assert result == {"total_samples": 200}


class TestGenerateReportTask:
    """Tests for generate_report_task."""
    
    @patch('src.worker.tasks.async_to_sync')
    def test_generate_report_unknown_type(self, mock_async_to_sync):
        """Test report generation with unknown type."""
        from src.worker.tasks import generate_report_task
        
        result = generate_report_task.run("unknown", {})
        
        assert result["status"] == "error"
        assert "Unknown report type" in result["message"]


class TestOptimizeStrategyTask:
    """Tests for optimize_strategy_task."""
    
    @patch('src.optimization.genetic.GeneticOptimizer')
    def test_optimize_genetic_basic(self, mock_optimizer_class):
        """Test basic genetic optimization."""
        from src.worker.tasks import optimize_strategy_task
        
        mock_optimizer = Mock()
        mock_optimizer.optimize.return_value = {
            "best_params": {"period": 14},
            "best_score": 0.85
        }
        mock_optimizer_class.return_value = mock_optimizer
        
        result = optimize_strategy_task.run(
            strategy_type="rsi",
            symbol="BTC/USD",
            param_ranges={"period": (5, 50)},
            method="genetic",
            population_size=10,
            generations=5
        )
        
        assert result["best_params"] == {"period": 14}
        assert result["best_score"] == 0.85
    
    def test_optimize_unknown_method(self):
        """Test optimization with unknown method."""
        from src.worker.tasks import optimize_strategy_task
        
        result = optimize_strategy_task.run(
            strategy_type="rsi",
            symbol="BTC/USD",
            param_ranges={"period": (5, 50)},
            method="unknown_method"
        )
        
        assert "error" in result


class TestExportDataTask:
    """Tests for export_data_task."""
    
    @patch('src.reporting.csv_exporter.get_csv_exporter')
    @patch('src.worker.tasks.async_to_sync')
    def test_export_orders(self, mock_async_to_sync, mock_exporter_func):
        """Test order export."""
        mock_exporter = Mock()
        mock_exporter.export_orders.return_value = True
        mock_exporter_func.return_value = mock_exporter
        mock_async_to_sync.return_value = []  # Empty orders list
        
        from src.worker.tasks import export_data_task
        
        result = export_data_task.run("orders", {})
        
        assert result["status"] == "success"
        assert result["export_type"] == "orders"
    
    def test_export_unknown_type(self):
        """Test export with unknown type."""
        from src.worker.tasks import export_data_task
        
        result = export_data_task.run("unknown", {})
        
        assert result["status"] == "error"
