package org.jeecg.config.mybatis.update;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.system.vo.LoginUser;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @description: 制作SQL工具类
 * @author: niugeng
 * @create: 2023-01-07 16:01
 **/
@Slf4j
public class MakeSqlUtil{
    /** * 拼接更新SQL语句 * * @param list 需要更新的实体类集合 * @return java.lang.String 如果没有需要更新的则返回null */
    public static <T> List<String> spliceUpdateSql(List<T> list, TableCacheDTO tableDTO) {
        Map<String, List<Stash>> updateFields = Maps.newHashMap();
        List<Object> ids = Lists.newArrayList();
        for (T t : list) {
            String keyColumn = tableDTO.getKeyColumn();
            if (keyColumn == null) {
                throw new RuntimeException("主键id不存在或者未设置@TableId");
            }
            Object idValue = ReflectUtil.getFieldValue(t, keyColumn);

            // 如果主键的值为null, 则忽略此条更新SQL
            if (idValue == null) {
                continue;
            }
            ids.add(idValue);
            composeUpdateFields(t, idValue, tableDTO, updateFields);
        }
        if (MapUtil.isEmpty(updateFields)) {
            return null;
        }
        return makeUpdateSql(tableDTO, ids, updateFields);
    }
    /** * 制作执行得更新sql语句 * * @param tableDTO 数据表信息 * @param ids 主键值集合 * @param updateFields 需要更新得field集合 * @return java.lang.String 拼接得update SQL */
    private static List<String> makeUpdateSql(TableCacheDTO tableDTO, List<Object> ids, Map<String, List<Stash>> updateFields) {
        List<String> list =new ArrayList<>();

        Iterator<Map.Entry<String, List<Stash>>> iterator = updateFields.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<String, List<Stash>> next = iterator.next();
            List<Stash> value = next.getValue();
            //如果字段数量小于ids数量，表示存在为null的字段，不想更新为null的字段
            if (value.size() < ids.size()){
                List<Object> idsNew = value.stream().map(Stash::getId).collect(Collectors.toList());

                Map<String, List<Stash>> updateFieldsNull = new HashMap<>();
                //将存在null的字段放到新的集合里面，分批执行
                updateFieldsNull.put(next.getKey(),next.getValue());

                list.add(sqlStr(tableDTO, idsNew, updateFieldsNull));

                //移除存在null的字段
                iterator.remove();
            }
        }

        if (!updateFields.isEmpty()){
            list.add(sqlStr(tableDTO, ids, updateFields));
        }

        return list;
    }

    public static String sqlStr(TableCacheDTO tableDTO, List<Object> ids, Map<String, List<Stash>> updateFields) {
        StringBuilder spliceSQL = new StringBuilder();
        // 拼接Where之前的语句
        spliceSQL.append(SqlConstant.DML_UPDATE).append(tableDTO.getTableName()).append(SqlConstant.SQL_SET);

        for (Map.Entry<String, List<Stash>> entry : updateFields.entrySet()) {

            String columnName = entry.getKey();

            spliceSQL.append(columnName).append(SqlConstant.EQUAL_CASE).append(tableDTO.getKeyColumn());
            // 拼接 WHEN xx THEN xx
            for (Stash term : entry.getValue()) {
                Object id = term.getId();
                if (id instanceof String){
                    id = StrUtil.toString(id);
                    id = "'"+id+"'";
                }

                spliceSQL.append(SqlConstant.FUNC_WHEN).append(id).append(SqlConstant.THEN_APOSTROPHE).append(term.getValue())
                        .append(SqlConstant.APOSTROPHE);
            }
            spliceSQL.append(SqlConstant.END_COMMA);
        }
        // 删除最后一个逗号
        spliceSQL.deleteCharAt(spliceSQL.length() - 1);
        // 拼接where语句
        spliceSQL.append(SqlConstant.FUNC_WHERE).append(tableDTO.getKeyColumn()).append(SqlConstant.IN_PARENTHESIS);
        String conditionValue = ids.stream().map(idVal -> SqlConstant.APOSTROPHE + idVal + SqlConstant.APOSTROPHE)
                .collect(Collectors.joining(SqlConstant.COMMA));
        spliceSQL.append(conditionValue).append(SqlConstant.RIGHT_PARENTHESIS);

        return spliceSQL.toString();
    }
    /** * 组合需要更新表的列名得map集合 * * @param t 对象值 * @param idValue 主键得值 * @param tableDTO 表信息 * @param updateFields 组合进得集合 */
    private static <T> void composeUpdateFields(T t, Object idValue, TableCacheDTO tableDTO, Map<String, List<Stash>> updateFields) {
        // 提取存在表中真正的列名集合。这样可以减少因@TableField(exist = false)注解造成的性能浪费
        Set<String> existKeys = tableDTO.getFieldColumn().keySet();
        // 排除主键，因为不需要更新主键信息
        existKeys.remove(tableDTO.getKeyColumn());
        Map<String, Field> targetFieldMap = ReflectUtils.getFieldMap(t.getClass());
        Collection<Field> realFields = MapUtil.filter(targetFieldMap, existKeys.toArray(new String[0])).values();
        LoginUser sysUser = getLoginUser();

        for (Field field : realFields) {
            // 依据列名将不同的值归类到Map中
            String columnName = MapUtil.getStr(tableDTO.getFieldColumn(), field.getName());

            field.setAccessible(true);
            Object fieldValue = ReflectUtil.getFieldValue(t, field);

            if (StrUtil.equalsIgnoreCase(field.getName(),"updateTime")){
                fieldValue = DateUtil.now();
            }
            if (StrUtil.equalsIgnoreCase(field.getName(),"updateBy")){
                if (sysUser != null){
                    fieldValue = sysUser.getUsername();
                }
            }
            if (StrUtil.equalsAnyIgnoreCase(field.getName(),"createTime","createBy")){
                //更新的时候 不更新插入时间和插入人
            //    continue;
            }

            if (fieldValue == null) {
                // 只对非空值的字段进行更新
                continue;
            }
            final Class<?> type = field.getType();
            if (StrUtil.containsIgnoreCase(type.getName(),"date") && !StrUtil.equalsIgnoreCase(field.getName(),"updateTime") && StrUtil.isNotBlank(StrUtil.toString(fieldValue))){
                String pattern = "";
                if (StrUtil.toString(fieldValue).contains(":")){
                    pattern = DatePattern.NORM_DATETIME_PATTERN;
                }else{
                    pattern = DatePattern.NORM_DATE_PATTERN;
                }
                fieldValue = DateUtil.format((Date) fieldValue, pattern);
            }

            List<Stash> valueObjs = updateFields.get(columnName);
            Stash stash = new Stash();
            stash.setId(idValue);
            stash.setValue(fieldValue);
            if (CollUtil.isEmpty(valueObjs)) {
                valueObjs = Lists.newArrayList(stash);
            } else {
                valueObjs.add(stash);
            }
            // 将需要更新的字段保存到Map集合中
            updateFields.put(columnName, valueObjs);
        }
    }
    /** * 为了拼接更新sql了创建的存储内部类 */
    @Data
    private static class Stash {
        private Object id;
        private Object value;
    }

        /**
         * 获取登录用户
         * @return
         */
        private static LoginUser getLoginUser() {
            LoginUser sysUser = null;
            try {
                sysUser = SecurityUtils.getSubject().getPrincipal() != null ? (LoginUser) SecurityUtils.getSubject().getPrincipal() : null;
            } catch (Exception e) {
                //e.printStackTrace();
                sysUser = null;
            }
            return sysUser;
        }
}
