package com.jwipc.aggregation.impl;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import com.jwipc.aggregation.ModelAggregationService;
import com.jwipc.aggregation.RelationAggregationData;
import com.jwipc.aggregation.annotation.RelationAggregationField;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhangjie
 * @version 2023.07
 * @since back_repo 2023.07
 */
@Slf4j
@Component
public class ModelAggregationServiceImpl implements ModelAggregationService {
    /**
     * Model对象的RPC接口关联的结构列表，该字段在系统启动阶段一次性预加载，提升运行时效率。
     */
    private final Map<Class<?>, Map<String, RelationAggregationData>> relationAggregationDataMap = Maps.newHashMap();


    /**
     * 通过配置注入模块包路径， 一般为 io.fxc.module.system， 模块包，这样在系统初始化时减少其他模块的类影响，加速速度
     */
    @Value("${sys.base-package}")
    private String modulePackage;

    /**
     * 封装数据，发起远程调用，并获取数据后，将数据加入临时Table，后续聚合数据使用
     *
     * @param relationValueTable      临时Table
     * @param relationAggregationData 数据关联信息
     * @param materId                 当前对象关联字段
     */
    private void relationValueTable(Table<String, Object, Object> relationValueTable,
                                    RelationAggregationData relationAggregationData, Object materId)
    {
        Object relationClient = relationAggregationData.getRelationClient();
        Method clientMethod = relationAggregationData.getClientMethod();
        List<Object> relationObjectList = ReflectUtil.invoke(relationClient, clientMethod, materId);
        if (Objects.isNull(relationObjectList)) {
            return;
        }

        String relationIdField = relationAggregationData.getRelationIdField();
        for (Object relationObject : relationObjectList) {
            Object fieldValue = ReflectUtil.getFieldValue(relationObject, relationIdField);
            relationValueTable.put(relationIdField, fieldValue, relationObject);
        }
    }

    // 在解析数据

    /**
     * 初始化时缓存所有的映射对象逻辑
     */
    @PostConstruct
    protected void init() {
        // 获取VO Dto类
        Set<Class<?>> objectClassList = ClassUtil.scanPackage(modulePackage, aClass -> {
            String className = aClass.getName();
            return StrUtil.endWithAny(className, "DTO");
        });

        for (Class<?> vdtoClass : objectClassList) {
            Map<String, RelationAggregationData> vdtoRelationMap = Maps.newHashMap();
            Field[] fields = ReflectUtil.getFields(vdtoClass);
            for (Field field : fields) {
                // 获取属性注解
                RelationAggregationField relationField = AnnotationUtil.getAnnotation(field,
                        RelationAggregationField.class);
                if (Objects.isNull(relationField)) {
                    continue;
                }

                Class<?> clientClass = relationField.relationClientClass();
                Object clientObj = SpringUtil.getBean(clientClass);
                if (Objects.isNull(clientObj)) {
                    log.error("视图对象 {} 关联字段 {} 无法找到远程加载服务 {}", vdtoClass, field, clientClass);
                    continue;
                }
                String relationMethodName = relationField.relationMethod();
                Method relationMethod = ReflectUtil.getMethodOfObj(clientObj, relationMethodName,
                        Collections.emptyList());

                if (Objects.isNull(relationMethod)) {
                    log.error("视图对象 {} 关联字段 {} 远程加载服务 {} 没有关联字段获取方法 {}", vdtoClass, field,
                            clientClass, relationMethodName);
                    continue;
                }

                RelationAggregationData relationAggregationData = new RelationAggregationData(
                        relationField.masterIdField(), relationField.relationIdField(),
                        relationField.relationNameField(), clientObj, relationMethod);
                vdtoRelationMap.put(field.getName(), relationAggregationData);
            }
            relationAggregationDataMap.put(vdtoClass, vdtoRelationMap);
        }
        if (log.isDebugEnabled()) {
            log.debug("初始化关联处理类完成 ");
        }
    }

    /**
     * 集成所有与主表实体对象相关的关联数据列表。包括本地和远程服务的一对多聚合等。 也可以根据实际需求，单独调用该函数所包含的各个数据集成函数。
     *
     * @param resultList 主表实体对象列表。数据集成将直接作用于该对象列表。
     */
    @Override
    public <M> void processRelationAggregation(List<M> resultList) {

        this.processRelationAggregation(resultList, null);

    }

    @Override
    public <M> void processRelationAggregation(List<M> resultList, Set<String> ignoreFields) {
        if (CollUtil.isEmpty(resultList)) {
            return;
        }

        // 取出模型获取数据
        final M firstModel = resultList.get(0);
        final Class<?> modelClass = firstModel.getClass();
        final Map<String, RelationAggregationData> relationFieldMap = this.relationAggregationDataMap.get(modelClass);
        if (Objects.isNull(relationFieldMap)) {
            return;
        }
        Map<String, String> supplierCodeMap = Maps.newHashMap();
        final Map<String, List<Object>> fieldValueMap = Maps.newHashMap();
        Collection<RelationAggregationData> relationFieldConfig = relationFieldMap.values();
        Set<String> masterIdFields = relationFieldConfig.stream().map(RelationAggregationData::getMasterIdField)
                .collect(Collectors.toSet());
        // 获取当前Bean的关联字段的值
        for (M model : resultList) {
            for (String masterIdField : masterIdFields) {
                Object fieldValue = ReflectUtil.getFieldValue(model, masterIdField);
                if (Objects.isNull(fieldValue)) {
                    continue;
                }
                List<Object> fieldValues = fieldValueMap.get(masterIdField);
                if (Objects.isNull(fieldValues)) {
                    fieldValues = Lists.newArrayList();
                }
                //判断是否重复
                if (StringUtils.isNotEmpty(masterIdField) && masterIdField.equals("supplierCode") && !supplierCodeMap.keySet().contains(String.valueOf(fieldValue))) {
                    fieldValues.add(fieldValue);
                    supplierCodeMap.put((String)fieldValue,(String)fieldValue);
                } else {
                    if(StringUtils.isNotEmpty(masterIdField) && !masterIdField.equals("supplierCode")) {
                        fieldValues.add(fieldValue);
                    }
                }
                fieldValueMap.put(masterIdField, fieldValues);
            }
        }

        final Table<String, Object, Object> relationValueTable = HashBasedTable.create();

        relationFieldConfig.forEach(relationAggregationData -> {
            String masterIdField = relationAggregationData.getMasterIdField();
            Object relationClient = relationAggregationData.getRelationClient();
            if (Objects.isNull(relationClient)) {
                return;
            }
            List<Object> materIds = fieldValueMap.get(masterIdField);
            relationValueTable(relationValueTable, relationAggregationData, materIds);
        });
        // 最后在组装数据
        Set<String> resultObjectFields = relationFieldMap.keySet();
        for (M result : resultList) {
            for (String resultObjectFieldName : resultObjectFields) {
                RelationAggregationData relationAggregationData = relationFieldMap.get(resultObjectFieldName);
                String relationIdField = relationAggregationData.getRelationIdField();
                String masterIdField = relationAggregationData.getMasterIdField();
                String relationNameField = relationAggregationData.getRelationNameField();
                // 获取当前对象的关联字段值
                Object masterId = ReflectUtil.getFieldValue(result, masterIdField);
                // 从数据中获取关联对象数据
                Object relationValue = relationValueTable.get(relationIdField, masterId);
                //需要在从对象中取出值
                final Object fieldValue = ReflectUtil.getFieldValue(relationValue, relationNameField);
                // 写入对象
                ReflectUtil.setFieldValue(result, resultObjectFieldName, fieldValue);
            }
        }

    }

    @Override
    public <M> void processRelationAggregation(M resultObj) {
        this.processRelationAggregation(resultObj, null);
    }

    /**
     * 集成所有与主表实体对象相关的关联数据列表。包括本地和远程服务的一对多聚合等。 也可以根据实际需求，单独调用该函数所包含的各个数据集成函数。
     *
     * @param resultObj    主表实体。数据集成将直接作用于该对象列表。
     * @param ignoreFields 该集合中的字段，即便包含注解也不会在当前调用中进行数据组装。
     */
    @Override
    public <M> void processRelationAggregation(M resultObj, Set<String> ignoreFields) {
        if (Objects.isNull(resultObj)) {
            return;
        }

        // 取出模型获取数据
        final Class<?> modelClass = resultObj.getClass();
        final Map<String, RelationAggregationData> relationFieldMap = this.relationAggregationDataMap.get(modelClass);
        if (Objects.isNull(relationFieldMap)) {
            return;
        }
        Collection<RelationAggregationData> relationFieldConfig = relationFieldMap.values();

        final Map<String, Object> fieldValueMap = Maps.newHashMap();
        Set<String> masterIdFields = relationFieldConfig.stream().map(RelationAggregationData::getMasterIdField)
                .collect(Collectors.toSet());
        // 获取当前Bean的关联字段的值
        for (String masterIdField : masterIdFields) {
            Object fieldValue = ReflectUtil.getFieldValue(resultObj, masterIdField);
            if (Objects.isNull(fieldValue)) {
                continue;
            }
            fieldValueMap.put(masterIdField, fieldValue);
        }

        final Table<String, Object, Object> relationValueTable = HashBasedTable.create();

        relationFieldConfig.forEach(relationAggregationData -> {
            String masterIdField = relationAggregationData.getMasterIdField();
            Object relationClient = relationAggregationData.getRelationClient();
            if (Objects.isNull(relationClient)) {
                return;
            }
            Object materId = fieldValueMap.get(masterIdField);
            relationValueTable(relationValueTable, relationAggregationData, materId);
        });
        // 最后在组装数据
        Set<String> resultObjectFields = relationFieldMap.keySet();
        for (String resultObjectFieldName : resultObjectFields) {
            RelationAggregationData relationAggregationData = relationFieldMap.get(resultObjectFieldName);
            String relationIdField = relationAggregationData.getRelationIdField();
            String relationNameField = relationAggregationData.getRelationNameField();
            String masterIdField = relationAggregationData.getMasterIdField();
            // 获取当前对象的关联字段值
            Object masterId = ReflectUtil.getFieldValue(resultObj, masterIdField);
            // 从数据中获取关联对象数据
            Object relationValue = relationValueTable.get(relationIdField, masterId);
            if (Objects.isNull(relationValue)) {
                continue;
            }
            //需要在从对象中取出值
            final Object fieldValue = ReflectUtil.getFieldValue(relationValue, relationNameField);
            // 写入对象
            ReflectUtil.setFieldValue(resultObj, resultObjectFieldName, fieldValue);
        }
    }

}
