package com.artfess.base.interceptor;

import com.alibaba.druid.pool.DruidPooledResultSet;
import com.alibaba.druid.pool.DruidPooledStatement;
import com.alibaba.druid.proxy.jdbc.NClobProxyImpl;
import com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl;
import com.alibaba.fastjson.JSONObject;
import dm.jdbc.desc.Column;
import dm.jdbc.driver.DmdbNClob;
import dm.jdbc.driver.DmdbPreparedStatement;
import org.apache.ibatis.plugin.Invocation;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 陈实
 * @Package com.artfess.base.interceptor
 * @date 2023/3/17 11:46
 * @Description:
 */
public class MapIntercept {
    /**
     * 处理拦截map
     *
     * @param invocation
     * @return
     * @throws Throwable
     */
    public static Object interceptMap(Invocation invocation) throws Throwable {
        //执行请求方法，框架所得结果保存到result中
        Object result = invocation.proceed();
        if (result != null && result instanceof ArrayList) {
            ArrayList resultList = (ArrayList) result;
            //当前返回值的实体类是实现了InterceptorFlagInterface的才会做转换
             if (resultList.size() != 0 && resultList.get(0) instanceof Map) {
                //将key大写的map转为小写
                return handelResultMap(resultList, getColumns(invocation));
            }
        }
        return result;
    }

    private static Object handelResultMap(ArrayList resultList, Column[] columns) {
        Map<String, String> columnsToMap = columnsToMap(columns);
        List<Map> res = new ArrayList<>();
        for (int i = 0; i < resultList.size(); i++) {
            Map<String, Object> oldMap = (Map<String, Object>) resultList.get(i);
            Map<String, Object> newMap = new HashMap<>(oldMap.size());
            for (Map.Entry<String, Object> entry : oldMap.entrySet()) {
                Object value = entry.getValue();
                //如果数据库类型为clob或blob，sql映射类型为map，需要单独处理
                if(value instanceof NClobProxyImpl) {
                    NClobProxyImpl nClobProxy = (NClobProxyImpl) value;
                    DmdbNClob rawClob = (DmdbNClob) nClobProxy.getRawClob();
                    value = rawClob.data;
                }
                if (columnsToMap.get(entry.getKey()) == null) {
                    newMap.put(entry.getKey(), value);
                } else {//将大写key变为我们正确的列名
                    newMap.put(columnsToMap.get(entry.getKey()), value);
                }
            }
            res.add(newMap);
        }
        return res;
    }

    /**
     * 当sql的返回值是Map类型时，需要将大写的key转成小写的key
     *
     * @param columns
     * @return
     */
    public static Map<String, String> columnsToMap(Column[] columns) {
        if (columns == null || columns.length == 0) {
            throw new RuntimeException("Mybatis拦截器未获取到原SQL中的列名");
        }
        //这个map：当sql的返回值是Map类型时，需要将大写的key转成小写的key
        Map<String, String> columnsToMap1 = new HashMap<>(8);
        for (Column column : columns) {
            String name = column.name;
            //LABEL 是个特殊字，会默认大写，就算在sql中用的是小写
            if ("LABEL".equals(name)) {
                columnsToMap1.put(name.toUpperCase(), "label");
            } else {
                columnsToMap1.put(name.toUpperCase(), column.name);
            }
        }
        return columnsToMap1;
    }

    /**
     * 获取sql中查询结果中的列名
     *
     * @param invocation
     * @return
     */
    public static Column[] getColumns(Invocation invocation) throws SQLException {
        PreparedStatement statement = (PreparedStatement) invocation.getArgs()[0];
        DruidPooledResultSet generatedKeys = (DruidPooledResultSet) statement.getGeneratedKeys();
        DruidPooledStatement poolableStatement = generatedKeys.getPoolableStatement();
        PreparedStatementProxyImpl statement1 = (PreparedStatementProxyImpl) poolableStatement.getStatement();
        DmdbPreparedStatement rawObject = (DmdbPreparedStatement) statement1.getRawObject();
        return rawObject.columns;
    }
}

