import sys
import os

# Add project root directory to Python path
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

@log_class(type='use')
class Normalizer:
    def __init__(self, shortcut_norm=None):
        self.shortcut_norm = shortcut_norm
    
    @with_evaluation
    def __call__(self, data):
        return self.MY_Normalization(data, norm_params=self.shortcut_norm)
    
    def MY_Normalization(self, data, norm_params=None):
        import pandas as pd
        import numpy as np
        
#region 检查参数和数据
        if norm_params == 'skip':
            ColorPrinter.print_info('Skipping normalization as it is configured to "skip"')
            return data
        
        # 检查norm_params是否为None
        if norm_params is None:
            ColorPrinter.print_warning('归一化参数为None，返回原始数据')
            return data
#endregion

#region 提取归一化参数
        if 'normalized_ways' in norm_params:
            normalized_ways = norm_params['normalized_ways']
            # 从 normalized_ways 中提取列名和方法
            normalized_col = [config['col'] for config in normalized_ways]
            normalizer_method_choice = [config['method'] for config in normalized_ways]
        else:
            ColorPrinter.print_warning('No normalized_ways specified in norm_params, skipping normalization')
            return data
            
#endregion
        
#region 检查数据大小
        data_size = data.memory_usage(deep=True).sum() / (1024 * 1024)  # Convert to MB
        ColorPrinter.print_info(f'Data size: {data_size:.2f} MB')
        # Apply different normalization methods to different columns
#endregion


#region 执行归一化处理
        ColorPrinter.print_info('Applying different normalization methods to different columns')
        try:
            from sklearn.preprocessing import Normalizer
            from sklearn.preprocessing import RobustScaler
            
            # Create data copy
            normalized_data = data.copy()
            
            # Skip processing if no columns specified
            if not normalized_col:
                ColorPrinter.print_info('No normalization columns specified, skipping normalization')
                return data
            # Apply corresponding normalization method to each column
            for col, method in zip(normalized_col, normalizer_method_choice):
                if col in data.columns:
                    if method == 'skip':
                        ColorPrinter.print_info(f'Skipping normalization for column {col}')
                    elif method == 'L1 Normalization':
                        ColorPrinter.print_info(f'Applying {method} to column {col}')
                        normalizer = Normalizer(norm='l1')
                        normalized_data[[col]] = pd.DataFrame(
                            normalizer.fit_transform(normalized_data[[col]]),
                            columns=[col],
                            index=normalized_data.index
                        )
                    elif method == 'L2 Normalization':
                        ColorPrinter.print_info(f'Applying {method} to column {col}')
                        normalizer = Normalizer(norm='l2')
                        normalized_data[[col]] = pd.DataFrame(
                            normalizer.fit_transform(normalized_data[[col]]),
                            columns=[col],
                            index=normalized_data.index
                        )
                    elif method == 'Max Normalization':
                        ColorPrinter.print_info(f'Applying {method} to column {col}')
                        normalizer = Normalizer(norm='max')
                        normalized_data[[col]] = pd.DataFrame(
                            normalizer.fit_transform(normalized_data[[col]]),
                            columns=[col],
                            index=normalized_data.index
                        )
                    elif method == 'Robust Normalization':
                        ColorPrinter.print_info(f'Applying {method} to column {col}')
                        scaler = RobustScaler()
                        normalized_data[[col]] = pd.DataFrame(
                            scaler.fit_transform(normalized_data[[col]]),
                            columns=[col],
                            index=normalized_data.index
                        )
                    elif method == 'Power Normalization':
                        ColorPrinter.print_info(f'Applying {method} to column {col}')
                        normalized_data[col] = normalized_data[col] ** 2
                        normalizer = Normalizer(norm='l2')
                        normalized_data[[col]] = pd.DataFrame(
                            normalizer.fit_transform(normalized_data[[col]]),
                            columns=[col],
                            index=normalized_data.index
                        )
                    elif method == 'Log Normalization':
                        ColorPrinter.print_info(f'Applying {method} to column {col}')
                        min_val = normalized_data[col].min()
                        if min_val <= 0:
                            normalized_data[col] = normalized_data[col] - min_val + 0.001
                        normalized_data[col] = np.log10(normalized_data[col])
                    elif method == 'Square Root Normalization':
                        ColorPrinter.print_info(f'Applying {method} to column {col}')
                        normalized_data[col] = normalized_data[col].clip(lower=0)
                        normalized_data[col] = np.sqrt(normalized_data[col])
                    else:
                        ColorPrinter.print_error(f'Normalization method {method} is invalid for column {col}')
                else:
                    ColorPrinter.print_error(f'Column {col} does not exist')
            return normalized_data
        except Exception as e:
            ColorPrinter.print_error(f'Error during normalization: {e}')
            return data
#endregion


"""
Parameter documentation:

shortcut_norm parameter dictionary:
    - normalized_ways: List of column normalization configurations
      Type: list of dictionaries
      Default: []
      Description: List of dictionaries, each containing normalization configuration for a column
      Each dictionary in normalized_ways contains:
        - col: Column name
          Type: string
          Description: Name of the column to normalize
        - method: Normalization method
          Type: string
          Options: ['L1 Normalization', 'L2 Normalization', 'Max Normalization', 'Robust Normalization', 'Power Normalization', 'Log Normalization', 'Square Root Normalization', 'skip']
          Description: Normalization method for the column, 'skip' to skip normalization

Normalization method descriptions:
  - 'L1 Normalization': Normalize using L1 norm, each sample's feature values divided by L1 norm
  - 'L2 Normalization': Normalize using L2 norm, each sample's feature values divided by L2 norm (most commonly used)
  - 'Max Normalization': Normalize using maximum value, each sample's feature values divided by maximum value
  - 'Robust Normalization': Normalize using RobustScaler, based on median and quartiles, not sensitive to outliers
  - 'Power Normalization': Use square as default power, then perform L2 normalization, suitable for data with large skewness
  - 'Log Normalization': Use log transformation, suitable for positive data with large value range
  - 'Square Root Normalization': Use square root transformation, suitable for non-negative data

Usage example:
    # Apply different normalization methods to different columns
    norm_params = {
        'normalized_ways': [
            {
                'col': 'feature1',
                'method': 'L1 Normalization'
            },
            {
                'col': 'feature2',
                'method': 'L2 Normalization'
            },
            {
                'col': 'feature3',
                'method': 'Max Normalization'
            },
            {
                'col': 'feature4',
                'method': 'skip'  # Skip normalization for this column
            }
        ]
    }
    normalizer = Normalizer(shortcut_norm=norm_params)
    normalized_data = normalizer(data)
"""


if __name__ == "__main__":
    import pandas as pd
    import numpy as np
    
    ColorPrinter.print_any("=" * 60)
    ColorPrinter.print_info("Normalization Processing Test Program")
    ColorPrinter.print_any("=" * 60)
    
    # Create test data
    np.random.seed(42)
    test_data = pd.DataFrame({
        'feature1': np.random.randint(1, 100, 10),
        'feature2': np.random.randint(1, 100, 10),
        'feature3': np.random.randint(1, 100, 10)
    })
    
    ColorPrinter.print_any("\nOriginal data:")
    ColorPrinter.print_any(test_data)
    ColorPrinter.print_any("\nData statistics:")
    ColorPrinter.print_any(test_data.describe())
    
    # Test applying different normalization methods to different columns
    ColorPrinter.print_any(f"\n{'=' * 60}")
    ColorPrinter.print_info("Test: Applying different normalization methods to different columns")
    ColorPrinter.print_any('=' * 60)
    
    norm_params = {
        'normalized_ways': [
            {'col': 'feature1', 'method': 'L1 Normalization'},
            {'col': 'feature2', 'method': 'L2 Normalization'},
            {'col': 'feature3', 'method': 'Max Normalization'}
        ]
    }
    
    normalizer = Normalizer(shortcut_norm=norm_params)
    normalized_data = normalizer(test_data.copy())
    
    ColorPrinter.print_any("\nNormalized data:")
    ColorPrinter.print_any(normalized_data)
    ColorPrinter.print_any("\nNormalized data statistics:")
    ColorPrinter.print_any(normalized_data.describe())
    
    ColorPrinter.print_any("\n" + "=" * 60)
    ColorPrinter.print_success("Test completed!")
    ColorPrinter.print_any("=" * 60)
