package cn.ey.biz.module.system.service.category;

import cn.ey.biz.common.enums.CommonStatusEnum;
import cn.ey.biz.module.system.api.category.dto.CategoryCodeExtraRespDTO;
import cn.ey.biz.module.system.api.category.dto.CategorySyncReq;
import cn.ey.biz.module.system.controller.admin.category.vo.CategoryDataCreateReqVO;
import cn.ey.biz.module.system.controller.admin.category.vo.CategoryDataUpdateReqVO;
import cn.ey.biz.module.system.controller.admin.i18n.vo.I18nRespVO;
import cn.ey.biz.module.system.dal.dataobject.category.CategoryDataDO;
import cn.ey.biz.module.system.dal.mapper.category.CategoryDataMapper;
import cn.ey.biz.module.system.dal.mapper.category.CategoryMapper;
import cn.ey.biz.module.system.enums.YesOrNoStatusEnum;
import cn.ey.biz.module.system.enums.category.CategoryCommonConstants;
import cn.ey.biz.module.system.enums.category.CategoryDataHasChildrenEnum;
import cn.ey.biz.module.system.enums.category.CategoryDataIdEnum;
import cn.ey.biz.module.system.service.category.v2.CategoryDataV2Service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Jian.Yang
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CategorySyncServiceImpl implements CategorySyncService{
    private final CategoryDataV2Service categoryDataService;
    private final CategoryMapper categoryMapper;
    private final CategoryDataMapper categoryDataMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void dictSync(CategorySyncReq syncReq) {
        CategoryCodeExtraRespDTO categoryCodeExtraInfo = syncReq.getCategoryCodeExtraInfo();
        Integer storeType = categoryCodeExtraInfo.getStoreType();
        if (storeType == YesOrNoStatusEnum.YES.getCode()) {
            this.independentDictSync(syncReq);
        } else {
            this.publicDictSync(syncReq);
        }
    }

    /**
     * 公共字典同步
     *
     * @param syncReq 同步请求参数
     */
    public void publicDictSync(CategorySyncReq syncReq) {
        CategoryCodeExtraRespDTO categoryCodeExtraInfo = syncReq.getCategoryCodeExtraInfo();
        Integer isFullSync = categoryCodeExtraInfo.getIsFullSync();
        List<Map<String, Object>> addRows = syncReq.getAddRows();
        addExtraInfo(addRows, categoryCodeExtraInfo);
        if (isFullSync != null && isFullSync == YesOrNoStatusEnum.YES.getCode()) {
            if (CollectionUtil.isNotEmpty(addRows)) {
                categoryDataMapper.delete(new LambdaQueryWrapper<CategoryDataDO>()
                        .eq(CategoryDataDO::getCategoryCode, categoryCodeExtraInfo.getCategoryCode()));
                List<CategoryDataCreateReqVO> createReqVOList = getCategoryDataCreateReq(categoryCodeExtraInfo, addRows);
                categoryDataService.createBatchCategoryData(createReqVOList);
            }
        } else {
            // update
            List<Map<String, Object>> updateRows = syncReq.getUpdateRows();
            if (CollectionUtil.isNotEmpty(updateRows)) {
                initUpdateRowInfo(updateRows, addRows, categoryCodeExtraInfo);
                List<CategoryDataUpdateReqVO> updateReqVOList = getCategoryDataUpdateReq(categoryCodeExtraInfo, updateRows);
                categoryDataService.updateBatchCategoryData(updateReqVOList);
            }
            // add
            if (CollectionUtil.isNotEmpty(addRows)) {
                List<CategoryDataCreateReqVO> createReqVOList = getCategoryDataCreateReq(categoryCodeExtraInfo, addRows);
                categoryDataService.createBatchCategoryData(createReqVOList);
            }
        }
    }

    /**
     * 独立字典同步
     *
     * @param syncReq 同步请求参数
     */
    public void independentDictSync(CategorySyncReq syncReq) {
        CategoryCodeExtraRespDTO categoryCodeExtraInfo = syncReq.getCategoryCodeExtraInfo();
        Integer isFullSync = categoryCodeExtraInfo.getIsFullSync();
        String tableName = categoryCodeExtraInfo.getStoreTableName();
        List<Map<String, Object>> addRows = syncReq.getAddRows();
        addExtraInfo(addRows, categoryCodeExtraInfo);
        if (isFullSync != null && isFullSync == YesOrNoStatusEnum.YES.getCode()) {
            if (CollectionUtil.isNotEmpty(addRows)) {
                initAddRowInfo(addRows, categoryCodeExtraInfo);
                // delete all
                categoryMapper.deleteFullDataDynamic(tableName);
                // insert all
                this.insertSyncData(tableName, addRows);
            }
        } else { // 增量
            List<Map<String, Object>> updateRows = syncReq.getUpdateRows();
            if (CollectionUtil.isNotEmpty(updateRows)) {
                initUpdateRowInfo(updateRows, addRows, categoryCodeExtraInfo);
                this.updateSyncData(tableName, updateRows);
            }
            // 新增
            if (CollectionUtil.isNotEmpty(addRows)) {
                initAddRowInfo(addRows, categoryCodeExtraInfo);
                this.insertSyncData(tableName, addRows);
            }
        }
    }

    private List<CategoryDataUpdateReqVO> getCategoryDataUpdateReq(CategoryCodeExtraRespDTO categoryCodeExtraInfo,final List<Map<String, Object>> updateRows) {
        List<CategoryDataUpdateReqVO> updateReqVOList = Collections.synchronizedList(new ArrayList<>());
        String categoryCode = categoryCodeExtraInfo.getCategoryCode();
        List<CategoryDataDO> categoryDataEntities = categoryDataMapper.selectList(new LambdaQueryWrapper<CategoryDataDO>()
                .eq(CategoryDataDO::getCategoryCode, categoryCode)
                .eq(CategoryDataDO::getNewVersion, YesOrNoStatusEnum.YES.getCode())
        );
        // 保证同category_code下data_code 唯一(同版本)
        Map<String, List<CategoryDataDO>> categoryDataCodeMap = categoryDataEntities.stream().collect(Collectors.groupingBy(CategoryDataDO::getCode));
        updateRows.parallelStream().forEach(updateRow -> {
            if (updateRow.get(CategoryCommonConstants.DATA_CODE) != null) {
                String dataCode = (String) updateRow.get(CategoryCommonConstants.DATA_CODE);
                CategoryDataDO categoryDataDO = categoryDataCodeMap.get(dataCode).get(0);
                CategoryDataUpdateReqVO updateReqVO = new CategoryDataUpdateReqVO();
                updateReqVO.setId(categoryDataDO.getId());
                updateReqVO.setPid(categoryDataDO.getPid());
                updateReqVO.setCreateType(CategoryCommonConstants.CreatedType.SYNC.getCode());
                updateReqVO.setDataParentIdentifier(categoryDataDO.getDataParentIdentifier());
                updateReqVO.setCategoryCode(categoryCode);
                updateReqVO.setDataIdentifier(categoryDataDO.getDataIdentifier());
                updateReqVO.setCode(dataCode);
                updateReqVO.setName(String.valueOf(updateRow.get(CategoryCommonConstants.DATA_NAME)));
                updateReqVO.setHasChildren(categoryDataDO.getHasChildren());
                updateReqVO.setStatus(updateRow.get(CategoryCommonConstants.STATUS) == null ?
                        CommonStatusEnum.ENABLE.getStatus() : (Integer) updateRow.get(CategoryCommonConstants.STATUS));
                updateReqVO.setLangs(initI18nFromMapRow(updateRow));
                updateReqVO.setCategoryVersion(categoryDataDO.getCategoryVersion());
                updateReqVO.setDataVersion(categoryDataDO.getDataVersion());
                Integer isNewVersion = updateRow.get(CategoryCommonConstants.NEW_VERSION) == null ?
                        categoryDataDO.getNewVersion() : (Integer) updateRow.get(CategoryCommonConstants.NEW_VERSION);
                updateReqVO.setNewVersion(isNewVersion);
                updateReqVO.setSort(updateRow.get(CategoryCommonConstants.SORT) == null ?
                        -1 : (Integer) updateRow.get(CategoryCommonConstants.SORT));
                if (updateRow.get(CategoryCommonConstants.DATA_REMARK) != null) {
                    updateReqVO.setRemark(String.valueOf(updateRow.get(CategoryCommonConstants.DATA_REMARK)));
                }
                if (updateRow.get(CategoryCommonConstants.UPDATE_DATE) != null) {
                    updateReqVO.setUpdateTime(DateUtil.parse((String) updateRow.get(CategoryCommonConstants.UPDATE_DATE),
                            DatePattern.NORM_DATETIME_PATTERN));
                }
                if (updateRow.get(CategoryCommonConstants.CREATE_DATE) != null) {
                    updateReqVO.setCreateTime(DateUtil.parse((String) updateRow.get(CategoryCommonConstants.CREATE_DATE),
                            DatePattern.NORM_DATETIME_PATTERN));
                }
                updateReqVOList.add(updateReqVO);
            }
        });
        return updateReqVOList;
    }

    private List<CategoryDataCreateReqVO> getCategoryDataCreateReq(CategoryCodeExtraRespDTO categoryCodeExtraInfo, final List<Map<String, Object>> addRows) {
        List<CategoryDataCreateReqVO> createReqVOList = new ArrayList<>();
        for (Map<String, Object> addRow : addRows) {
            if (addRow.get(CategoryCommonConstants.DATA_CODE) == null) {
                continue;
            }
            CategoryDataCreateReqVO createReqVO = new CategoryDataCreateReqVO();
            createReqVO.setPid(CategoryDataIdEnum.ROOT.getId());
            createReqVO.setCreateType(CategoryCommonConstants.CreatedType.SYNC.getCode());
            createReqVO.setCategoryCode(categoryCodeExtraInfo.getCategoryCode());
            createReqVO.setCode(String.valueOf(addRow.get(CategoryCommonConstants.DATA_CODE)));
            createReqVO.setName(String.valueOf(addRow.get(CategoryCommonConstants.DATA_NAME)));
            createReqVO.setHasChildren(CategoryDataHasChildrenEnum.NOT_HAVE.getHasChildren());
            createReqVO.setStatus(addRow.get(CategoryCommonConstants.STATUS) == null ?
                    CommonStatusEnum.ENABLE.getStatus() :
                    (Integer) addRow.get(CategoryCommonConstants.STATUS) );
            createReqVO.setLangs(initI18nFromMapRow(addRow));
            createReqVO.setCategoryVersion(categoryCodeExtraInfo.getCategoryVersion());
            createReqVO.setDataVersion(1L);
            createReqVO.setNewVersion(YesOrNoStatusEnum.YES.getCode());
            createReqVO.setSort(addRow.get(CategoryCommonConstants.SORT) == null ?
                    -1 : (Integer) addRow.get(CategoryCommonConstants.SORT));
            if (addRow.get(CategoryCommonConstants.DATA_REMARK) != null) {
                createReqVO.setRemark(String.valueOf(addRow.get(CategoryCommonConstants.DATA_REMARK)));
            }
            if (addRow.get(CategoryCommonConstants.CREATE_DATE) != null) {
                createReqVO.setCreateTime(DateUtil.parse((String)addRow.get(CategoryCommonConstants.CREATE_DATE),
                        DatePattern.NORM_DATETIME_PATTERN));
            }
            if (addRow.get(CategoryCommonConstants.UPDATE_DATE) != null) {
                createReqVO.setUpdateTime(DateUtil.parse((String)addRow.get(CategoryCommonConstants.UPDATE_DATE),
                        DatePattern.NORM_DATETIME_PATTERN));
            }
            addRow.putIfAbsent(CategoryCommonConstants.CREATE_DATE, addRow.get(CategoryCommonConstants.UPDATE_DATE));
            createReqVOList.add(createReqVO);
        }
        return createReqVOList;

    }

    private List<I18nRespVO> initI18nFromMapRow(Map<String, Object> row) {
        List<I18nRespVO> in18ns = new ArrayList<>();
        if (row != null && !row.isEmpty()) {
            I18nRespVO i18nRespCn = new I18nRespVO();
            i18nRespCn.setLang(CategoryCommonConstants.CN);
            i18nRespCn.setLabel(String.valueOf(row.get(CategoryCommonConstants.DATA_NAME)));
            in18ns.add(i18nRespCn);
            I18nRespVO i18nRespEn = new I18nRespVO();
            i18nRespEn.setLang(CategoryCommonConstants.EN);
            i18nRespEn.setLabel(String.valueOf(row.get(CategoryCommonConstants.DATA_VALUE)));
            in18ns.add(i18nRespEn);
        }
        return in18ns;
    }

    private void updateSyncData(String tableName, List<Map<String, Object>> updateRows) {
        if (CollectionUtil.isNotEmpty(updateRows)) {
            for (Map<String, Object> row : updateRows) {
                Map<String, Object> bizKeyMap = JSONObject.parseObject(String.valueOf(row.get(CategoryCommonConstants.BIZ_KEY)), new TypeReference<HashMap<String, Object>>() {
                });
                row.remove(CategoryCommonConstants.BIZ_KEY);
                row.keySet().removeIf(bizKeyMap::containsKey);
                categoryMapper.updateDataDynamic(tableName, bizKeyMap, row);
            }
        }
    }

    private void insertSyncData(String tableName, List<Map<String, Object>> addRows) {
        if (CollectionUtil.isNotEmpty(addRows)) {
            List<List<Map<String, Object>>> partition = Lists.partition(addRows, 1000);
            for (List<Map<String, Object>> listRow : partition) {
                categoryMapper.insertDataDynamic(tableName, listRow.get(0), listRow);
            }
        }
    }

    private void initUpdateRowInfo(List<Map<String, Object>> updateRows, List<Map<String, Object>> addRows, CategoryCodeExtraRespDTO categoryCodeExtraInfo) {
        if (CollectionUtil.isNotEmpty(updateRows)) {
            List<Map<String, Object>> newVersionRows = new ArrayList<>();
            Integer storeType = categoryCodeExtraInfo.getStoreType();
            Integer isVersionControl = categoryCodeExtraInfo.getIsVersionControl();
            String version = storeType == YesOrNoStatusEnum.YES.getCode() ? CategoryCommonConstants.VERSION : CategoryCommonConstants.DATA_VERSION;
            String isNewVersion = storeType == YesOrNoStatusEnum.YES.getCode() ? CategoryCommonConstants.NEW_VERSION : CategoryCommonConstants.NEW_VERSION;
            List<String> include = Arrays.asList(CategoryCommonConstants.VERSION, CategoryCommonConstants.DATA_VERSION, CategoryCommonConstants.NEW_VERSION,
                    CategoryCommonConstants.IS_NEW_VERSION, CategoryCommonConstants.CATEGORY_VERSION, CategoryCommonConstants.BIZ_KEY);
            for (Map<String, Object> updateRow : updateRows) {
                if (isVersionControl == YesOrNoStatusEnum.YES.getCode()) {
                    // 新版本 新增插入
                    HashMap<String, Object> newVersionRow = new HashMap<>(updateRow);
                    newVersionRow.computeIfPresent(version, (key, value) -> Long.parseLong(value.toString()) + 1);
                    newVersionRow.put(isNewVersion, YesOrNoStatusEnum.YES.getCode());
                    newVersionRows.add(newVersionRow);
                    // 旧版本要remove掉除版本信息之外的内容进行更新
                    if ( storeType == YesOrNoStatusEnum.YES.getCode()) {
                        updateRow.keySet().removeIf(key -> !include.contains(key));
                    }
                    updateRow.put(isNewVersion, YesOrNoStatusEnum.NO.getCode());
                }
            }
            addExtraInfo(newVersionRows, categoryCodeExtraInfo);
            if (addRows == null) {
                addRows = new ArrayList<>();
            }
            addRows.addAll(newVersionRows);

        }
    }

    private void initAddRowInfo(List<Map<String, Object>> addRows, CategoryCodeExtraRespDTO categoryCodeExtraInfo) {
        if (CollectionUtil.isNotEmpty(addRows)) {
            for (Map<String, Object> addRow : addRows) {
                addRow.putIfAbsent(CategoryCommonConstants.CREATE_DATE, addRow.get(CategoryCommonConstants.UPDATE_DATE));
                addRow.remove(CategoryCommonConstants.BIZ_KEY);
            }
        }
    }

    private void addExtraInfo(List<Map<String, Object>> addRows, CategoryCodeExtraRespDTO categoryCodeExtraInfo) {
        if (CollectionUtil.isNotEmpty(addRows)) {
            Long categoryVersion = categoryCodeExtraInfo.getCategoryVersion();
            String categoryCode = categoryCodeExtraInfo.getCategoryCode();
            Integer storeType = categoryCodeExtraInfo.getStoreType();
            Integer isVersionControl = categoryCodeExtraInfo.getIsVersionControl();
            String version = storeType == YesOrNoStatusEnum.YES.getCode() ? CategoryCommonConstants.VERSION : CategoryCommonConstants.DATA_VERSION;
            String isNewVersion = storeType == YesOrNoStatusEnum.YES.getCode() ? CategoryCommonConstants.NEW_VERSION : CategoryCommonConstants.NEW_VERSION;
            for (Map<String, Object> addRow : addRows) {
                if (isVersionControl == YesOrNoStatusEnum.YES.code) {
                    addRow.put(CategoryCommonConstants.CATEGORY_CODE, categoryCode);
                    addRow.put(CategoryCommonConstants.CATEGORY_VERSION, categoryVersion);
                    addRow.putIfAbsent(CategoryCommonConstants.VERSION, 1L);
                    addRow.put(isNewVersion, YesOrNoStatusEnum.YES.getCode());
                }
                addRow.put(CategoryCommonConstants.CREATOR, 1);
                addRow.put(CategoryCommonConstants.UPDATER, 1);
            }
        }
    }


}
