import sys
import os

# 添加项目根目录到Python路径
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..')))

from myffe.tools.Loggerv1_1 import log_class
from myffe.tools.color_printer import ColorPrinter
from myffe.tools.evaluation_decorator import with_evaluation
from myffe.tools.data_inspector import inspect_data
from myffe.tools.param_utils import convert_to_list
from myffe.preprocessing.function.relativefunction import linear_plus, feature_plus, feature_importance, pca_lda_kernel

@log_class(type='use')
class RelativePreprocessing:
    def __init__(self, shortcut_relative=None):
        self.shortcut_relative = shortcut_relative

    @with_evaluation
    def __call__(self, data, data_label=None):
        self.data_label = data_label
        
        self.data = data.copy()
        ColorPrinter.print_progress('开始相关性分析和特征工程')
        
        # 使用 pandas 处理数据
        ColorPrinter.print_info('使用 pandas 处理')
        processed_data = self._process_with_pandas(data.copy())
        
        # 返回完整数据
        return processed_data
    
    def _process_with_pandas(self, data):
        """使用pandas处理数据"""
        # 统一使用一个处理方法，不再区分普通和高级模式
        data = self.relative_normal_switch(data)
        return data



    def relative_normal_switch(self, data):
        # 直接检查各个方法的参数是否存在，决定执行哪些特征工程方法
        
        try:
            # 线性特征组合
            linear_cols = self.shortcut_relative.get('linear_cols')
            if linear_cols:
                threshold = self.shortcut_relative.get('linear_threshold', 0.75)
                data = linear_plus(data, linear_cols, self.data_label, threshold)

            # 非线性特征构建
            feature_cols = self.shortcut_relative.get('feature_cols')
            if feature_cols:
                threshold = self.shortcut_relative.get('feature_threshold', 0.75)
                relation_func = self.shortcut_relative.get('relation_func', 'func1')
                data = feature_plus(data, feature_cols, self.data_label, threshold, relation_func)

            # 特征重要性评估
            feature_cols = self.shortcut_relative.get('feature_cols')
            if feature_cols:
                n_features = self.shortcut_relative.get('n_features', 10)
                data = feature_importance(data, feature_cols, self.data_label, n_features)

            # 降维处理
            pca_cols = self.shortcut_relative.get('pca_cols')
            if pca_cols:
                n_components = self.shortcut_relative.get('n_components', 10)
                kernel_selection = self.shortcut_relative.get('kernel_selection', 'pca')
                data = pca_lda_kernel(data, pca_cols, self.data_label, n_components, kernel_selection)

        except Exception as e:
            ColorPrinter.print_error(f'特征工程处理出错: {e}')

        return data




"""
Parameter documentation:

shortcut_relative parameter dictionary:
    - linear_cols: Columns for linear combination
      Type: list of column names
      Default: None (must be specified in linear_plus mode)
      Description: Select columns from numeric columns for linear combination
    
    - linear_threshold: Correlation coefficient threshold for linear combination
      Type: float
      Optional values: 0.0 - 1.0
      Default: None (must be specified in linear_plus mode)
      Description: Columns with correlation coefficient greater than this value will be linearly combined
    
    - feature_cols: Columns for feature construction
      Type: list of column names
      Default: None (must be specified in feature_plus mode)
      Description: Select columns from numeric columns for feature construction
    
    - feature_threshold: R² threshold for feature construction
      Type: float
      Optional values: 0.0 - 1.0
      Default: None (must be specified in feature_plus mode)
      Description: New features will only be added if the fitted R² value is greater than this threshold
    
    - relation_func: Function type for feature construction
      Type: string
      Optional values: 'func1', 'func2', 'func3', 'func4', 'func5'
      Default: 'func1'
      Description: Select function type for fitting:
        - 'func1': Quadratic function a*x^2 + b*x + c
        - 'func2': Cubic function a*x^3 + b*x^2 + c*x + d
        - 'func3': Exponential function a*exp(b*x) + c
        - 'func4': Logarithmic function a*log(x) + b
        - 'func5': Square root function a*sqrt(x) + b
      Note: This parameter must be specified in feature_plus mode
    
    - pca_cols: Columns for dimensionality reduction
      Type: list of column names
      Default: None (must be specified in pca_lda_kernel mode)
      Description: Select columns from numeric columns for dimensionality reduction
    
    - n_components: Number of dimensions after dimensionality reduction
      Type: integer
      Optional values: >= 1
      Default: None (must be specified in pca_lda_kernel mode)
      Description: Number of features after dimensionality reduction
    
    - kernel_selection: Dimensionality reduction method selection
      Type: string
      Optional values: 'pca', 'lda', 'kernel'
      Default: None (must be specified in pca_lda_kernel mode)
      Description: 
        - 'pca': Principal Component Analysis
        - 'lda': Linear Discriminant Analysis (requires label column)
        - 'kernel': Kernel Principal Component Analysis
    
    - n_features: Number of features for importance evaluation
      Type: integer
      Optional values: >= 1
      Default: 10
      Description: Number of features to retain after feature importance evaluation (only used in feature_importance mode)

Usage example:
    # Example 1: Using quadratic function for feature construction
    relative_params = {
        'feature_cols': ['col1', 'col2', 'col3'],
        'feature_threshold': 0.75,
        'relation_func': 'func1'
    }
    relative_processor = RelativePreprocessing(shortcut_relative=relative_params)
    processed_data = relative_processor(data, 'label')
    
    # Example 2: Using cubic function for feature construction
    relative_params = {
        'feature_cols': ['col1', 'col2', 'col3'],
        'feature_threshold': 0.8,
        'relation_func': 'func2'
    }
    
    # Example 3: Using exponential function for feature construction
    relative_params = {
        'feature_cols': ['col1', 'col2'],
        'feature_threshold': 0.7,
        'relation_func': 'func3'
    }
    
    # Example 4: Using logarithmic function for feature construction
    relative_params = {
        'feature_cols': ['col1', 'col2'],
        'feature_threshold': 0.7,
        'relation_func': 'func4'
    }
    
    # Example 5: Using square root function for feature construction
    relative_params = {
        'feature_cols': ['col1', 'col2'],
        'feature_threshold': 0.7,
        'relation_func': 'func5'
    }
    
    # Example 6: Using feature importance evaluation
    relative_params = {
        'feature_cols': ['col1', 'col2', 'col3', 'col4', 'col5'],
        'n_features': 3
    }
    relative_processor = RelativePreprocessing(shortcut_relative=relative_params)
    processed_data = relative_processor(data, 'label')
"""


if __name__ == "__main__":
    import pandas as pd
    import numpy as np
    
    print("=" * 60)
    print("相关性分析和特征工程测试程序")
    print("=" * 60)
    
    # 创建测试数据
    np.random.seed(42)
    test_data = pd.DataFrame({
        'feature1': np.random.normal(50, 10, 50),
        'feature2': np.random.normal(100, 20, 50),
        'feature3': np.random.normal(200, 30, 50),
        'feature4': np.random.normal(300, 40, 50),
        'label': ['A'] * 25 + ['B'] * 25
    })
    
    # 添加一些相关性
    test_data['feature5'] = test_data['feature1'] * 2 + np.random.normal(0, 5, 50)
    test_data['feature6'] = test_data['feature2'] * 1.5 + np.random.normal(0, 5, 50)
    
    print("\n原始数据:")
    print(test_data.head(10))
    print(f"\n数据形状: {test_data.shape}")
    
    # 测试不同的特征工程方式
    test_cases = [
        {
            'params': {
                'linear_cols': ['feature1', 'feature2', 'feature3', 'feature4', 'feature5', 'feature6'],
                'linear_threshold': 0.8
            },
            'description': '线性特征组合'
        },
        {
            'params': {
                'feature_cols': ['feature1', 'feature2', 'feature3'],
                'feature_threshold': 0.8,
                'relation_func': 'func1'
            },
            'description': '非线性特征构建-二次函数'
        },
        {
            'params': {
                'feature_cols': ['feature1', 'feature2', 'feature3'],
                'feature_threshold': 0.8,
                'relation_func': 'func2'
            },
            'description': '非线性特征构建-三次函数'
        },
        {
            'params': {
                'feature_cols': ['feature1', 'feature2'],
                'feature_threshold': 0.7,
                'relation_func': 'func3'
            },
            'description': '非线性特征构建-指数函数'
        },
        {
            'params': {
                'pca_cols': ['feature1', 'feature2', 'feature3', 'feature4', 'feature5', 'feature6'],
                'n_components': 3,
                'kernel_selection': 'pca'
            },
            'description': 'PCA降维'
        },
        {
            'params': {},
            'description': '跳过特征工程'
        }
    ]
    
    for i, test_case in enumerate(test_cases, 1):
        print(f"\n{'=' * 60}")
        print(f"测试 {i}: {test_case['description']}")
        print('=' * 60)
        
        relative_processor = RelativePreprocessing(shortcut_relative=test_case['params'])
        try:
            processed_data = relative_processor(test_data.copy(), 'label')
            
            print("\n处理后的数据形状:")
            print(f"原始数据: {test_data.shape}")
            print(f"处理后数据: {processed_data.shape}")
            
            if processed_data.shape[1] != test_data.shape[1]:
                print("\n新增/删除的列:")
                original_cols = set(test_data.columns)
                processed_cols = set(processed_data.columns)
                added_cols = processed_cols - original_cols
                removed_cols = original_cols - processed_cols
                if added_cols:
                    print(f"新增列: {added_cols}")
                if removed_cols:
                    print(f"删除列: {removed_cols}")
        except Exception as e:
            print(f"\n处理出错: {e}")
    
    print("\n" + "=" * 60)
    print("测试完成！")
    print("=" * 60)
    
    # 深度渗透测试套件
    print(f"\n{'=' * 80}")
    print("深度渗透测试套件")
    print('=' * 80)
    
    # 创建更大的测试数据
    np.random.seed(42)
    large_test_data = pd.DataFrame({
        'feature1': np.random.normal(50, 10, 1000),
        'feature2': np.random.normal(100, 20, 1000),
        'feature3': np.random.normal(200, 30, 1000),
        'feature4': np.random.normal(300, 40, 1000),
        'feature5': np.random.normal(400, 50, 1000),
        'feature6': np.random.normal(500, 60, 1000),
        'feature7': np.random.normal(600, 70, 1000),
        'feature8': np.random.normal(700, 80, 1000),
        'feature9': np.random.normal(800, 90, 1000),
        'feature10': np.random.normal(900, 100, 1000),
        'label': np.random.choice(['A', 'B', 'C'], 1000)
    })
    
    # 添加一些强相关性
    large_test_data['feature11'] = large_test_data['feature1'] * 2 + np.random.normal(0, 2, 1000)
    large_test_data['feature12'] = large_test_data['feature2'] * 3 + np.random.normal(0, 3, 1000)
    large_test_data['feature13'] = large_test_data['feature3'] * 1.5 + np.random.normal(0, 1.5, 1000)
    
    # 测试1: 边界条件测试 - 空参数
    print(f"\n{'-' * 60}")
    print("测试1: 边界条件测试 - 空参数")
    print('-' * 60)
    
    relative_processor_1 = RelativePreprocessing(shortcut_relative={})
    result_1 = relative_processor_1(large_test_data.copy(), 'label')
    
    print(f"\n数据形状变化: {large_test_data.shape} -> {result_1.shape}")
    print("期望：数据保持不变")
    
    # 测试2: 边界条件测试 - 不存在的列
    print(f"\n{'-' * 60}")
    print("测试2: 边界条件测试 - 不存在的列")
    print('-' * 60)
    
    params_nonexistent = {
        'linear_cols': ['nonexistent_col', 'feature1', 'feature2'],
        'linear_threshold': 0.8
    }
    relative_processor_2 = RelativePreprocessing(shortcut_relative=params_nonexistent)
    result_2 = relative_processor_2(large_test_data.copy(), 'label')
    
    print(f"\n数据形状变化: {large_test_data.shape} -> {result_2.shape}")
    print("期望：只处理存在的列")
    
    # 测试3: 边界条件测试 - 列数不足
    print(f"\n{'-' * 60}")
    print("测试3: 边界条件测试 - 列数不足")
    print('-' * 60)
    
    params_few_cols = {
        'linear_cols': ['feature1'],
        'linear_threshold': 0.8
    }
    relative_processor_3 = RelativePreprocessing(shortcut_relative=params_few_cols)
    result_3 = relative_processor_3(large_test_data.copy(), 'label')
    
    print(f"\n数据形状变化: {large_test_data.shape} -> {result_3.shape}")
    print("期望：数据保持不变，至少需要2列")
    
    # 测试4: 边界条件测试 - 阈值边界值
    print(f"\n{'-' * 60}")
    print("测试4: 边界条件测试 - 阈值边界值")
    print('-' * 60)
    
    test_cases_threshold = [
        {'threshold': 0.0, 'desc': '阈值为0'},
        {'threshold': 1.0, 'desc': '阈值为1'},
    ]
    
    for tc in test_cases_threshold:
        params_threshold = {
            'linear_cols': ['feature1', 'feature2', 'feature3', 'feature11', 'feature12'],
            'linear_threshold': tc['threshold']
        }
        relative_processor = RelativePreprocessing(shortcut_relative=params_threshold)
        result = relative_processor(large_test_data.copy(), 'label')
        print(f"\n{tc['desc']}:")
        print(f"  数据形状变化: {large_test_data.shape} -> {result.shape}")
    
    # 测试5: 边界条件测试 - 只有一个数值列
    print(f"\n{'-' * 60}")
    print("测试5: 边界条件测试 - 只有一个数值列")
    print('-' * 60)
    
    data_single_numeric = pd.DataFrame({
        'numeric_col': np.random.normal(50, 10, 100),
        'str_col': ['a'] * 100,
        'label': ['A'] * 50 + ['B'] * 50
    })
    
    params_single_numeric = {
        'linear_cols': ['numeric_col', 'str_col'],
        'linear_threshold': 0.8
    }
    relative_processor_5 = RelativePreprocessing(shortcut_relative=params_single_numeric)
    result_5 = relative_processor_5(data_single_numeric.copy(), 'label')
    
    print(f"\n数据形状变化: {data_single_numeric.shape} -> {result_5.shape}")
    print("期望：数据保持不变，需要至少2个数值列")
    
    # 测试6: 边界条件测试 - 没有数值列
    print(f"\n{'-' * 60}")
    print("测试6: 边界条件测试 - 没有数值列")
    print('-' * 60)
    
    data_no_numeric = pd.DataFrame({
        'str_col1': ['a', 'b', 'c', 'd', 'e'] * 20,
        'str_col2': ['x', 'y', 'z', 'w', 'v'] * 20,
        'label': ['A'] * 50 + ['B'] * 50
    })
    
    params_no_numeric = {
        'linear_cols': ['str_col1', 'str_col2'],
        'linear_threshold': 0.8
    }
    relative_processor_6 = RelativePreprocessing(shortcut_relative=params_no_numeric)
    result_6 = relative_processor_6(data_no_numeric.copy(), 'label')
    
    print(f"\n数据形状变化: {data_no_numeric.shape} -> {result_6.shape}")
    print("期望：数据保持不变")
    
    # 测试7: 边界条件测试 - 空数据
    print(f"\n{'-' * 60}")
    print("测试7: 边界条件测试 - 空数据")
    print('-' * 60)
    
    empty_data = pd.DataFrame({
        'feature1': [],
        'feature2': [],
        'label': []
    })
    
    params_empty = {
        'linear_cols': ['feature1', 'feature2'],
        'linear_threshold': 0.8
    }
    relative_processor_7 = RelativePreprocessing(shortcut_relative=params_empty)
    result_7 = relative_processor_7(empty_data.copy(), 'label')
    
    print(f"\n数据形状变化: {empty_data.shape} -> {result_7.shape}")
    print("期望：空数据保持不变")
    
    # 测试8: 组合测试 - 所有函数类型测试
    print(f"\n{'-' * 60}")
    print("测试8: 组合测试 - 所有函数类型测试")
    print('-' * 60)
    
    funcs = ['func1', 'func2', 'func3', 'func4', 'func5']
    
    for func in funcs:
        params_func = {
            'feature_cols': ['feature1', 'feature2', 'feature3'],
            'feature_threshold': 0.7,
            'relation_func': func
        }
        relative_processor = RelativePreprocessing(shortcut_relative=params_func)
        result = relative_processor(large_test_data.copy(), 'label')
        print(f"\n函数类型 {func}:")
        print(f"  数据形状变化: {large_test_data.shape} -> {result.shape}")
    
    # 测试9: 组合测试 - 降维参数边界值
    print(f"\n{'-' * 60}")
    print("测试9: 组合测试 - 降维参数边界值")
    print('-' * 60)
    
    test_cases_pca = [
        {'n_components': 1, 'desc': '降维到1维'},
        {'n_components': 5, 'desc': '降维到5维'},
        {'n_components': 10, 'desc': '降维到10维'},
    ]
    
    for tc in test_cases_pca:
        params_pca = {
            'pca_cols': ['feature1', 'feature2', 'feature3', 'feature4', 'feature5', 
                        'feature6', 'feature7', 'feature8', 'feature9', 'feature10'],
            'n_components': tc['n_components'],
            'kernel_selection': 'pca'
        }
        relative_processor = RelativePreprocessing(shortcut_relative=params_pca)
        result = relative_processor(large_test_data.copy(), 'label')
        print(f"\n{tc['desc']}:")
        print(f"  数据形状变化: {large_test_data.shape} -> {result.shape}")
    
    # 测试10: 验证结果
    print(f"\n{'-' * 60}")
    print("测试10: 验证结果")
    print('-' * 60)
    
    test_data_verify = pd.DataFrame({
        'x': np.array(range(100)),
        'y': np.array([2 * i + np.random.normal(0, 0.1) for i in range(100)]),
        'label': ['A'] * 50 + ['B'] * 50
    })
    
    params_verify = {
        'linear_cols': ['x', 'y'],
        'linear_threshold': 0.9
    }
    
    relative_processor_10 = RelativePreprocessing(shortcut_relative=params_verify)
    result_10 = relative_processor_10(test_data_verify.copy(), 'label')
    
    print(f"\n原始列: {list(test_data_verify.columns)}")
    print(f"处理后列: {list(result_10.columns)}")
    print("期望：由于x和y高度相关，应该删除其中一列")
    
    print(f"\n{'=' * 80}")
    print("深度渗透测试完成！")
    print('=' * 80)
