package com.trade.common.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.trade.common.annotation.I18nField;
import com.trade.common.config.mybatis.plugin.BaseI18nMetaData;
import com.trade.common.core.domain.BaseI18nEntity;
import com.trade.common.exception.ServiceException;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.reflection.Reflector;
import org.apache.ibatis.reflection.invoker.Invoker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;

public class I18nUtil {

    public static final Logger log = LoggerFactory.getLogger(I18nUtil.class);
    private final static Map<Class<?>, List<String>> classListMap = new HashMap<>();
    public static ConcurrentMap<Class<?>, Reflector> i18nEntityMethodCache = new ConcurrentHashMap<>();

    /**
     * 拿到clazz中所有带I18nField注解的field的name集合
     *
     * @param clazz 主类Class
     * @return
     */
    public static List<String> getSpecificAnnotationI18nFieldNameList(Class<?> clazz) {
        return classListMap.computeIfAbsent(clazz, (t) -> {
            List<String> list = new ArrayList<>();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(I18nField.class)) {
                    list.add(field.getName());
                }
            }
            return list;
        });
    }

    /**
     * 设置i18nMap
     */
    public static <T extends BaseI18nEntity> void setI18nMap(List<T> baseResult, Class<T> domainClass,
            TableInfo tableInfo, Connection connection) {
        setI18n4DomainEntities(baseResult, domainClass, tableInfo, connection, null, true);
    }

    /**
     * @param language 指定language，如果传，设置指定的语言field。如果不传，则设置当前语言field，并设置i18nMap.
     */
    private static <T extends BaseI18nEntity> void setI18n4DomainEntities(List<T> baseResult, Class<T> domainClass,
            TableInfo tableInfo, Connection connection, String language, boolean withI18nMap) {
        Reflector reflector = I18nUtil.i18nEntityMethodCache.get(domainClass);
        Invoker get = reflector.getGetInvoker("id");
        List<Long> ids = (baseResult).stream().map(result -> {
            Long id = -1L;
            try {
                id = (Long) get.invoke(result, new Object[] {});
            } catch (IllegalAccessException | InvocationTargetException e) {
                log.error("GetInvoker id of class {} failed", domainClass.getName(), e);
            }
            return id;
        }).collect(Collectors.toList());
        List<String> i18nFieldNameList = getSpecificAnnotationI18nFieldNameList(domainClass);
        List<TableFieldInfo> tableFieldInfoList = tableInfo.getFieldList();
        List<TableFieldInfo> i18nTableFieldInfoList = tableFieldInfoList.stream()
                .filter(tableFieldInfo -> i18nFieldNameList.contains(tableFieldInfo.getProperty()))
                .collect(Collectors.toList());
        if (withI18nMap) {
            setI18nMapAndField(i18nTableFieldInfoList, baseResult, ids, reflector, domainClass, tableInfo, connection,
                    language);
        } else {
            setI18nField(i18nTableFieldInfoList, baseResult, ids, reflector, domainClass, tableInfo, connection,
                    language);
        }
    }

    /**
     * 设置I18nField字段，仅设置当前语言。
     */
    private static <T extends BaseI18nEntity> void setI18nField(List<TableFieldInfo> i18nTableFieldInfoList,
            List<T> baseResult, List<Long> ids, Reflector reflector, Class<T> domainClass, TableInfo tableInfo,
            Connection connection, String language) {
        if (CollUtil.isNotEmpty(i18nTableFieldInfoList)) {
            Map<Long, Map<String, String>> i18nMap = getDesignatedI18nField(tableInfo, ids, language,
                    i18nTableFieldInfoList, connection);
            if (CollUtil.isEmpty(i18nMap)) {
                return;
            }
            for (int index = 0; index < ids.size(); index++) {
                Map<String, String> languageMap = i18nMap.get(ids.get(index));
                if (CollUtil.isEmpty(languageMap)) {
                    continue;
                }
                T o = (baseResult).get(index);
                languageMap.forEach((field, value) -> {
                    if (StringUtils.isNotEmpty(value)) {
                        try {
                            reflector.getSetInvoker(field).invoke(o, new Object[] {
                                    value
                            });
                        } catch (Exception e) {
                            log.error("SetInvoker {} of class {} failed", field, domainClass.getName(), e);
                        }
                    }
                });
            }
        }
    }

    /**
     * 设置I18nField字段（当前语言），并且补全i18nMap
     */
    private static <T extends BaseI18nEntity> void setI18nMapAndField(List<TableFieldInfo> i18nTableFieldInfoList,
            List<T> baseResult, List<Long> ids, Reflector reflector, Class<T> domainClass, TableInfo tableInfo,
            Connection connection, String language) {
        if (CollUtil.isNotEmpty(i18nTableFieldInfoList)) {
            Map<Long, Map<String, List<Map<String, String>>>> allI18nMap = getI18nFieldFullMap(tableInfo, ids,
                    i18nTableFieldInfoList, connection);
            if (CollUtil.isEmpty(allI18nMap)) {
                return;
            }
            for (int index = 0; index < ids.size(); index++) {
                Map<String, List<Map<String, String>>> i18nMap = allI18nMap.get(ids.get(index));
                if (CollUtil.isEmpty(i18nMap)) {
                    continue;
                }
                T o = (baseResult).get(index);
                o.setI18n(i18nMap);
                if (language == null) {
                    continue;
                }
                i18nMap.forEach((field, languageList) -> languageList.stream()
                        .filter(item -> language.equalsIgnoreCase(item.get("language"))).findFirst()
                        .map(item -> item.get("value")).ifPresent((value) -> {
                            if (StringUtils.isNotEmpty(value)) {
                                try {
                                    reflector.getSetInvoker(field).invoke(o, new Object[] {
                                            value
                                    });
                                } catch (Exception e) {
                                    log.error("SetInvoker {} of class {} failed", field, domainClass.getName(), e);
                                }
                            }
                        }));
            }
        }
    }

    /**
     * @param language 指定language，如果不传，查所有
     */
    private static List<BaseI18nMetaData> queryI18nMetaData(TableInfo tableInfo, List<Long> ids,
            List<TableFieldInfo> i18nTableFieldInfoList, Connection connection, String language) {
        StringBuilder sb = new StringBuilder("SELECT id, ");
        i18nTableFieldInfoList
                .forEach(f -> sb.append(f.getColumn()).append(" AS ").append(f.getProperty()).append(", "));
        sb.append("language FROM ").append(tableInfo.getTableName()).append("_i18n").append(" WHERE")
                .append("  id  in (");
        sb.append(ids.stream().map((item) -> "?").collect(Collectors.joining(",")));
        sb.append(" )");
        // 先用是否为null做区分
        if (language != null) {
            sb.append(" and language = ?");
        }
        PreparedStatement psm = null;
        try {
            psm = connection.prepareStatement(sb.toString());
            for (int i = 0; i < ids.size(); i++) {
                psm.setLong(i + 1, ids.get(i));
            }
            if (language != null) {
                psm.setString(ids.size() + 1, language);
            }
            ResultSet resultSet = psm.executeQuery();
            List<BaseI18nMetaData> baseI18nMetaDataList = new ArrayList<>();
            while (resultSet.next()) {
                Long id = resultSet.getLong("id");
                i18nTableFieldInfoList.forEach(t -> {
                    try {
                        Object value = resultSet.getObject(t.getProperty());
                        baseI18nMetaDataList.add(new BaseI18nMetaData(id, t.getProperty(),
                                resultSet.getString("language"), value == null ? null : String.valueOf(value)));
                    } catch (SQLException e) {
                        log.error(e.getMessage());
                    }
                });
            }
            resultSet.close();
            return baseI18nMetaDataList;
        } catch (Exception e) {
            log.error("get i18n Fields field", e);
        } finally {
            close(psm);
        }
        return new ArrayList<>();
    }

    /**
     * 批量查询指定语言。 map结构 id : { name : value }
     */
    private static Map<Long, Map<String, String>> getDesignatedI18nField(TableInfo tableInfo, List<Long> ids,
            String language, List<TableFieldInfo> i18nTableFieldInfoList, Connection connection) {
        List<BaseI18nMetaData> baseI18nMetaDataList = queryI18nMetaData(tableInfo, ids, i18nTableFieldInfoList,
                connection, language);
        return baseI18nMetaDataList.stream().filter(item -> item.getValue() != null).collect(Collectors.groupingBy(
                BaseI18nMetaData::getId, Collectors.toMap(BaseI18nMetaData::getField, BaseI18nMetaData::getValue)));
    }

    /**
     * 批量查询多语言。 map结构 id : { name : [{ language: 'ja', value: 1 },{ language: 'en', value: 2 }] }
     */
    public static Map<Long, Map<String, List<Map<String, String>>>> getI18nFieldFullMap(TableInfo tableInfo,
            List<Long> ids, List<TableFieldInfo> i18nTableFieldInfoList, Connection connection) {
        List<BaseI18nMetaData> baseI18nMetaDataList = queryI18nMetaData(tableInfo, ids, i18nTableFieldInfoList,
                connection, null);
        return baseI18nMetaDataList.stream().collect(Collectors.groupingBy(BaseI18nMetaData::getId,
                Collectors.groupingBy(BaseI18nMetaData::getField, Collector.of(ArrayList::new, (list, result) -> {
                    if (result.getValue() != null) {
                        Map<String, String> map = new HashMap<>();
                        map.put("language", result.getLanguage());
                        map.put("value", result.getValue());
                        list.add(map);
                    }
                }, (k1, k2) -> k1))));
    }

    public static void close(Statement x) {
        if (x != null) {
            try {
                x.close();
            } catch (Exception var2) {
                log.debug("close statement error", var2);
            }
        }
    }
}
