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.preprocessing.function.outlierfunction import thres_sigma_auto, boxplot, isolation_forest, multivariate_outlier_detection
import pandas as pd
import numpy as np

# 尝试导入可选依赖
try:
    from sklearn.ensemble import IsolationForest
    from sklearn.covariance import EllipticEnvelope
    sklearn_available = True
except ImportError:
    sklearn_available = False

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

    @with_evaluation
    def __call__(self, data, data_label=None):
        self.data_label = data_label
        
#region 正式异常值处理
        ColorPrinter.print_progress('开始异常值处理')
        # 传递原始数据（包含标签列）给处理函数
        processed_data = self.outlier_preprocessing_switch(data)
        
#region 异常值处理完成
        ColorPrinter.print_progress('异常值处理完成')
        return processed_data

    def outlier_preprocessing_switch(self, data):
        if self.shortcut_outlier is None:
            ColorPrinter.print_warning('没有指定任何处理参数')
            return data
        outlier_choice = self.shortcut_outlier.get('outlier_choice', 'skip')
#region 使用字典映射替代if-elif链
        outlier_methods = {
            'thres_sigma_auto': self.thres_sigma_auto,
            'boxplot': self.boxplot,
            'isolation_forest': self.isolation_forest,
            'multivariate': self.multivariate_outlier_detection,
            'skip': lambda data: (ColorPrinter.print_warning('跳过异常值处理'), data)[1]
        }
#endregion 
#region 正式的异常值处理
        if outlier_choice in outlier_methods:
            data = outlier_methods[outlier_choice](data)
        else:
            ColorPrinter.print_error('请输入正确的异常值处理方式')
#endregion 
        return data

    def _get_parameters(self):
        """Get outlier processing parameters"""
        row_drop_thres = self.shortcut_outlier.get('row_drop_thres', 3.0)
        outlier_action = self.shortcut_outlier.get('outlier_action', 'drop')
        specify_auto_drop_row_extr = self.shortcut_outlier.get('specify_auto_drop_row_extr', None)
        return row_drop_thres, outlier_action, specify_auto_drop_row_extr
    

    
    def thres_sigma_auto(self, data):
        """基于标准差的异常值检测"""
        row_drop_thres, outlier_action, specify_auto_drop_row_extr = self._get_parameters()
        return thres_sigma_auto(data, self.data_label, row_drop_thres, outlier_action, specify_auto_drop_row_extr)
    


    def boxplot(self, data):
        """基于箱线图的异常值检测"""
        # 处理outlier_action参数，默认为'drop'，可选'replace'
        outlier_action = self.shortcut_outlier.get('outlier_action', 'drop')
        return boxplot(data, self.data_label, outlier_action)
   
    def isolation_forest(self, data):
        """基于孤立森林的异常值检测"""
        # 处理outlier_action参数，默认为'drop'，可选'replace'
        outlier_action = self.shortcut_outlier.get('outlier_action', 'drop')
        # 处理contamination参数，默认为0.1
        contamination = self.shortcut_outlier.get('contamination', 0.1)
        return isolation_forest(data, self.data_label, outlier_action, contamination)
    
    def multivariate_outlier_detection(self, data):
        """基于多变量的异常值检测"""
        # 处理outlier_action参数，默认为'drop'，可选'replace'
        outlier_action = self.shortcut_outlier.get('outlier_action', 'drop')
        # 处理contamination参数，默认为0.1
        contamination = self.shortcut_outlier.get('contamination', 0.1)
        return multivariate_outlier_detection(data, self.data_label, outlier_action, contamination)


"""
Parameter documentation:

shortcut_outlier parameter dictionary:
    - outlier_choice: Outlier processing method choice
      Type: string
      Optional values: 'thres_sigma_auto', 'boxplot', 'isolation_forest', 'multivariate', 'skip'
      Default: None (must be specified)
      Description: 
        - 'thres_sigma_auto': Standard deviation-based outlier detection
        - 'boxplot': Box plot-based outlier detection
        - 'isolation_forest': Isolation Forest-based outlier detection
        - 'multivariate': Elliptic Envelope-based multivariate outlier detection
        - 'skip': Skip outlier processing
    
    - row_drop_thres: Standard deviation multiple threshold
      Type: float
      Optional values: >= 0
      Default: 3.0
      Description: Threshold multiple for determining outliers (only used in thres_sigma_auto mode)
    
    - outlier_action: Outlier processing action
      Type: string
      Optional values: 'drop', 'replace'
      Default: 'drop'
      Description: 
        - 'drop': Drop rows containing outliers
        - 'replace': Replace outliers with boundary values or median
    
    - contamination: Outlier ratio
      Type: float
      Optional values: 0.0 - 0.5
      Default: 0.1
      Description: Proportion of outliers in total samples (only used in isolation_forest and multivariate modes)
    
    - specify_auto_drop_row_extr: Specify column for outlier detection
      Type: string
      Optional values: Any valid column name or 'auto'
      Default: None
      Description: Specify column for outlier detection (only used in thres_sigma_auto mode)
             'auto' means perform outlier detection on all numeric columns

Usage example:
    # Standard deviation-based outlier detection (drop outliers)
    outlier_params = {
        'outlier_choice': 'thres_sigma_auto',
        'row_drop_thres': 3.0,
        'specify_auto_drop_row_extr': 'feature_col'
    }
    outlier_processor = OutlierPreprocessing(shortcut_outlier=outlier_params)
    cleaned_data = outlier_processor(data, 'label')
    
    # Box plot-based outlier detection (replace outliers)
    outlier_params_replace = {
        'outlier_choice': 'boxplot',
        'outlier_action': 'replace'
    }
    outlier_processor_replace = OutlierPreprocessing(shortcut_outlier=outlier_params_replace)
    cleaned_data_replace = outlier_processor_replace(data, 'label')
    
    # Isolation Forest-based outlier detection
    outlier_params_iso = {
        'outlier_choice': 'isolation_forest',
        'contamination': 0.1,
        'outlier_action': 'drop'
    }
    outlier_processor_iso = OutlierPreprocessing(shortcut_outlier=outlier_params_iso)
    cleaned_data_iso = outlier_processor_iso(data, 'label')
    
    # Multivariate outlier detection
    outlier_params_multi = {
        'outlier_choice': 'multivariate',
        'contamination': 0.1,
        'outlier_action': 'replace'
    }
    outlier_processor_multi = OutlierPreprocessing(shortcut_outlier=outlier_params_multi)
    cleaned_data_multi = outlier_processor_multi(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, 100),
        'feature2': np.random.normal(100, 20, 100),
        'feature3': np.random.normal(200, 30, 100),
        'label': ['A'] * 50 + ['B'] * 50
    })
    
    # 添加一些异常值
    test_data.loc[0, 'feature1'] = 500
    test_data.loc[1, 'feature2'] = 800
    test_data.loc[2, 'feature3'] = 1200
    test_data.loc[3, 'feature1'] = -300
    test_data.loc[4, 'feature2'] = -500
    
    print("\n原始数据:")
    print(test_data.head(10))
    print(f"\n数据形状: {test_data.shape}")
    print("\n数据统计:")
    print(test_data.describe())
    
    # 测试不同的异常值处理方式
    test_cases = [
        {
            'params': {
                'outlier_choice': 'thres_sigma_auto',
                'row_drop_thres': 3.0,
                'specify_auto_drop_row_extr': 'feature2'
            },
            'description': '基于标准差的异常值检测（自动删除行）'
        },
        {
            'params': {
                'outlier_choice': 'thres_sigma_auto',
                'row_drop_thres': 3.0,
                'specify_auto_drop_row_extr': 'feature2',
                'outlier_action': 'replace'
            },
            'description': '基于标准差的异常值检测（替换异常值）'
        },
        {
            'params': {
                'outlier_choice': 'boxplot'
            },
            'description': '基于箱线图的异常值检测（删除异常值）'
        },
        {
            'params': {
                'outlier_choice': 'boxplot',
                'outlier_action': 'replace'
            },
            'description': '基于箱线图的异常值检测（替换异常值）'
        },
        {
            'params': {
                'outlier_choice': 'isolation_forest',
                'contamination': 0.1,
                'outlier_action': 'drop'
            },
            'description': '基于孤立森林的异常值检测（删除异常值）'
        },
        {
            'params': {
                'outlier_choice': 'isolation_forest',
                'contamination': 0.1,
                'outlier_action': 'replace'
            },
            'description': '基于孤立森林的异常值检测（替换异常值）'
        },
        {
            'params': {
                'outlier_choice': 'multivariate',
                'contamination': 0.1,
                'outlier_action': 'drop'
            },
            'description': '基于多变量的异常值检测（删除异常值）'
        },
        {
            'params': {
                'outlier_choice': 'multivariate',
                'contamination': 0.1,
                'outlier_action': 'replace'
            },
            'description': '基于多变量的异常值检测（替换异常值）'
        },
        {
            'params': {
                'outlier_choice': 'skip'
            },
            'description': '跳过异常值处理'
        }
    ]
    
    for i, test_case in enumerate(test_cases, 1):
        print(f"\n{'=' * 60}")
        print(f"测试 {i}: {test_case['description']}")
        print('=' * 60)
        print(f"参数: {test_case['params']}")
        
        outlier_processor = OutlierPreprocessing(shortcut_outlier=test_case['params'])
        try:
            cleaned_data = outlier_processor(test_data.copy(), 'label')
            
            print("\n处理后的数据:")
            print(cleaned_data.head(10))
            print(f"\n处理后的数据形状: {cleaned_data.shape}")
            
            if cleaned_data.shape[0] != test_data.shape[0]:
                print(f"删除的行数: {test_data.shape[0] - cleaned_data.shape[0]}")
            if cleaned_data.shape[1] != test_data.shape[1]:
                print(f"删除的列数: {test_data.shape[1] - cleaned_data.shape[1]}")
        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': np.random.choice(['A', 'B', 'C'], 1000)
    })
    
    # 添加大量异常值
    for i in range(10):
        large_test_data.loc[i*100, 'feature1'] = 500 + i * 100
        large_test_data.loc[i*100+1, 'feature2'] = 1000 + i * 100
        large_test_data.loc[i*100+2, 'feature3'] = 2000 + i * 100
    
    # 测试1: 边界条件测试 - 空参数
    print(f"\n{'-' * 60}")
    print("测试1: 边界条件测试 - 空参数")
    print('-' * 60)
    
    outlier_processor_1 = OutlierPreprocessing(shortcut_outlier={})
    result_1 = outlier_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_col = {
        'outlier_choice': 'thres_sigma_auto',
        'row_drop_thres': 3.0,
        'specify_auto_drop_row_extr': 'nonexistent_col'
    }
    outlier_processor_2 = OutlierPreprocessing(shortcut_outlier=params_nonexistent_col)
    result_2 = outlier_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)
    
    test_cases_contamination = [
        {'contamination': 0.0, 'desc': '异常值比例为0'},
        {'contamination': 0.5, 'desc': '异常值比例为0.5'},
    ]
    
    for tc in test_cases_contamination:
        params_contamination = {
            'outlier_choice': 'isolation_forest',
            'contamination': tc['contamination'],
            'outlier_action': 'drop'
        }
        outlier_processor = OutlierPreprocessing(shortcut_outlier=params_contamination)
        result = outlier_processor(large_test_data.copy(), 'label')
        print(f"\n{tc['desc']}:")
        print(f"  数据形状变化: {large_test_data.shape} -> {result.shape}")
        print(f"  删除的行数: {large_test_data.shape[0] - result.shape[0]}")
    
    # 测试4: 边界条件测试 - 标准差倍数边界值
    print(f"\n{'-' * 60}")
    print("测试4: 边界条件测试 - 标准差倍数边界值")
    print('-' * 60)
    
    test_cases_sigma = [
        {'row_drop_thres': 0.0, 'desc': '标准差倍数为0'},
        {'row_drop_thres': 1.0, 'desc': '标准差倍数为1'},
        {'row_drop_thres': 10.0, 'desc': '标准差倍数为10'},
    ]
    
    for tc in test_cases_sigma:
        params_sigma = {
            'outlier_choice': 'thres_sigma_auto',
            'row_drop_thres': tc['row_drop_thres'],
            'specify_auto_drop_row_extr': 'feature1',
            'outlier_action': 'drop'
        }
        outlier_processor = OutlierPreprocessing(shortcut_outlier=params_sigma)
        result = outlier_processor(large_test_data.copy(), 'label')
        print(f"\n{tc['desc']}:")
        print(f"  数据形状变化: {large_test_data.shape} -> {result.shape}")
        print(f"  删除的行数: {large_test_data.shape[0] - result.shape[0]}")
    
    # 测试5: 边界条件测试 - 只有一个数值列
    print(f"\n{'-' * 60}")
    print("测试5: 边界条件测试 - 只有一个数值列")
    print('-' * 60)
    
    data_single_col = pd.DataFrame({
        'feature1': np.random.normal(50, 10, 100),
        'str_col': ['a'] * 100,
        'label': ['A'] * 50 + ['B'] * 50
    })
    
    data_single_col.loc[0, 'feature1'] = 500
    
    params_single_col = {
        'outlier_choice': 'thres_sigma_auto',
        'row_drop_thres': 3.0,
        'specify_auto_drop_row_extr': 'feature1'
    }
    outlier_processor_5 = OutlierPreprocessing(shortcut_outlier=params_single_col)
    result_5 = outlier_processor_5(data_single_col.copy(), 'label')
    
    print(f"\n数据形状变化: {data_single_col.shape} -> {result_5.shape}")
    print("期望：成功处理单个数值列")
    
    # 测试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 = {
        'outlier_choice': 'thres_sigma_auto',
        'row_drop_thres': 3.0,
        'specify_auto_drop_row_extr': 'auto'
    }
    outlier_processor_6 = OutlierPreprocessing(shortcut_outlier=params_no_numeric)
    result_6 = outlier_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)
    
    data_insufficient_cols = pd.DataFrame({
        'feature1': np.random.normal(50, 10, 100),
        'label': ['A'] * 50 + ['B'] * 50
    })
    
    data_insufficient_cols.loc[0, 'feature1'] = 500
    
    params_insufficient = {
        'outlier_choice': 'multivariate',
        'contamination': 0.1,
        'outlier_action': 'drop'
    }
    outlier_processor_7 = OutlierPreprocessing(shortcut_outlier=params_insufficient)
    result_7 = outlier_processor_7(data_insufficient_cols.copy(), 'label')
    
    print(f"\n数据形状变化: {data_insufficient_cols.shape} -> {result_7.shape}")
    print("期望：数据保持不变，因为需要至少2个数值列")
    
    # 测试8: 边界条件测试 - 空数据
    print(f"\n{'-' * 60}")
    print("测试8: 边界条件测试 - 空数据")
    print('-' * 60)
    
    empty_data = pd.DataFrame({
        'feature1': [],
        'feature2': [],
        'label': []
    })
    
    params_empty = {
        'outlier_choice': 'thres_sigma_auto',
        'row_drop_thres': 3.0,
        'specify_auto_drop_row_extr': 'auto'
    }
    outlier_processor_8 = OutlierPreprocessing(shortcut_outlier=params_empty)
    result_8 = outlier_processor_8(empty_data.copy(), 'label')
    
    print(f"\n数据形状变化: {empty_data.shape} -> {result_8.shape}")
    print("期望：空数据保持不变")
    
    # 测试9: 组合测试 - 所有方法在大数量上运行
    print(f"\n{'-' * 60}")
    print("测试9: 组合测试 - 所有方法在大数量上运行")
    print('-' * 60)
    
    huge_test_data = pd.DataFrame({
        'col1': np.random.normal(0, 1, 10000),
        'col2': np.random.normal(10, 5, 10000),
        'col3': np.random.normal(100, 20, 10000),
        'col4': np.random.normal(200, 30, 10000),
        'label': np.random.choice(['A', 'B'], 10000)
    })
    
    for i in range(100):
        huge_test_data.loc[i*100, 'col1'] = 10 + i
    
    methods = ['thres_sigma_auto', 'boxplot', 'isolation_forest', 'multivariate']
    
    for method in methods:
        params_huge = {
            'outlier_choice': method,
            'row_drop_thres': 3.0,
            'specify_auto_drop_row_extr': 'auto',
            'contamination': 0.01,
            'outlier_action': 'drop'
        }
        outlier_processor = OutlierPreprocessing(shortcut_outlier=params_huge)
        result = outlier_processor(huge_test_data.copy(), 'label')
        print(f"\n方法 {method}:")
        print(f"  处理前: {huge_test_data.shape}")
        print(f"  处理后: {result.shape}")
        print(f"  删除行数: {huge_test_data.shape[0] - result.shape[0]}")
    
    # 测试10: 验证异常值检测结果
    print(f"\n{'-' * 60}")
    print("测试10: 验证异常值检测结果")
    print('-' * 60)
    
    test_data_verify = pd.DataFrame({
        'col': np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100]),
        'label': ['A'] * 5 + ['B'] * 6
    })
    
    params_verify = {
        'outlier_choice': 'thres_sigma_auto',
        'row_drop_thres': 2.0,
        'specify_auto_drop_row_extr': 'col',
        'outlier_action': 'drop'
    }
    
    outlier_processor_10 = OutlierPreprocessing(shortcut_outlier=params_verify)
    result_10 = outlier_processor_10(test_data_verify.copy(), 'label')
    
    print(f"\n原始数据: {test_data_verify['col'].tolist()}")
    print(f"处理后数据: {result_10['col'].tolist()}")
    print("期望：异常值100应该被删除")
    
    print(f"\n{'=' * 80}")
    print("深度渗透测试完成！")
    print('=' * 80)
