package com.jy.log.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.jy.framework.model.annotation.LogTag;
import com.jy.framework.model.base.BaseIdPo;
import com.jy.framework.model.enums.LogTagExtendedType;
import com.jy.log.model.wrapper.ClazzWrapper;
import com.jy.log.model.wrapper.FieldWrapper;
import com.jy.system.service.SysDictDataService;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class LogTagUtil {

    /**
     * 字段对比
     */
    @SneakyThrows
    public static String fieldComparison(Object oldObject, Object newObject) {
        Class<?> modelClazz = newObject.getClass();
        ClazzWrapper clazzWrapper = new ClazzWrapper(modelClazz);

        // 操作描述
        StringBuilder description = new StringBuilder();
        // 遍历字段列表
        List<Field> fieldList = clazzWrapper.getFieldList();
        String separator = "；\n";
        // 筛选出LogTag标记的属性进行属性对比
        List<FieldWrapper> logTagFieldWrappers = fieldList.stream()
                .map(field -> {
                    field.setAccessible(true);
                    try {
                        Object oldValue = field.get(oldObject);
                        Object newValue = field.get(newObject);
                        return new FieldWrapper(field, oldValue, newValue);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("无法访问字段: " + field.getName(), e);
                    }
                })
                .filter(FieldWrapper::isWithLogTag)
                .sorted(Comparator.comparing(wrapper -> wrapper.getLogTag().extendedType()))
                .toList();

        for (FieldWrapper logTagFieldWrapper : logTagFieldWrappers) {
            LogTag logTag = logTagFieldWrapper.getLogTag();
            LogTagExtendedType logTagExtendedType = logTag.extendedType();

            switch (logTagExtendedType) {
                case OBJECT -> {
                    String fieldDescription = fieldComparison(logTagFieldWrapper.getOldValue(), logTagFieldWrapper.getNewValue());
                    if (StrUtil.isNotBlank(fieldDescription)) {
                        description.append(logTagFieldWrapper.getAttributeAlias())
                                .append(":").append(fieldDescription)
                                .append(separator);
                    }
                }
                case COLLECTION -> {
                    String collectionDescription = handleCollectionChange(logTagFieldWrapper);
                    if (StrUtil.isNotBlank(collectionDescription)) {
                        description.append(logTagFieldWrapper.getAttributeAlias())
                                .append("：[").append(collectionDescription).append("]")
                                .append(separator);
                    }
                }
                default -> {
                    // 非 OBJECT/COLLECTION 类型，且值发生变化时处理
                    if (!nullableEquals(logTagFieldWrapper.getOldValue(), logTagFieldWrapper.getNewValue())) {
                        String baseModel = switch (logTagExtendedType) {
                            case NORMAL -> handleNormalExtendedTypeItem(logTagFieldWrapper);
                            case DATE -> handleDateExtendedTypeItem(logTagFieldWrapper);
                            case CONVERT -> handleConvertExtendedTypeItem(logTagFieldWrapper);
                            case DICT -> handleDictExtendedTypeItem(logTagFieldWrapper);
                            case COMPLEX -> handleComplexExtendedTypeItem(logTagFieldWrapper);
                            default -> "";
                        };
                        if (StrUtil.isNotBlank(baseModel)) {
                            description.append(baseModel).append(separator);
                        }
                    }
                }
            }
        }

        return StrUtil.removeSuffix(description.toString(), separator);
    }

    /**
     * 获取对象所有字段日志信息
     */
    @SneakyThrows
    public static String getObjectAllLogTag(Object object) {
        Class<?> modelClazz = object.getClass();
        ClazzWrapper clazzWrapper = new ClazzWrapper(modelClazz);

        // 操作描述
        StringBuilder description = new StringBuilder();
        // 遍历字段列表
        List<Field> fieldList = clazzWrapper.getFieldList();
        String separator = ", ";
        for (Field field : fieldList) {
            field.setAccessible(true);
            FieldWrapper fieldWrapper = new FieldWrapper(field, null, field.get(object));
            // 对LogTag标记的属性进行属性对比
            if (fieldWrapper.isWithLogTag()) {
                LogTag logTag = fieldWrapper.getLogTag();
                if (ObjectUtil.equals(logTag.extendedType(), LogTagExtendedType.OBJECT)) {
                    String fieldDescription = getObjectAllLogTag(fieldWrapper.getNewValue());
                    description.append(fieldWrapper.getAttributeAlias()).append(":").append(fieldDescription).append(separator);
                }
                if (ObjectUtil.equals(logTag.extendedType(), LogTagExtendedType.COLLECTION)) {
                    Collection<?> newValue = (Collection<?>) fieldWrapper.getNewValue();
                    StringBuilder fieldDescription = new StringBuilder();
                    for (Object o : newValue) {
                        fieldDescription.append(getObjectAllLogTag(o));
                    }
                    description.append(fieldWrapper.getAttributeAlias()).append(":").append(fieldDescription).append(separator);
                }

                String baseAttributeModel = switch (logTag.extendedType()) {
                    case NORMAL -> fieldWrapper.getAttributeAlias() + "：" + fieldWrapper.getNewValueString();
                    case CONVERT ->
                            fieldWrapper.getAttributeAlias() + "：" + convertByExp(fieldWrapper.getNewValueString(), logTag.readConverterExp(), logTag.separator());
                    case DICT ->
                            fieldWrapper.getAttributeAlias() + "：" + convertByDict(logTag.dictCode(), fieldWrapper.getNewValueString(), logTag.separator());
                    case COMPLEX -> fieldWrapper.getAttributeAlias();
                    default -> "";
                };

                description.append(baseAttributeModel).append(separator);

            }
        }

        return StrUtil.removeSuffix(description.toString(), separator);
    }

    /**
     * 是否相等
     *
     * @param oldValue
     * @param newValue
     * @return
     */
    private static boolean nullableEquals(Object oldValue, Object newValue) {
        if (oldValue == null && newValue == null) {
            return true;
        }
        if (oldValue == null || newValue == null) {
            return false;
        }
        return ObjectUtil.equals(oldValue, newValue);
    }

    /**
     * 处理正常类型
     *
     * @param fieldWrapper
     * @return
     */
    public static String handleNormalExtendedTypeItem(FieldWrapper fieldWrapper) {
        String oldValueStr = fieldWrapper.getOldValueString();
        if (StrUtil.isBlank(oldValueStr)) {
            oldValueStr = "空";
        }
        String newValueStr = fieldWrapper.getNewValueString();
        if (StrUtil.isBlank(newValueStr)) {
            newValueStr = "空";
        }
        return fieldWrapper.getAttributeAlias() + "：" + oldValueStr + " → " + newValueStr;
    }

    /**
     * 处理日期类型
     *
     * @param fieldWrapper
     * @return
     */
    private static String handleDateExtendedTypeItem(FieldWrapper fieldWrapper) {
        String oldValueStr = fieldWrapper.getOldValueString();
        String format = fieldWrapper.getLogTag().format();
        if (StrUtil.isBlank(oldValueStr)) {
            oldValueStr = "空";
        } else {
            oldValueStr = DateUtil.format((Date) fieldWrapper.getOldValue(), format);
        }
        String newValueStr = fieldWrapper.getNewValueString();
        if (StrUtil.isBlank(newValueStr)) {
            newValueStr = "空";
        } else {
            newValueStr = DateUtil.format((Date) fieldWrapper.getNewValue(), format);
        }
        return fieldWrapper.getAttributeAlias() + "：" + oldValueStr + " → " + newValueStr;
    }

    /**
     * 处理转换类型
     *
     * @param fieldWrapper
     * @return
     */
    private static String handleConvertExtendedTypeItem(FieldWrapper fieldWrapper) {
        LogTag logTag = fieldWrapper.getLogTag();
        String oldValueStr = convertByExp(fieldWrapper.getOldValueString(), logTag.readConverterExp(), logTag.separator());
        String newValueStr = convertByExp(fieldWrapper.getNewValueString(), logTag.readConverterExp(), logTag.separator());
        return fieldWrapper.getAttributeAlias() + "：" + oldValueStr + " → " + newValueStr;
    }

    /**
     * 处理字典类型
     *
     * @param fieldWrapper
     * @return
     */
    private static String handleDictExtendedTypeItem(FieldWrapper fieldWrapper) {
        LogTag logTag = fieldWrapper.getLogTag();
        String oldValueStr = convertByDict(logTag.dictCode(), fieldWrapper.getOldValueString(), logTag.separator());
        String newValueStr = convertByDict(logTag.dictCode(), fieldWrapper.getNewValueString(), logTag.separator());
        return fieldWrapper.getAttributeAlias() + "：" + oldValueStr + " → " + newValueStr;
    }

    /**
     * 处理复杂类型
     *
     * @param fieldWrapper
     * @return
     */
    private static String handleComplexExtendedTypeItem(FieldWrapper fieldWrapper) {
        return "修改了" + fieldWrapper.getAttributeAlias();
    }

    /**
     * 处理集合对象类型
     * @param fieldWrapper
     * @return
     */
    private static String handleCollectionChange(FieldWrapper fieldWrapper) {
        Collection<?> oldCollection = fieldWrapper.getOldValue() == null ? Collections.emptyList() : (Collection<?>) fieldWrapper.getOldValue();
        Collection<?> newCollection = fieldWrapper.getNewValue() == null ? Collections.emptyList() : (Collection<?>) fieldWrapper.getNewValue();

        // 转为 id -> object 的 map
        Map<Long, Object> oldMap = oldCollection.stream()
                .filter(Objects::nonNull)
                .filter(item -> item instanceof BaseIdPo)
                .map(item -> (BaseIdPo) item)
                .filter(item -> item.getId() != null)
                .collect(Collectors.toMap(BaseIdPo::getId, Function.identity()));

        StringBuilder desc = new StringBuilder();

        String separator = "; \n";

        // 遍历新集合：判断新增 or 修改
        for (Object newObj : newCollection) {
            Object oldObj = null;
            Long id = null;
            if (newObj instanceof BaseIdPo) {
                id = ((BaseIdPo) newObj).getId();
            }
            if (id != null && oldMap.containsKey(id)) {
                oldObj = oldMap.remove(id);
            }

            if (oldObj != null) {
                String editDesc = fieldComparison(oldObj, newObj);
                if (StrUtil.isNotBlank(editDesc)) {
                    desc.append("修改数据：{").append(editDesc).append("}").append(separator);
                }
            } else {
                desc.append("新增数据：{").append(getObjectAllLogTag(newObj)).append("}").append(separator);
            }
        }

        // 剩余在 oldMap 中的为删除项
        if (!oldMap.isEmpty()) {
            String deleted = oldMap.values().stream()
                    .map(LogTagUtil::getObjectAllLogTag)
                    .collect(Collectors.joining(separator, "{", "}"));
            desc.append("删除数据：").append(deleted);
        }

        return desc.toString();
    }

    /**
     * 解析字典值
     *
     * @param dictCode      字典编码
     * @param propertyValue 参数值
     * @param separator     分隔符
     * @return 解析后值
     */
    private static String convertByDict(String dictCode, String propertyValue, String separator) {
        if (StrUtil.isBlank(propertyValue)) {
            return "空";
        }

        return SpringUtil.getBean(SysDictDataService.class).getDictLabel(dictCode, propertyValue, separator);
    }

    /**
     * 解析值 0=男,1=女,2=未知
     *
     * @param propertyValue 参数值
     * @param converterExp  翻译注解
     * @param separator     分隔符
     * @return 解析后值
     */
    private static String convertByExp(String propertyValue, String converterExp, String separator) {
        if (StrUtil.isBlank(propertyValue)) {
            return "空";
        }

        String[] convertSource = converterExp.split(separator);
        Map<String, String> convertExpMap = new HashMap<>();
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            convertExpMap.put(itemArray[0].trim(), itemArray[1].trim());
        }

        // 如果是多值（包含分隔符）
        if (propertyValue.contains(separator)) {
            return Arrays.stream(propertyValue.split(separator))
                    .map(key -> convertExpMap.getOrDefault(key.trim(), key))
                    .filter(value -> !value.isEmpty())
                    .collect(Collectors.joining(separator));
        } else {
            // 单值直接返回
            return convertExpMap.getOrDefault(propertyValue.trim(), propertyValue);
        }
    }

}
