package shop.alien.store.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 字段名到中文的映射（從實體類的@ApiModelProperty注解中讀取）
 */
@Slf4j
class FieldNameChineseMap {
    // 緩存：類名 -> (字段名 -> 中文名稱)
    private static final Map<String, Map<String, String>> CLASS_FIELD_MAP_CACHE = new HashMap<>();
    
    /**
     * 從實體類的@ApiModelProperty注解中獲取字段的中文名稱
     */
    static String getChineseName(Class<?> entityClass, String fieldName) {
        if (fieldName == null || entityClass == null) {
            return fieldName;
        }
        
        // 從緩存中獲取
        String className = entityClass.getName();
        Map<String, String> fieldMap = CLASS_FIELD_MAP_CACHE.get(className);
        if (fieldMap != null && fieldMap.containsKey(fieldName)) {
            return fieldMap.get(fieldName);
        }
        
        // 如果緩存中沒有，則通過反射讀取
        if (fieldMap == null) {
            fieldMap = buildFieldNameMap(entityClass);
            CLASS_FIELD_MAP_CACHE.put(className, fieldMap);
        }
        
        return fieldMap.getOrDefault(fieldName, fieldName);
    }
    
    /**
     * 從實體類的@ApiModelProperty注解中獲取字段的中文名稱（通過對象實例）
     */
    static String getChineseName(Object obj, String fieldName) {
        if (obj == null || fieldName == null) {
            return fieldName;
        }
        return getChineseName(obj.getClass(), fieldName);
    }
    
    /**
     * 構建字段名到中文名稱的映射
     */
    private static Map<String, String> buildFieldNameMap(Class<?> clazz) {
        Map<String, String> fieldMap = new HashMap<>();
        
        try {
            // 遍歷類及其父類的所有字段
            Class<?> currentClass = clazz;
            while (currentClass != null && !currentClass.equals(Object.class)) {
                Field[] fields = currentClass.getDeclaredFields();
                for (Field field : fields) {
                    String fieldName = field.getName();
                    
                    // 如果已經有映射，跳過（子類字段優先）
                    if (fieldMap.containsKey(fieldName)) {
                        continue;
                    }
                    
                    // 嘗試從@ApiModelProperty注解中獲取中文名稱
                    ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
                    if (apiModelProperty != null) {
                        String value = apiModelProperty.value();
                        if (StringUtils.isNotBlank(value)) {
                            fieldMap.put(fieldName, value);
                            continue;
                        }
                    }
                    
                    // 如果沒有注解或注解值為空，使用字段名
                    fieldMap.put(fieldName, fieldName);
                }
                
                // 繼續處理父類
                currentClass = currentClass.getSuperclass();
            }
        } catch (Exception e) {
            log.warn("構建字段名映射失敗，類: {}", clazz.getName(), e);
        }
        
        return fieldMap;
    }
    
    /**
     * 獲取字段的中文名稱（兼容舊方法，使用默認映射）
     */
    static String getChineseName(String fieldName) {
        if (fieldName == null) {
            return fieldName;
        }
        // 如果沒有實體類信息，返回字段名本身
        return fieldName;
    }
    
    /**
     * 批量獲取字段的中文名稱映射
     */
    static Map<String, String> getChineseNameMap(Class<?> entityClass, Set<String> fieldNames) {
        Map<String, String> map = new HashMap<>();
        if (fieldNames != null && entityClass != null) {
            for (String fieldName : fieldNames) {
                map.put(fieldName, getChineseName(entityClass, fieldName));
            }
        }
        return map;
    }
}

/**
 * 數據比較工具類
 * 用於比較修改前後的數據，生成變更描述
 *
 * @author ssk
 * @since 2025-12-09
 */
@Slf4j
public class DataCompareUtil {

    /**
     * 比較兩個對象的差異，生成變更描述
     *
     * @param beforeObj 修改前的對象
     * @param afterObj  修改後的對象
     * @param fieldNameMap 字段名稱映射（可選，用於將字段名轉換為中文描述）
     * @return 變更描述列表
     */
    public static List<String> compareObjects(Object beforeObj, Object afterObj, Map<String, String> fieldNameMap) {
        List<String> changes = new ArrayList<>();
        
        if (beforeObj == null && afterObj == null) {
            return changes;
        }
        
        if (beforeObj == null) {
            changes.add("新增數據");
            return changes;
        }
        
        if (afterObj == null) {
            changes.add("刪除數據");
            return changes;
        }

        try {
            // 檢查對象是否為字符串（可能是SpEL表達式解析失敗）
            if (beforeObj instanceof String || afterObj instanceof String) {
                log.warn("比較對象中包含字符串類型，跳過比較。beforeObj類型: {}, afterObj類型: {}", 
                        beforeObj != null ? beforeObj.getClass().getSimpleName() : "null",
                        afterObj != null ? afterObj.getClass().getSimpleName() : "null");
                changes.add("數據變更（對象類型不正確）");
                return changes;
            }
            
            // 將對象轉換為JSON對象以便比較（避免反射問題）
            String beforeJsonStr = JSON.toJSONString(beforeObj);
            String afterJsonStr = JSON.toJSONString(afterObj);
            
            log.info("修改前的JSON數據: {}", beforeJsonStr.length() > 500 ? beforeJsonStr.substring(0, 500) + "..." : beforeJsonStr);
            log.info("修改後的JSON數據: {}", afterJsonStr.length() > 500 ? afterJsonStr.substring(0, 500) + "..." : afterJsonStr);
            
            JSONObject beforeJson = JSONObject.parseObject(beforeJsonStr);
            JSONObject afterJson = JSONObject.parseObject(afterJsonStr);

            if (beforeJson == null || afterJson == null) {
                log.warn("JSON解析失敗，beforeJson: {}, afterJson: {}", beforeJson != null, afterJson != null);
                changes.add("數據變更（JSON解析失敗）");
                return changes;
            }
            
            log.info("修改前的JSON字段: {}", beforeJson.keySet());
            log.info("修改後的JSON字段: {}", afterJson.keySet());

            // 獲取所有字段
            Set<String> allFields = new HashSet<>();
            allFields.addAll(beforeJson.keySet());
            allFields.addAll(afterJson.keySet());
            
            // 如果沒有提供字段映射，則從實體類的@ApiModelProperty注解中讀取
            if (fieldNameMap == null) {
                Class<?> entityClass = beforeObj != null ? beforeObj.getClass() : 
                                      (afterObj != null ? afterObj.getClass() : null);
                if (entityClass != null) {
                    // 跳過JDK內部類
                    if (!entityClass.getName().startsWith("java.") && !entityClass.getName().startsWith("javax.")) {
                        fieldNameMap = FieldNameChineseMap.getChineseNameMap(entityClass, allFields);
                        log.debug("從實體類 {} 的@ApiModelProperty注解中讀取字段映射，共 {} 個字段", 
                                entityClass.getSimpleName(), fieldNameMap.size());
                    }
                }
            }

            // 過濾不需要比較的字段
            Set<String> excludeFields = new HashSet<>(Arrays.asList(
                    "createdTime", "updatedTime", "createdUserId", "updatedUserId",
                    "deleteFlag", "delFlag", "version", "serialVersionUID", "class"
            ));

            // 比較每個字段
            log.info("開始比較字段，總共 {} 個字段，排除 {} 個字段", allFields.size(), excludeFields.size());
            for (String field : allFields) {
                if (excludeFields.contains(field)) {
                    log.debug("跳過排除字段: {}", field);
                    continue;
                }

                Object beforeValue = beforeJson.get(field);
                Object afterValue = afterJson.get(field);

                // 輸出每個字段的比較信息
                log.info("比較字段: {} - 修改前: [{}], 修改後: [{}]", field, beforeValue, afterValue);

                // 比較值是否發生變化（使用深度比較）
                boolean isEqual = deepEquals(beforeValue, afterValue);
                if (!isEqual) {
                    String fieldName = getFieldLabel(field, fieldNameMap);
                    String changeDesc = formatChange(fieldName, beforeValue, afterValue);
                    if (StringUtils.isNotBlank(changeDesc)) {
                        changes.add(changeDesc);
                        log.info("✓ 發現字段變更: {} - 修改前: [{}], 修改後: [{}]", fieldName, beforeValue, afterValue);
                    } else {
                        log.warn("字段變更但描述為空: {} - 修改前: {}, 修改後: {}", fieldName, beforeValue, afterValue);
                    }
                } else {
                    log.debug("字段 {} 未變更: [{}]", field, beforeValue);
                }
            }
            
            // 如果沒有變化，添加提示並記錄詳細信息
            if (changes.isEmpty()) {
                log.error("========== 未發現任何字段變更 ==========");
                log.error("比較的字段數量: {}, 排除的字段: {}", allFields.size(), excludeFields.size());
                log.error("所有字段列表: {}", allFields);
                log.error("排除的字段列表: {}", excludeFields);
                log.error("修改前的完整數據: {}", beforeJsonStr);
                log.error("修改後的完整數據: {}", afterJsonStr);
                
                // 強制比較幾個關鍵字段
                String[] keyFields = {"festivalName", "festival_name", "particularYear", "particular_year", 
                                     "festivalDate", "festival_date", "name", "title"};
                for (String keyField : keyFields) {
                    if (beforeJson.containsKey(keyField) || afterJson.containsKey(keyField)) {
                        Object beforeVal = beforeJson.get(keyField);
                        Object afterVal = afterJson.get(keyField);
                        boolean isEqual = deepEquals(beforeVal, afterVal);
                        log.error("關鍵字段 {} 比較: 修改前=[{}], 修改後=[{}], 是否相等={}", 
                                keyField, beforeVal, afterVal, isEqual);
                        if (!isEqual) {
                            String changeDesc = formatChange(keyField, beforeVal, afterVal);
                            if (StringUtils.isNotBlank(changeDesc)) {
                                changes.add(changeDesc);
                                log.error("強制發現字段變更: {}", changeDesc);
                            }
                        }
                    }
                }
                
                if (changes.isEmpty()) {
                    changes.add("無字段變更");
                }
            } else {
                log.info("發現 {} 個字段變更", changes.size());
            }
            
        } catch (Exception e) {
            log.error("比較數據對象失敗", e);
            changes.add("數據變更（詳細比較失敗: " + e.getMessage() + "）");
        }

        return changes;
    }
    
    /**
     * 深度比較兩個值是否相等
     */
    private static boolean deepEquals(Object obj1, Object obj2) {
        if (obj1 == obj2) {
            return true;
        }
        if (obj1 == null || obj2 == null) {
            log.debug("深度比較：一個值為null，obj1={}, obj2={}", obj1, obj2);
            return false;
        }
        
        // 如果是字符串，去除首尾空格後比較
        if (obj1 instanceof String && obj2 instanceof String) {
            String str1 = ((String) obj1).trim();
            String str2 = ((String) obj2).trim();
            boolean equals = str1.equals(str2);
            if (!equals) {
                log.debug("字符串比較不相等: '{}' vs '{}'", str1, str2);
            }
            return equals;
        }
        
        // 如果是數字類型，轉換為字符串比較（避免類型不同但值相同的情況）
        if (obj1 instanceof Number && obj2 instanceof Number) {
            boolean equals = Objects.equals(obj1.toString(), obj2.toString());
            if (!equals) {
                log.debug("數字比較不相等: {} vs {}", obj1, obj2);
            }
            return equals;
        }
        
        // 其他類型直接比較
        boolean equals = Objects.equals(obj1, obj2);
        if (!equals) {
            log.debug("對象比較不相等: {} ({}) vs {} ({})", obj1, obj1.getClass().getSimpleName(), obj2, obj2.getClass().getSimpleName());
        }
        return equals;
    }
    
    /**
     * 獲取字段標籤（優先使用映射，否則使用字段名）
     */
    private static String getFieldLabel(String fieldName, Map<String, String> fieldNameMap) {
        // 優先使用傳入的映射
        if (fieldNameMap != null && fieldNameMap.containsKey(fieldName)) {
            String label = fieldNameMap.get(fieldName);
            // 如果映射中的值不是字段名本身，說明有中文名稱，直接返回
            if (!label.equals(fieldName) && StringUtils.isNotBlank(label)) {
                return label;
            }
        }
        // 如果沒有映射或映射值為空，使用字段名
        return fieldName;
    }

    /**
     * 格式化變更描述
     */
    private static String formatChange(String fieldName, Object beforeValue, Object afterValue) {
        if (beforeValue == null && afterValue == null) {
            return null;
        }

        if (beforeValue == null) {
            return String.format("%s: 新增為 %s", fieldName, formatValue(afterValue));
        }

        if (afterValue == null) {
            return String.format("%s: 從 %s 變更為空", fieldName, formatValue(beforeValue));
        }

        return String.format("%s: 从 %s 修改成了 %s", fieldName, formatValue(beforeValue), formatValue(afterValue));
    }

    /**
     * 格式化值（處理長字符串和特殊類型）
     */
    private static String formatValue(Object value) {
        if (value == null) {
            return "空";
        }

        String strValue = value.toString();
        
        // 如果是字符串且過長，截取前50個字符
        if (strValue.length() > 50) {
            return strValue.substring(0, 50) + "...";
        }

        return strValue;
    }

    /**
     * 比較兩個對象並生成簡要描述
     *
     * @param beforeObj 修改前的對象
     * @param afterObj  修改後的對象
     * @param idField   標識字段（如id）
     * @param nameField 名稱字段（如name、title等）
     * @return 變更描述
     */
    public static String generateChangeDescription(Object beforeObj, Object afterObj, String idField, String nameField) {
        try {
            String idStr = getFieldValue(beforeObj != null ? beforeObj : afterObj, idField);
            String nameStr = getFieldValue(beforeObj != null ? beforeObj : afterObj, nameField);
            
            // 獲取字段的中文名稱
            String idFieldChinese = FieldNameChineseMap.getChineseName(idField);
            String nameFieldChinese = FieldNameChineseMap.getChineseName(nameField);

            if (beforeObj == null) {
                return String.format("新增了%s為%s,%s為%s的記錄", 
                        idFieldChinese, StringUtils.isNotBlank(idStr) ? idStr : "未知",
                        nameFieldChinese, StringUtils.isNotBlank(nameStr) ? nameStr : "未知");
            }

            if (afterObj == null) {
                return String.format("刪除了%s為%s,%s為%s的記錄", 
                        idFieldChinese, StringUtils.isNotBlank(idStr) ? idStr : "未知",
                        nameFieldChinese, StringUtils.isNotBlank(nameStr) ? nameStr : "未知");
            }

            // 比較對象差異（會自動從@ApiModelProperty注解中讀取中文名稱）
            List<String> changes = compareObjects(beforeObj, afterObj, null);
            
            // 構建描述
            StringBuilder desc = new StringBuilder();
            if (StringUtils.isNotBlank(nameStr)) {
                desc.append("修改了").append(nameStr);
            } else if (StringUtils.isNotBlank(idStr)) {
                desc.append("修改了").append(idFieldChinese).append("為").append(idStr).append("的記錄");
            } else {
                desc.append("修改了記錄");
            }
            
            // 添加具體變更字段
            if (changes != null && !changes.isEmpty() && !changes.contains("無字段變更")) {
                desc.append("，變更內容：").append(String.join("; ", changes));
            }
            
            return desc.toString();
        } catch (Exception e) {
            log.error("生成變更描述失敗", e);
            return "數據發生變更";
        }
    }

    /**
     * 獲取對象的字段值（通過反射）
     */
    private static String getFieldValue(Object obj, String fieldName) {
        if (obj == null || StringUtils.isBlank(fieldName)) {
            return "";
        }

        try {
            // 先嘗試通過JSON獲取
            JSONObject jsonObj = JSONObject.parseObject(JSON.toJSONString(obj));
            Object value = jsonObj.get(fieldName);
            return value != null ? value.toString() : "";

            // 也可以使用反射方式
            // Class<?> clazz = obj.getClass();
            // Field field = clazz.getDeclaredField(fieldName);
            // field.setAccessible(true);
            // Object value = field.get(obj);
            // return value != null ? value.toString() : "";
        } catch (Exception e) {
            log.debug("獲取字段值失敗: {}", fieldName, e);
            return "";
        }
    }

    /**
     * 比較兩個列表的差異（用於批量操作）
     *
     * @param beforeList 修改前的列表
     * @param afterList  修改後的列表
     * @param idField    標識字段
     * @return 變更描述
     */
    public static String compareLists(List<?> beforeList, List<?> afterList, String idField) {
        if (beforeList == null) {
            beforeList = new ArrayList<>();
        }
        if (afterList == null) {
            afterList = new ArrayList<>();
        }

        List<String> changes = new ArrayList<>();

        // 查找新增的項
        Set<String> beforeIds = extractIds(beforeList, idField);
        Set<String> afterIds = extractIds(afterList, idField);

        Set<String> addedIds = new HashSet<>(afterIds);
        addedIds.removeAll(beforeIds);
        if (!addedIds.isEmpty()) {
            changes.add(String.format("新增了%d條記錄(id: %s)", addedIds.size(), String.join(", ", addedIds)));
        }

        // 查找刪除的項
        Set<String> deletedIds = new HashSet<>(beforeIds);
        deletedIds.removeAll(afterIds);
        if (!deletedIds.isEmpty()) {
            changes.add(String.format("刪除了%d條記錄(id: %s)", deletedIds.size(), String.join(", ", deletedIds)));
        }

        // 查找修改的項
        Set<String> commonIds = new HashSet<>(beforeIds);
        commonIds.retainAll(afterIds);
        int modifiedCount = 0;
        for (String id : commonIds) {
            Object beforeObj = findById(beforeList, id, idField);
            Object afterObj = findById(afterList, id, idField);
            if (beforeObj != null && afterObj != null) {
                List<String> fieldChanges = compareObjects(beforeObj, afterObj, null);
                if (!fieldChanges.isEmpty()) {
                    modifiedCount++;
                }
            }
        }
        if (modifiedCount > 0) {
            changes.add(String.format("修改了%d條記錄", modifiedCount));
        }

        return String.join("; ", changes);
    }

    /**
     * 從列表中提取ID
     */
    private static Set<String> extractIds(List<?> list, String idField) {
        Set<String> ids = new HashSet<>();
        for (Object obj : list) {
            String id = getFieldValue(obj, idField);
            if (StringUtils.isNotBlank(id)) {
                ids.add(id);
            }
        }
        return ids;
    }

    /**
     * 根據ID查找對象
     */
    private static Object findById(List<?> list, String id, String idField) {
        for (Object obj : list) {
            String objId = getFieldValue(obj, idField);
            if (id.equals(objId)) {
                return obj;
            }
        }
        return null;
    }
}

