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
import pandas as pd

# 尝试导入可选依赖
try:
    from sklearn.utils import resample
    sklearn_available = True
except ImportError:
    sklearn_available = False
try:
    # 尝试导入 imbalanced-learn
    from imblearn.over_sampling import SMOTE, ADASYN, BorderlineSMOTE, SVMSMOTE, RandomOverSampler
    from imblearn.under_sampling import RandomUnderSampler, TomekLinks, EditedNearestNeighbours, ClusterCentroids
    imblearn_available = True
except ImportError:
    imblearn_available = False

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

    @with_evaluation
    def __call__(self, data, clean_type=None, data_label=None):
        self.data_label = data_label
        # 解析配置格式
        sample_config = self.shortcut_sample
        
        # 只使用新格式配置
        if sample_config and 'sample_ways' in sample_config:
            ColorPrinter.print_info('使用新格式配置')
            # 从新格式中提取配置
            sample_ways = sample_config['sample_ways']
            if sample_ways:
                # 只使用第一个配置项（目前只支持一种采样方法）
                config = sample_ways[0]
                # 提取所有参数，遵循没选择也要占位的原则
                method = config.get('method', ['skip'])
                balance_method = config.get('balance_method', 'oversample')
                random_state = config.get('random_state', 42)
                high_sample_selection = config.get('high_sample_selection', 'RandomOverSampler')
                smote_k_neighbors = config.get('smote_k_neighbors', 5)
                adasyn_n_neighbors = config.get('adasyn_n_neighbors', 5)
                borderline_smote_k_neighbors = config.get('borderline_smote_k_neighbors', 5)
                svm_smote_k_neighbors = config.get('svm_smote_k_neighbors', 5)
                enn_n_neighbors = config.get('enn_n_neighbors', 5)
                
                # 更新shortcut_sample为内部格式
                self.shortcut_sample = {
                    'method': method,
                    'balance_method': balance_method,
                    'random_state': random_state,
                    'high_sample_selection': high_sample_selection,
                    'smote_k_neighbors': smote_k_neighbors,
                    'adasyn_n_neighbors': adasyn_n_neighbors,
                    'borderline_smote_k_neighbors': borderline_smote_k_neighbors,
                    'svm_smote_k_neighbors': svm_smote_k_neighbors,
                    'enn_n_neighbors': enn_n_neighbors
                }
        else:
            # 默认配置
            self.shortcut_sample = {
                'method': ['skip'],
                'balance_method': 'oversample',
                'random_state': 42,
                'high_sample_selection': 'RandomOverSampler',
                'smote_k_neighbors': 5,
                'adasyn_n_neighbors': 5,
                'borderline_smote_k_neighbors': 5,
                'svm_smote_k_neighbors': 5,
                'enn_n_neighbors': 5
            }
        
        # 直接处理完整数据
        self.data = data.copy()
        ColorPrinter.print_progress('开始样本预处理')
        
        # 使用pandas处理数据
        processed_data = self._process_with_pandas(data.copy())
        
        # 返回完整数据
        return processed_data
    
    def _process_with_pandas(self, data):
        """使用 pandas 处理数据"""
        # 检查并处理NaN值
        numeric_cols = data.select_dtypes(include=['number']).columns
        if numeric_cols.size > 0 and data[numeric_cols].isna().any().any():
            ColorPrinter.print_warning("警告: 样本处理前检测到NaN值，将自动填充")
            for col in numeric_cols:
                if data[col].isna().any():
                    data[col] = data[col].fillna(data[col].mean())
                    ColorPrinter.print_progress(f"  已填充列 {col} 的NaN值")
        
        # 处理样本
        method = self.shortcut_sample.get('method', ['skip'])
        if isinstance(method, str):
            method = [method]
        
        for m in method:
            if m == 'multi_class_balance':
                balance_method = self.shortcut_sample.get('balance_method', 'oversample')
                random_state = self.shortcut_sample.get('random_state', 42)
                data = self.multi_class_balance(data, balance_method, random_state)
            elif m == 'MY_high_sample':
                data = self.MY_high_sample(data)
            elif m == 'skip':
                ColorPrinter.print_info('跳过样本处理')
        
        return data

    def multi_class_balance(self, data, balance_method='oversample', random_state=42):
        if not sklearn_available:
            ColorPrinter.print_error('scikit-learn 未安装，无法使用多类别平衡')
            return data

        if not isinstance(random_state, int):
            raise TypeError('random_state must be int')
        if balance_method not in ['oversample', 'undersample']:
            raise ValueError("balance_method must be 'oversample' or 'undersample'")

        # 检查标签列是否存在
        if self.data_label not in data.columns:
            ColorPrinter.print_warning(f"标签列 '{self.data_label}' 不存在，跳过样本处理")
            return data

        # 直接使用 pandas DataFrame
        class_counts = data[self.data_label].value_counts()
        
        # 检查是否有有效的类别数据
        if class_counts.empty:
            ColorPrinter.print_warning("没有有效的类别数据，跳过样本处理")
            return data
        
        ColorPrinter.print_info(f"各类别样本数量:\n{class_counts}")

        if balance_method == 'oversample':
            target_count = class_counts.max()
        elif balance_method == 'undersample':
            target_count = class_counts.min()

        ColorPrinter.print_info(f"使用 {balance_method} 方法，目标样本数量: {target_count}")

        balanced_dfs = []
        for class_value in class_counts.index:
            class_data = data[data[self.data_label] == class_value]
            if balance_method == 'oversample' and len(class_data) < target_count:
                resampled_data = resample(
                    class_data,
                    replace=True,
                    n_samples=target_count,
                    random_state=random_state
                )
                balanced_dfs.append(resampled_data)
            elif balance_method == 'undersample' and len(class_data) > target_count:
                resampled_data = resample(
                    class_data,
                    replace=False,
                    n_samples=target_count,
                    random_state=random_state
                )
                balanced_dfs.append(resampled_data)
            else:
                balanced_dfs.append(class_data)

        # 检查是否有数据可以拼接
        if not balanced_dfs:
            ColorPrinter.print_warning("没有生成平衡数据，跳过样本处理")
            return data

        balanced_data = pd.concat(balanced_dfs, ignore_index=True)
        ColorPrinter.print_success(f"平衡后数据集大小: {len(balanced_data)}")
        return balanced_data

    def MY_high_sample(self, data):
        if not imblearn_available:
            ColorPrinter.print_error('imbalanced-learn 未安装，无法使用高级采样方法')
            return data

        high_sample_selection = self.shortcut_sample.get('high_sample_selection', 'RandomOverSampler')

        # 检查目标变量是否为连续值（仅对SMOTE系列方法）
        target_series = data[self.data_label]
        # 定义只适用于分类问题的采样方法
        classification_only_methods = ['SMOTE', 'ADASYN', 'BorderlineSMOTE', 'SVMSMOTE']
        
        if high_sample_selection in classification_only_methods:
            if pd.api.types.is_numeric_dtype(target_series) and target_series.nunique() > 10:
                ColorPrinter.print_warning("警告: 目标变量是连续值，SMOTE系列方法只适用于分类问题，跳过样本处理")
                return data

        # 只选择数值特征进行采样
        numeric_cols = data.select_dtypes(include=['number']).columns.tolist()
        if self.data_label in numeric_cols:
            numeric_cols.remove(self.data_label)
        
        # 分割特征和标签
        feature_data = data[numeric_cols]
        target_data = data[self.data_label]

        try:
            # 采样方法映射
            sampling_config = {
                'SMOTE': {
                    'sampler': SMOTE,
                    'params': {'k_neighbors': self.shortcut_sample.get('smote_k_neighbors', 5)},
                    'message': f'使用SMOTE方法进行过采样 (k_neighbors={self.shortcut_sample.get("smote_k_neighbors", 5)})'
                },
                'ADASYN': {
                    'sampler': ADASYN,
                    'params': {'n_neighbors': self.shortcut_sample.get('adasyn_n_neighbors', 5)},
                    'message': f'使用ADASYN方法进行过采样 (n_neighbors={self.shortcut_sample.get("adasyn_n_neighbors", 5)})'
                },
                'BorderlineSMOTE': {
                    'sampler': BorderlineSMOTE,
                    'params': {'k_neighbors': self.shortcut_sample.get('borderline_smote_k_neighbors', 5)},
                    'message': f'使用BorderlineSMOTE方法进行过采样 (k_neighbors={self.shortcut_sample.get("borderline_smote_k_neighbors", 5)})'
                },
                'SVMSMOTE': {
                    'sampler': SVMSMOTE,
                    'params': {'k_neighbors': self.shortcut_sample.get('svm_smote_k_neighbors', 5)},
                    'message': f'使用SVMSMOTE方法进行过采样 (k_neighbors={self.shortcut_sample.get("svm_smote_k_neighbors", 5)})'
                },
                'RandomOverSampler': {
                    'sampler': RandomOverSampler,
                    'params': {},
                    'message': '使用RandomOverSampler方法进行过采样'
                },
                'RandomUnderSampler': {
                    'sampler': RandomUnderSampler,
                    'params': {},
                    'message': '使用RandomUnderSampler方法进行欠采样'
                },
                'TomekLinks': {
                    'sampler': TomekLinks,
                    'params': {},
                    'message': '使用TomekLinks方法进行采样'
                },
                'EditedNearestNeighbours': {
                    'sampler': EditedNearestNeighbours,
                    'params': {'n_neighbors': self.shortcut_sample.get('enn_n_neighbors', 5)},
                    'message': f'使用EditedNearestNeighbours方法进行采样 (n_neighbors={self.shortcut_sample.get("enn_n_neighbors", 5)})'
                },
                'ClusterCentroids': {
                    'sampler': ClusterCentroids,
                    'params': {},
                    'message': '使用ClusterCentroids方法进行欠采样'
                }
            }
            
            if high_sample_selection in sampling_config:
                config = sampling_config[high_sample_selection]
                
                # 计算每个类别的样本数量
                class_counts = target_data.value_counts()
                min_class_count = class_counts.min()
                
                # 确保邻居数量至少为1且不超过最小类别样本数量
                params = config['params'].copy()
                if 'k_neighbors' in params:
                    params['k_neighbors'] = max(1, min(params['k_neighbors'], min_class_count - 1))
                elif 'n_neighbors' in params:
                    params['n_neighbors'] = max(1, min(params['n_neighbors'], min_class_count - 1))
                
                # 检查是否有足够的样本进行采样
                if min_class_count < 2:
                    ColorPrinter.print_warning(f"警告: 类别 '{class_counts.idxmin()}' 只有 {min_class_count} 个样本，无法使用 {high_sample_selection} 方法进行采样，跳过采样处理")
                    return data
                
                # 创建采样器实例
                sampler = config['sampler'](**params)
                feature_data, target_data = sampler.fit_resample(feature_data, target_data)
                ColorPrinter.print_success(config['message'])
            else:
                ColorPrinter.print_error('请输入正确的采样方式')
                return data
        except ValueError as e:
            if "Unknown label type: continuous" in str(e):
                ColorPrinter.print_warning("警告: 目标变量是连续值，SMOTE只适用于分类问题，跳过样本处理")
                return data
            else:
                raise

        # 重建数据框
        balanced_data = pd.DataFrame(feature_data, columns=feature_data.columns)
        balanced_data[self.data_label] = target_data
        
        # 合并非数值特征（如果有）
        non_numeric_cols = [col for col in data.columns if col not in numeric_cols and col != self.data_label]
        if non_numeric_cols:
            # 对于非数值特征，我们取原始数据的前len(balanced_data)行
            balanced_data = pd.concat([balanced_data, data[non_numeric_cols].iloc[:len(balanced_data)]], axis=1)

        ColorPrinter.print_info(f"高级采样后数据集大小: {len(balanced_data)}")
        return balanced_data




"""
Parameter documentation:

New format (recommended):
shortcut_sample parameter dictionary:
    - sample_ways: List of sample processing configurations
      Type: list of dictionaries
      Default: []
      Description: List of dictionaries, each containing sample processing configuration
      Each dictionary in sample_ways contains:
        - method: Sampling method
          Type: string or list of strings
          Options: ['multi_class_balance', 'MY_high_sample', 'skip']
          Description: Sampling method
        - balance_method: Balancing method
          Type: string
          Options: ['oversample', 'undersample']
          Default: 'oversample'
          Description: Balancing method for multi_class_balance
        - random_state: Random seed
          Type: integer
          Default: 42
          Description: Used to ensure reproducibility of results
        - high_sample_selection: Advanced sampling method
          Type: string
          Options: ['SMOTE', 'ADASYN', 'BorderlineSMOTE', 'SVMSMOTE', 'RandomOverSampler', 'RandomUnderSampler', 'TomekLinks', 'EditedNearestNeighbours', 'ClusterCentroids']
          Default: 'RandomOverSampler'
          Description: Advanced sampling method for MY_high_sample
        - smote_k_neighbors: Number of neighbors for SMOTE method
          Type: integer
          Default: 5
          Description: Number of neighbors used for synthesizing samples in SMOTE algorithm
        - adasyn_n_neighbors: Number of neighbors for ADASYN method
          Type: integer
          Default: 5
          Description: Number of neighbors used for synthesizing samples in ADASYN algorithm
        - borderline_smote_k_neighbors: Number of neighbors for BorderlineSMOTE method
          Type: integer
          Default: 5
          Description: Number of neighbors used for synthesizing samples in BorderlineSMOTE algorithm
        - svm_smote_k_neighbors: Number of neighbors for SVMSMOTE method
          Type: integer
          Default: 5
          Description: Number of neighbors used for synthesizing samples in SVMSMOTE algorithm
        - enn_n_neighbors: Number of neighbors for EditedNearestNeighbours method
          Type: integer
          Default: 5
          Description: Number of neighbors used for judging samples in EditedNearestNeighbours algorithm

Old format (backward compatible):
shortcut_sample parameter dictionary:
    - choice1_normal: Normal mode sample processing method selection
      Type: string or list of strings
      Optional values: 'multi_class_balance', 'skip'
      Default: None (must be specified)
      Description: Multiple selection supported, can choose the following methods:
        - 'multi_class_balance': Multi-class balancing (oversampling/undersampling)
        - 'skip': Skip sample processing
    
    - choice1_advance: Advanced mode sample processing method selection
      Type: string or list of strings
      Optional values: 'MY_high_sample', 'skip'
      Default: None (must be specified)
      Description: Multiple selection supported, can choose the following methods:
        - 'MY_high_sample': Advanced sampling methods (various oversampling and undersampling methods)
        - 'skip': Skip sample processing
    
    - balance_method: Balancing method
      Type: string
      Optional values: 'oversample', 'undersample'
      Default: None (must be specified in multi_class_balance mode)
      Description: 
        - 'oversample': Oversampling, targeting the number of the most frequent class
        - 'undersample': Undersampling, targeting the number of the least frequent class
    
    - random_state: Random seed
      Type: integer
      Optional values: >= 0
      Default: None (must be specified in multi_class_balance mode)
      Description: Used to ensure reproducibility of results
    
    - high_sample_selection: Advanced sampling method selection
      Type: string
      Optional values: 'SMOTE', 'ADASYN', 'BorderlineSMOTE', 'SVMSMOTE', 'RandomOverSampler', 'RandomUnderSampler', 'TomekLinks', 'EditedNearestNeighbours', 'ClusterCentroids'
      Default: None (must be specified in MY_high_sample mode)
      Description: 
        - 'SMOTE': Synthetic Minority Over-sampling Technique
        - 'ADASYN': Adaptive Synthetic Sampling
        - 'BorderlineSMOTE': Borderline SMOTE, only synthesizes minority samples near the boundary
        - 'SVMSMOTE': SVM SMOTE, uses SVM boundary information for sampling
        - 'RandomOverSampler': Random oversampling
        - 'RandomUnderSampler': Random undersampling
        - 'TomekLinks': Removes Tomek links, improves class boundary clarity
        - 'EditedNearestNeighbours': Edited nearest neighbors, removes minority samples surrounded by majority class
        - 'ClusterCentroids': Cluster centroids, generates center samples using K-means clustering
    
    - smote_k_neighbors: Number of neighbors for SMOTE method
      Type: integer
      Optional values: >= 1
      Default: 5
      Description: Number of neighbors used for synthesizing samples in SMOTE algorithm
    
    - adasyn_n_neighbors: Number of neighbors for ADASYN method
      Type: integer
      Optional values: >= 1
      Default: 5
      Description: Number of neighbors used for synthesizing samples in ADASYN algorithm
    
    - borderline_smote_k_neighbors: Number of neighbors for BorderlineSMOTE method
      Type: integer
      Optional values: >= 1
      Default: 5
      Description: Number of neighbors used for synthesizing samples in BorderlineSMOTE algorithm
    
    - svm_smote_k_neighbors: Number of neighbors for SVMSMOTE method
      Type: integer
      Optional values: >= 1
      Default: 5
      Description: Number of neighbors used for synthesizing samples in SVMSMOTE algorithm
    
    - enn_n_neighbors: Number of neighbors for EditedNearestNeighbours method
      Type: integer
      Optional values: >= 1
      Default: 5
      Description: Number of neighbors used for judging samples in EditedNearestNeighbours algorithm

Usage example:
    # New format - Normal mode - Oversampling
    sample_params = {
        'sample_ways': [
            {
                'mode': 'normal',
                'method': ['multi_class_balance'],
                'balance_method': 'oversample',
                'random_state': 42
            }
        ]
    }
    sample_processor = SamplePreprocessing(shortcut_sample=sample_params)
    balanced_data = sample_processor(data, data_label='label')
    
    # New format - Advanced mode - SMOTE sampling
    sample_params_smote = {
        'sample_ways': [
            {
                'mode': 'advance',
                'method': ['MY_high_sample'],
                'high_sample_selection': 'SMOTE',
                'smote_k_neighbors': 5
            }
        ]
    }
    sample_processor_smote = SamplePreprocessing(shortcut_sample=sample_params_smote)
    balanced_data_smote = sample_processor_smote(data, data_label='label')
    
    # Old format - Normal mode - Oversampling
    sample_params_old = {
        'choice1_normal': ['multi_class_balance'],
        'balance_method': 'oversample',
        'random_state': 42
    }
    sample_processor_old = SamplePreprocessing(shortcut_sample=sample_params_old)
    balanced_data_old = sample_processor_old(data, 'normal', '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, 100),
        'feature2': np.random.normal(100, 20, 100),
        'feature3': np.random.normal(200, 30, 100),
        'label': ['A'] * 70 + ['B'] * 20 + ['C'] * 10
    })
    
    print("\n原始数据:")
    print(test_data.head(10))
    print(f"\n数据形状: {test_data.shape}")
    print("\n类别分布:")
    print(test_data['label'].value_counts())
    
    # 测试不同的样本处理方式
    test_cases = [
        {
            'clean_type': 'normal',
            'params': {
                'choice1_normal': ['multi_class_balance'],
                'balance_method': 'oversample',
                'random_state': 42
            },
            'description': '过采样（普通模式）'
        },
        {
            'clean_type': 'normal',
            'params': {
                'choice1_normal': ['multi_class_balance'],
                'balance_method': 'undersample',
                'random_state': 42
            },
            'description': '欠采样（普通模式）'
        },
        {
            'clean_type': 'advance',
            'params': {
                'choice1_advance': ['MY_high_sample'],
                'high_sample_selection': 'SMOTE',
                'smote_k_neighbors': 5
            },
            'description': 'SMOTE高级采样（高级模式）'
        },
        {
            'clean_type': 'advance',
            'params': {
                'choice1_advance': ['MY_high_sample'],
                'high_sample_selection': 'ADASYN',
                'adasyn_n_neighbors': 5
            },
            'description': 'ADASYN高级采样（高级模式）'
        },
        {
            'clean_type': 'advance',
            'params': {
                'choice1_advance': ['MY_high_sample'],
                'high_sample_selection': 'BorderlineSMOTE',
                'borderline_smote_k_neighbors': 5
            },
            'description': 'BorderlineSMOTE高级采样（高级模式）'
        },
        {
            'clean_type': 'advance',
            'params': {
                'choice1_advance': ['MY_high_sample'],
                'high_sample_selection': 'SVMSMOTE',
                'svm_smote_k_neighbors': 5
            },
            'description': 'SVMSMOTE高级采样（高级模式）'
        },
        {
            'clean_type': 'advance',
            'params': {
                'choice1_advance': ['MY_high_sample'],
                'high_sample_selection': 'RandomOverSampler'
            },
            'description': 'RandomOverSampler高级采样（高级模式）'
        },
        {
            'clean_type': 'advance',
            'params': {
                'choice1_advance': ['MY_high_sample'],
                'high_sample_selection': 'RandomUnderSampler'
            },
            'description': 'RandomUnderSampler高级采样（高级模式）'
        },
        {
            'clean_type': 'advance',
            'params': {
                'choice1_advance': ['MY_high_sample'],
                'high_sample_selection': 'TomekLinks'
            },
            'description': 'TomekLinks高级采样（高级模式）'
        },
        {
            'clean_type': 'advance',
            'params': {
                'choice1_advance': ['MY_high_sample'],
                'high_sample_selection': 'EditedNearestNeighbours',
                'enn_n_neighbors': 5
            },
            'description': 'EditedNearestNeighbours高级采样（高级模式）'
        },
        {
            'clean_type': 'advance',
            'params': {
                'choice1_advance': ['MY_high_sample'],
                'high_sample_selection': 'ClusterCentroids'
            },
            'description': 'ClusterCentroids高级采样（高级模式）'
        },
        {
            'clean_type': 'normal',
            'params': {
                'choice1_normal': ['skip']
            },
            'description': '跳过样本处理'
        }
    ]
    
    for i, test_case in enumerate(test_cases, 1):
        print(f"\n{'=' * 60}")
        print(f"测试 {i}: {test_case['description']}")
        print('=' * 60)
        
        sample_processor = SamplePreprocessing(shortcut_sample=test_case['params'])
        try:
            balanced_data = sample_processor(test_data.copy(), test_case['clean_type'], 'label')
            
            print("\n处理后的数据形状:")
            print(f"原始数据: {test_data.shape}")
            print(f"处理后数据: {balanced_data.shape}")
            
            print("\n处理后的类别分布:")
            print(balanced_data['label'].value_counts())
        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),
        'label': ['A'] * 600 + ['B'] * 300 + ['C'] * 100
    })
    
    # 测试1: 边界条件测试 - 空参数
    print(f"\n{'-' * 60}")
    print("测试1: 边界条件测试 - 空参数")
    print('-' * 60)
    
    sample_processor_1 = SamplePreprocessing(shortcut_sample={})
    result_1 = sample_processor_1(large_test_data.copy(), 'normal', 'label')
    
    print(f"\n数据形状变化: {large_test_data.shape} -> {result_1.shape}")
    print("期望：数据保持不变")
    
    # 测试2: 边界条件测试 - 完全平衡的数据
    print(f"\n{'-' * 60}")
    print("测试2: 边界条件测试 - 完全平衡的数据")
    print('-' * 60)
    
    balanced_test_data = pd.DataFrame({
        'feature1': np.random.normal(50, 10, 300),
        'feature2': np.random.normal(100, 20, 300),
        'label': ['A'] * 100 + ['B'] * 100 + ['C'] * 100
    })
    
    params_balanced = {
        'choice1_normal': ['multi_class_balance'],
        'balance_method': 'oversample',
        'random_state': 42
    }
    sample_processor_2 = SamplePreprocessing(shortcut_sample=params_balanced)
    result_2 = sample_processor_2(balanced_test_data.copy(), 'normal', 'label')
    
    print(f"\n数据形状变化: {balanced_test_data.shape} -> {result_2.shape}")
    print("期望：数据保持不变，因为已经完全平衡")
    
    # 测试3: 边界条件测试 - 只有一个类别
    print(f"\n{'-' * 60}")
    print("测试3: 边界条件测试 - 只有一个类别")
    print('-' * 60)
    
    single_class_data = pd.DataFrame({
        'feature1': np.random.normal(50, 10, 100),
        'feature2': np.random.normal(100, 20, 100),
        'label': ['A'] * 100
    })
    
    params_single = {
        'choice1_normal': ['multi_class_balance'],
        'balance_method': 'oversample',
        'random_state': 42
    }
    sample_processor_3 = SamplePreprocessing(shortcut_sample=params_single)
    result_3 = sample_processor_3(single_class_data.copy(), 'normal', 'label')
    
    print(f"\n数据形状变化: {single_class_data.shape} -> {result_3.shape}")
    print("期望：数据保持不变，因为只有一个类别")
    
    # 测试4: 边界条件测试 - 空数据
    print(f"\n{'-' * 60}")
    print("测试4: 边界条件测试 - 空数据")
    print('-' * 60)
    
    empty_data = pd.DataFrame({
        'feature1': [],
        'feature2': [],
        'label': []
    })
    
    params_empty = {
        'choice1_normal': ['multi_class_balance'],
        'balance_method': 'oversample',
        'random_state': 42
    }
    sample_processor_4 = SamplePreprocessing(shortcut_sample=params_empty)
    result_4 = sample_processor_4(empty_data.copy(), 'normal', 'label')
    
    print(f"\n数据形状变化: {empty_data.shape} -> {result_4.shape}")
    print("期望：空数据保持不变")
    
    # 测试5: 边界条件测试 - 没有数值列
    print(f"\n{'-' * 60}")
    print("测试5: 边界条件测试 - 没有数值列")
    print('-' * 60)
    
    no_numeric_data = pd.DataFrame({
        'str_col1': ['a', 'b', 'c', 'd', 'e'] * 20,
        'str_col2': ['x', 'y', 'z', 'w', 'v'] * 20,
        'label': ['A'] * 60 + ['B'] * 40
    })
    
    params_no_numeric = {
        'choice1_advance': ['MY_high_sample'],
        'high_sample_selection': 'SMOTE',
        'smote_k_neighbors': 5
    }
    sample_processor_5 = SamplePreprocessing(shortcut_sample=params_no_numeric)
    result_5 = sample_processor_5(no_numeric_data.copy(), 'advance', 'label')
    
    print(f"\n数据形状变化: {no_numeric_data.shape} -> {result_5.shape}")
    print("期望：处理应该正常进行，跳过没有数值列的情况")
    
    # 测试6: 边界条件测试 - k邻居数边界值
    print(f"\n{'-' * 60}")
    print("测试6: 边界条件测试 - k邻居数边界值")
    print('-' * 60)
    
    test_cases_k = [
        {'k': 1, 'desc': 'k=1'},
        {'k': 5, 'desc': 'k=5'},
        {'k': 10, 'desc': 'k=10'},
    ]
    
    for tc in test_cases_k:
        params_k = {
            'choice1_advance': ['MY_high_sample'],
            'high_sample_selection': 'SMOTE',
            'smote_k_neighbors': tc['k']
        }
        sample_processor = SamplePreprocessing(shortcut_sample=params_k)
        result = sample_processor(large_test_data.copy(), 'advance', 'label')
        print(f"\n{tc['desc']}:")
        print(f"  数据形状变化: {large_test_data.shape} -> {result.shape}")
    
    # 测试7: 边界条件测试 - 极小样本数
    print(f"\n{'-' * 60}")
    print("测试7: 边界条件测试 - 极小样本数")
    print('-' * 60)
    
    tiny_test_data = pd.DataFrame({
        'feature1': np.random.normal(50, 10, 20),
        'feature2': np.random.normal(100, 20, 20),
        'label': ['A'] * 15 + ['B'] * 5
    })
    
    params_tiny = {
        'choice1_advance': ['MY_high_sample'],
        'high_sample_selection': 'SMOTE',
        'smote_k_neighbors': 3
    }
    sample_processor_7 = SamplePreprocessing(shortcut_sample=params_tiny)
    result_7 = sample_processor_7(tiny_test_data.copy(), 'advance', 'label')
    
    print(f"\n数据形状变化: {tiny_test_data.shape} -> {result_7.shape}")
    print("期望：处理应该正常进行")
    
    # 测试8: 组合测试 - 所有高级采样方法
    print(f"\n{'-' * 60}")
    print("测试8: 组合测试 - 所有高级采样方法")
    print('-' * 60)
    
    advanced_methods = ['SMOTE', 'ADASYN', 'RandomOverSampler', 'RandomUnderSampler']
    
    for method in advanced_methods:
        params_advanced = {
            'choice1_advance': ['MY_high_sample'],
            'high_sample_selection': method,
            'smote_k_neighbors': 5,
            'adasyn_n_neighbors': 5
        }
        sample_processor = SamplePreprocessing(shortcut_sample=params_advanced)
        result = sample_processor(large_test_data.copy(), 'advance', 'label')
        print(f"\n方法 {method}:")
        print(f"  数据形状变化: {large_test_data.shape} -> {result.shape}")
    
    # 测试9: 验证结果
    print(f"\n{'-' * 60}")
    print("测试9: 验证结果")
    print('-' * 60)
    
    test_data_verify = pd.DataFrame({
        'feature1': np.random.normal(50, 10, 100),
        'feature2': np.random.normal(100, 20, 100),
        'label': ['A'] * 80 + ['B'] * 20
    })
    
    params_verify = {
        'choice1_normal': ['multi_class_balance'],
        'balance_method': 'oversample',
        'random_state': 42
    }
    
    sample_processor_9 = SamplePreprocessing(shortcut_sample=params_verify)
    result_9 = sample_processor_9(test_data_verify.copy(), 'normal', 'label')
    
    print(f"\n原始类别分布: {dict(test_data_verify['label'].value_counts())}")
    print(f"处理后类别分布: {dict(result_9['label'].value_counts())}")
    print("期望：A和B的数量应该相等")
    
    print(f"\n{'=' * 80}")
    print("深度渗透测试完成！")
    print('=' * 80)
