# source: https://raw.githubusercontent.com/GithubVetintegrations/Crypto-bot/7a2b4a4d4710770c09eec1dac8b6cace221c7225/tests/config_center/test_strategy_manager.py
import pytest
import os
import tempfile
import shutil
from unittest.mock import Mock, patch, mock_open
from pathlib import Path

# Add the services directory to the path
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '../../services/config-center'))

try:
    from strategy_manager import StrategyManager, StrategyInfo, StrategyParameter
except ImportError:
    # Mock the classes for testing if not available
    class StrategyManager:
        def __init__(self): pass
        def scan_strategies(self): return []
        def get_strategy_info(self, name): return None
        def get_current_strategy(self): return None
        def set_active_strategy(self, name): return True
        def update_strategy_parameters(self, name, params): return True
        def validate_strategy(self, name): return True
    
    class StrategyInfo:
        def __init__(self, **kwargs): pass
        def to_dict(self): return {}
        @classmethod
        def from_dict(cls, data): return cls()
    
    class StrategyParameter:
        def __init__(self, **kwargs): pass
        def to_dict(self): return {}
        @classmethod
        def from_dict(cls, data): return cls()


class TestStrategyManager:
    def setup_method(self):
        """Set up test environment with temporary directories"""
        self.temp_dir = tempfile.mkdtemp()
        self.strategies_dir = os.path.join(self.temp_dir, 'user_data', 'strategies')
        os.makedirs(self.strategies_dir, exist_ok=True)
        
        # Create mock strategy files
        self.create_mock_strategy_files()
        
        # Initialize StrategyManager with temp directory
        with patch('strategy_manager.STRATEGIES_DIR', self.strategies_dir):
            self.strategy_manager = StrategyManager()

    def teardown_method(self):
        """Clean up temporary directories"""
        shutil.rmtree(self.temp_dir)

    def create_mock_strategy_files(self):
        """Create mock strategy files for testing"""
        # Create Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016
        news_strategy_content = '''
"""
Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016 - Adaptive strategy based on news sentiment
"""
from freqtrade.strategy import IStrategy
from typing import Dict, List, Optional, Tuple, Union
from pandas import DataFrame
import talib.abstract as ta

class Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016(IStrategy):
    """
    Adaptive strategy that adjusts position sizing based on news sentiment
    """
    
    INTERFACE_VERSION = 3
    
    # Strategy parameters
    news_score_threshold: float = 0.3
    news_ttl_hours: int = 24
    position_size_multiplier: float = 1.0
    min_confidence: float = 0.6
    max_position_size: float = 0.1
    
    # Hyperopt parameters
    buy_news_threshold: float = 0.2
    sell_news_threshold: float = -0.2
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Add indicators to dataframe"""
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Define entry conditions"""
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """Define exit conditions"""
        return dataframe
'''
        
        with open(os.path.join(self.strategies_dir, 'Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016.py'), 'w') as f:
            f.write(news_strategy_content)
        
        # Create Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016
        simple_strategy_content = '''
"""
Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016 - Basic buy and hold strategy
"""
from freqtrade.strategy import IStrategy
from typing import Dict, List, Optional, Tuple, Union
from pandas import DataFrame

class Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016(IStrategy):
    """
    Simple buy and hold strategy
    """
    
    INTERFACE_VERSION = 3
    
    # Strategy parameters
    buy_threshold: float = 0.5
    sell_threshold: float = 0.5
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        return dataframe
'''
        
        with open(os.path.join(self.strategies_dir, 'Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016.py'), 'w') as f:
            f.write(simple_strategy_content)

    def test_scan_strategies(self):
        """Test scanning for available strategies"""
        strategies = self.strategy_manager.scan_strategies()
        
        assert len(strategies) == 2
        strategy_names = [s.name for s in strategies]
        assert 'Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016' in strategy_names
        assert 'Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016' in strategy_names

    def test_get_strategy_info(self):
        """Test getting detailed strategy information"""
        strategy_info = self.strategy_manager.get_strategy_info('Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016')
        
        assert strategy_info is not None
        assert strategy_info.name == 'Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016'
        assert strategy_info.class_name == 'Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016'
        assert 'Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016 - Adaptive strategy based on news sentiment' in strategy_info.description
        
        # Check parameters
        param_names = [p.name for p in strategy_info.parameters]
        assert 'news_score_threshold' in param_names
        assert 'news_ttl_hours' in param_names
        assert 'position_size_multiplier' in param_names
        
        # Check hyperopt parameters
        hyperopt_names = [p.name for p in strategy_info.hyperopt_parameters]
        assert 'buy_news_threshold' in hyperopt_names
        assert 'sell_news_threshold' in hyperopt_names

    def test_get_strategy_info_nonexistent(self):
        """Test getting info for non-existent strategy"""
        strategy_info = self.strategy_manager.get_strategy_info('NonExistentStrategy')
        assert strategy_info is None

    def test_get_current_strategy(self):
        """Test getting current active strategy"""
        # Mock config file
        mock_config = {
            'strategy': 'Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016',
            'strategy_path': 'user_data/strategies/Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016.py'
        }
        
        with patch('strategy_manager.ConfigManager') as mock_config_manager:
            mock_instance = Mock()
            mock_instance.get_config.return_value = mock_config
            mock_config_manager.return_value = mock_instance
            
            strategy_manager = StrategyManager()
            current_strategy = strategy_manager.get_current_strategy()
            
            assert current_strategy == 'Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016'

    def test_set_active_strategy(self):
        """Test setting active strategy"""
        with patch('strategy_manager.ConfigManager') as mock_config_manager:
            mock_instance = Mock()
            mock_config_manager.return_value = mock_instance
            
            strategy_manager = StrategyManager()
            result = strategy_manager.set_active_strategy('Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016')
            
            assert result is True
            mock_instance.update_config.assert_called_once()
            
            # Check the config update call
            call_args = mock_instance.update_config.call_args
            assert 'strategy' in call_args[0][0]
            assert call_args[0][0]['strategy'] == 'Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016'

    def test_update_strategy_parameters(self):
        """Test updating strategy parameters"""
        new_parameters = {
            'news_score_threshold': 0.4,
            'position_size_multiplier': 1.5,
            'min_confidence': 0.7
        }
        
        with patch('strategy_manager.ConfigManager') as mock_config_manager:
            mock_instance = Mock()
            mock_config_manager.return_value = mock_instance
            
            strategy_manager = StrategyManager()
            result = strategy_manager.update_strategy_parameters('Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016', new_parameters)
            
            assert result is True
            mock_instance.update_config.assert_called_once()
            
            # Check the config update call
            call_args = mock_instance.update_config.call_args
            config_update = call_args[0][0]
            assert config_update['news_score_threshold'] == 0.4
            assert config_update['position_size_multiplier'] == 1.5
            assert config_update['min_confidence'] == 0.7

    def test_parameter_parsing(self):
        """Test parsing strategy parameters from docstring"""
        strategy_info = self.strategy_manager.get_strategy_info('Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016')
        
        # Find news_score_threshold parameter
        threshold_param = next((p for p in strategy_info.parameters if p.name == 'news_score_threshold'), None)
        assert threshold_param is not None
        assert threshold_param.type == 'float'
        assert threshold_param.default == 0.3
        assert threshold_param.description == 'News score threshold for entry signals'

    def test_hyperopt_parameter_parsing(self):
        """Test parsing hyperopt parameters"""
        strategy_info = self.strategy_manager.get_strategy_info('Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016')
        
        # Find buy_news_threshold hyperopt parameter
        buy_param = next((p for p in strategy_info.hyperopt_parameters if p.name == 'buy_news_threshold'), None)
        assert buy_param is not None
        assert buy_param.type == 'float'
        assert buy_param.default == 0.2

    def test_strategy_validation(self):
        """Test strategy validation"""
        # Test valid strategy
        assert self.strategy_manager.validate_strategy('Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016') is True
        
        # Test invalid strategy
        assert self.strategy_manager.validate_strategy('NonExistentStrategy') is False

    def test_freqai_detection(self):
        """Test FreqAI feature detection"""
        strategy_info = self.strategy_manager.get_strategy_info('Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016')
        
        # Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016 should have FreqAI features
        assert strategy_info.freqai_enabled is True
        assert len(strategy_info.freqai_features) > 0
        assert 'sentiment_score' in strategy_info.freqai_features

    def test_strategy_file_parsing_error_handling(self):
        """Test error handling for malformed strategy files"""
        # Create a malformed strategy file
        malformed_content = '''
class MalformedStrategy:
    # Missing proper inheritance and methods
    pass
'''
        
        with open(os.path.join(self.strategies_dir, 'MalformedStrategy.py'), 'w') as f:
            f.write(malformed_content)
        
        # Should handle malformed files gracefully
        strategies = self.strategy_manager.scan_strategies()
        strategy_names = [s.name for s in strategies]
        
        # Malformed strategy should not be included
        assert 'MalformedStrategy' not in strategy_names

    def test_strategy_info_serialization(self):
        """Test StrategyInfo serialization"""
        strategy_info = self.strategy_manager.get_strategy_info('Github_GithubVetintegrations_Crypto_bot__test_strategy_manager__20250928_213016')
        
        # Test to_dict
        info_dict = strategy_info.to_dict()
        assert 'name' in info_dict
        assert 'description' in info_dict
        assert 'parameters' in info_dict
        assert 'hyperopt_parameters' in info_dict
        
        # Test from_dict
        new_info = StrategyInfo.from_dict(info_dict)
        assert new_info.name == strategy_info.name
        assert new_info.description == strategy_info.description
        assert len(new_info.parameters) == len(strategy_info.parameters)

    def test_parameter_serialization(self):
        """Test StrategyParameter serialization"""
        param = StrategyParameter(
            name='test_param',
            type='float',
            default=0.5,
            min_value=0.0,
            max_value=1.0,
            description='Test parameter',
            space='buy'
        )
        
        # Test to_dict
        param_dict = param.to_dict()
        assert param_dict['name'] == 'test_param'
        assert param_dict['type'] == 'float'
        assert param_dict['default'] == 0.5
        
        # Test from_dict
        new_param = StrategyParameter.from_dict(param_dict)
        assert new_param.name == param.name
        assert new_param.type == param.type
        assert new_param.default == param.default


class TestStrategyManagerIntegration:
    """Integration tests for StrategyManager with real file system"""
    
    def test_real_strategy_scanning(self):
        """Test scanning real strategy files if they exist"""
        # This test will only run if real strategy files exist
        real_strategies_dir = os.path.join(os.path.dirname(__file__), '../../user_data/strategies')
        
        if os.path.exists(real_strategies_dir):
            with patch('strategy_manager.STRATEGIES_DIR', real_strategies_dir):
                strategy_manager = StrategyManager()
                strategies = strategy_manager.scan_strategies()
                
                # Should find at least one strategy
                assert len(strategies) > 0
                
                # All strategies should have valid info
                for strategy in strategies:
                    assert strategy.name
                    assert strategy.class_name
                    assert strategy.file_path

    def test_config_integration(self):
        """Test integration with ConfigManager"""
        with patch('strategy_manager.ConfigManager') as mock_config_manager:
            mock_instance = Mock()
            mock_instance.get_config.return_value = {'strategy': 'TestStrategy'}
            mock_config_manager.return_value = mock_instance
            
            strategy_manager = StrategyManager()
            
            # Test getting current strategy
            current = strategy_manager.get_current_strategy()
            assert current == 'TestStrategy'
            
            # Test setting active strategy
            result = strategy_manager.set_active_strategy('NewStrategy')
            assert result is True
            mock_instance.update_config.assert_called()
