r"""
测试 applymap 替换为 map 方法

测试目标：
- 验证 DataFrame.map() 方法能正确替代 applymap()
- 确保 DataOperator.convert_text_to_number() 功能正常
- 确保 DataOperator.convert_datetime_to_str() 功能正常
- 确保 DataOperator.standardize_date_str() 功能正常
- 验证边界情况处理（空值、inf、特殊字符串等）

运行方式：
    cd c:\NJ\python\quannengbao
    py tests\test_applymap_replacement.py
"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))

import pandas as pd
import numpy as np
from datetime import datetime
from quannengbao.data_oper import DataOperator


def test_text_to_number_integer():
    """TC-01: 文本转数字-整数"""
    dop = DataOperator()
    df = pd.DataFrame({'col1': ['123', '456'], 'col2': ['789', '101']})
    result = df.map(lambda x: dop.convert_text_to_number(x))
    expected = pd.DataFrame({'col1': [123, 456], 'col2': [789, 101]})
    assert result.equals(expected), f"Expected:\n{expected}\nGot:\n{result}"
    print("TC-01 PASSED: 文本转数字-整数")


def test_text_to_number_decimal():
    """TC-02: 文本转数字-小数"""
    dop = DataOperator()
    df = pd.DataFrame({'col1': ['12.34', '56.78'], 'col2': ['90.12', '34.56']})
    result = df.map(lambda x: dop.convert_text_to_number(x))
    expected = pd.DataFrame({'col1': [12.34, 56.78], 'col2': [90.12, 34.56]})
    assert result.equals(expected), f"Expected:\n{expected}\nGot:\n{result}"
    print("TC-02 PASSED: 文本转数字-小数")


def test_text_to_number_percentage():
    """TC-03: 文本转数字-百分比"""
    dop = DataOperator()
    df = pd.DataFrame({'col1': ['50%', '25%'], 'col2': ['100%', '75%']})
    result = df.map(lambda x: dop.convert_text_to_number(x))
    expected = pd.DataFrame({'col1': [0.5, 0.25], 'col2': [1.0, 0.75]})
    assert result.equals(expected), f"Expected:\n{expected}\nGot:\n{result}"
    print("TC-03 PASSED: 文本转数字-百分比")


def test_text_to_number_non_numeric():
    """TC-04: 文本转数字-非数字文本保持原样"""
    dop = DataOperator()
    df = pd.DataFrame({'col1': ['abc', 'test'], 'col2': ['hello', 'world']})
    result = df.map(lambda x: dop.convert_text_to_number(x))
    expected = pd.DataFrame({'col1': ['abc', 'test'], 'col2': ['hello', 'world']})
    assert result.equals(expected), f"Expected:\n{expected}\nGot:\n{result}"
    print("TC-04 PASSED: 文本转数字-非数字文本保持原样")


def test_datetime_to_str():
    """TC-05: datetime转字符串"""
    dop = DataOperator()
    dt = datetime(2024, 1, 15, 10, 30, 45)
    df = pd.DataFrame({'col1': [dt, dt], 'col2': ['text', '123']})
    result = df.map(lambda x: dop.convert_datetime_to_str(x))
    assert result['col1'][0] == '2024-01-15 10:30:45', f"Expected '2024-01-15 10:30:45', got {result['col1'][0]}"
    print("TC-05 PASSED: datetime转字符串")


def test_nan_values():
    """TC-06: 空值处理"""
    dop = DataOperator()
    df = pd.DataFrame({'col1': [np.nan, '123'], 'col2': [pd.NaT, '456']})
    df_clean = df.replace({np.nan: '', pd.NaT: ''})
    result = df_clean.map(lambda x: dop.convert_text_to_number(x))
    assert result['col1'][0] == '', f"Expected '', got {result['col1'][0]}"
    assert result['col2'][0] == '', f"Expected '', got {result['col2'][0]}"
    print("TC-06 PASSED: 空值处理")


def test_inf_values():
    """TC-07: inf值处理"""
    dop = DataOperator()
    df = pd.DataFrame({'col1': [np.inf, -np.inf], 'col2': ['123', '456']})
    df_clean = df.replace({np.inf: '', -np.inf: ''})
    result = df_clean.map(lambda x: dop.convert_text_to_number(x))
    assert result['col1'][0] == '', f"Expected '', got {result['col1'][0]}"
    assert result['col1'][1] == '', f"Expected '', got {result['col1'][1]}"
    print("TC-07 PASSED: inf值处理")


def test_special_strings():
    """TC-08: 特殊字符串处理"""
    dop = DataOperator()
    df = pd.DataFrame({'col1': ['NULL', 'null'], 'col2': ['', '123']})
    df_clean = df.replace({'NULL': '', 'null': '', '': ''})
    result = df_clean.map(lambda x: dop.convert_text_to_number(x))
    assert result['col1'][0] == '', f"Expected '', got {result['col1'][0]}"
    assert result['col1'][1] == '', f"Expected '', got {result['col1'][1]}"
    print("TC-08 PASSED: 特殊字符串处理")


def test_mixed_types():
    """TC-09: 混合类型DataFrame"""
    dop = DataOperator()
    df = pd.DataFrame({
        'int_str': ['123', '456'],
        'float_str': ['12.34', '56.78'],
        'percent': ['50%', '25%'],
        'text': ['abc', 'test'],
        'number': [100, 200]
    })
    result = df.map(lambda x: dop.convert_text_to_number(x))
    assert result['int_str'][0] == 123, f"Expected 123, got {result['int_str'][0]}"
    assert result['float_str'][0] == 12.34, f"Expected 12.34, got {result['float_str'][0]}"
    assert result['percent'][0] == 0.5, f"Expected 0.5, got {result['percent'][0]}"
    assert result['text'][0] == 'abc', f"Expected 'abc', got {result['text'][0]}"
    assert result['number'][0] == 100, f"Expected 100, got {result['number'][0]}"
    print("TC-09 PASSED: 混合类型DataFrame")


def test_standardize_date_str():
    """TC-10: standardize_date_str 方法测试"""
    dop = DataOperator()
    df = pd.DataFrame({
        'date_str': ['2024-01-15', '20240115'],
        'datetime_obj': [datetime(2024, 1, 15, 10, 30, 45), datetime(2024, 2, 20, 15, 45, 30)],
        'text': ['abc', 'test']
    })
    result = df.map(lambda x: dop.standardize_date_str(x))
    assert '2024-01-15' in str(result['date_str'][0]), f"Date string conversion failed"
    assert '2024-01-15' in str(result['datetime_obj'][0]), f"Datetime conversion failed"
    assert result['text'][0] == 'abc', f"Text should remain unchanged"
    print("TC-10 PASSED: standardize_date_str 方法测试")


def test_map_vs_applymap_equivalence():
    """TC-11: 验证 map 与 applymap 结果一致性（如果pandas版本支持applymap）"""
    dop = DataOperator()
    df = pd.DataFrame({
        'int_str': ['123', '456'],
        'float_str': ['12.34', '56.78'],
        'text': ['abc', 'test']
    })
    
    result_map = df.map(lambda x: dop.convert_text_to_number(x))
    
    if hasattr(pd.DataFrame, 'applymap'):
        try:
            result_applymap = df.applymap(lambda x: dop.convert_text_to_number(x))
            assert result_map.equals(result_applymap), "map and applymap results should be identical"
            print("TC-11 PASSED: map 与 applymap 结果一致")
        except AttributeError:
            print("TC-11 SKIPPED: applymap not available in this pandas version")
    else:
        print("TC-11 SKIPPED: applymap not available in this pandas version")


def test_clean_dataframe_method():
    """TC-12: 测试 DataOperator.clean_dataframe 方法"""
    dop = DataOperator()
    df = pd.DataFrame({
        'col1': [np.inf, -np.inf, np.nan],
        'col2': ['NULL', 'null', ''],
        'col3': ['2024-01-15', '20240115', 'test']
    })
    result = dop.clean_dataframe(df)
    
    assert result['col1'][0] is None or result['col1'][0] == '', f"inf should be cleaned"
    assert result['col1'][1] is None or result['col1'][1] == '', f"-inf should be cleaned"
    assert result['col2'][0] is None or result['col2'][0] == '', f"NULL should be cleaned"
    assert result['col2'][1] is None or result['col2'][1] == '', f"null should be cleaned"
    print("TC-12 PASSED: clean_dataframe 方法测试")


def run_all_tests():
    """运行所有测试"""
    print("=" * 60)
    print("开始测试 applymap 替换为 map 方法")
    print("=" * 60)
    
    tests = [
        test_text_to_number_integer,
        test_text_to_number_decimal,
        test_text_to_number_percentage,
        test_text_to_number_non_numeric,
        test_datetime_to_str,
        test_nan_values,
        test_inf_values,
        test_special_strings,
        test_mixed_types,
        test_standardize_date_str,
        test_map_vs_applymap_equivalence,
        test_clean_dataframe_method,
    ]
    
    passed = 0
    failed = 0
    skipped = 0
    
    for test in tests:
        try:
            test()
            passed += 1
        except AssertionError as e:
            print(f"FAILED: {test.__name__}")
            print(f"  Error: {e}")
            failed += 1
        except Exception as e:
            print(f"ERROR: {test.__name__}")
            print(f"  Error: {e}")
            failed += 1
    
    print("=" * 60)
    print(f"测试结果: 通过 {passed}, 失败 {failed}, 跳过 {skipped}")
    print("=" * 60)
    
    return failed == 0


if __name__ == '__main__':
    success = run_all_tests()
    sys.exit(0 if success else 1)
