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

import cn.ey.biz.common.exception.util.ServiceExceptionUtil;
import cn.ey.biz.common.util.json.JsonUtils;
import cn.ey.biz.framework.excel.core.util.ExcelUtils;
import cn.ey.biz.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.ey.biz.module.system.api.delegation.dto.UserDelegationUnitDTO;
import cn.ey.biz.module.system.controller.admin.delegation.vo.*;
import cn.ey.biz.module.system.convert.delegation.DelegationUnitConvert;
import cn.ey.biz.module.system.dal.dataobject.delegation.UserDelegationUnitDO;
import cn.ey.biz.module.system.dal.mapper.delegation.UserDelegationUnitMapper;
import cn.ey.biz.module.system.enums.ErrorCodeConstants;
import cn.ey.biz.module.system.enums.category.CategoryCodeEnum;
import cn.ey.biz.module.system.enums.delegation.DelegationTypeEnum;
import cn.ey.biz.module.system.service.category.v2.CategoryDataV2Service;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 代理人代理单元 Service 实现类
 *
 * @author Jian.Yang
 * @date 2023-02-23
 */
@Service
public class UserDelegationUnitServiceImpl extends ServiceImpl<UserDelegationUnitMapper, UserDelegationUnitDO> implements UserDelegationUnitService {

    @Resource
    private UserDelegationUnitMapper userDelegationUnitMapper;

    @Resource
    private CategoryDataV2Service categoryDataService;

    @Override
    public List<UserDelegationUnitVO<UserDelegationUnitRespVO>> getUserDelegationUnit(UserDelegationUnitReqVO reqVO) {
        List<UserDelegationUnitVO<UserDelegationUnitRespVO>> result = null;
        List<UserDelegationUnitDO> userDelegationUnitEntities = userDelegationUnitMapper.selectList(getQueryWrapper(reqVO));
        if (CollUtil.isNotEmpty(userDelegationUnitEntities)) {
            result = new ArrayList<>();
            Map<Long, List<UserDelegationUnitDO>> collect = userDelegationUnitEntities.stream().collect(Collectors.groupingBy(UserDelegationUnitDO::getUserId));
            for (Map.Entry<Long, List<UserDelegationUnitDO>> entry : collect.entrySet()) {
                UserDelegationUnitDO commonUserDelegationUnit = entry.getValue().get(0);
                UserDelegationUnitVO<UserDelegationUnitRespVO> userDelegationUnitVO = new UserDelegationUnitVO<>();
                userDelegationUnitVO.setAppId(commonUserDelegationUnit.getAppId())
                        .setUserInfo(new UserDelegationPersonVO()
                                .setUserId(commonUserDelegationUnit.getUserId())
                                .setUserName(commonUserDelegationUnit.getUserName())
                                .setEmail(commonUserDelegationUnit.getEmail())
                                .setGpn(commonUserDelegationUnit.getGpn()))
                        .setGroups(DelegationUnitConvert.INSTANCE.convertList(entry.getValue()));
                result.add(userDelegationUnitVO);
            }
        }
        return result;
    }

    private LambdaQueryWrapperX<UserDelegationUnitDO> getQueryWrapper(UserDelegationUnitReqVO reqVO) {
        return new LambdaQueryWrapperX<UserDelegationUnitDO>()
                .eq(UserDelegationUnitDO::getAppId, reqVO.getAppId())
                .eqIfPresent(UserDelegationUnitDO::getUserId, reqVO.getUserId())
                .likeIfPresent(UserDelegationUnitDO::getUserName, reqVO.getUserName())
                .likeIfPresent(UserDelegationUnitDO::getGpn, reqVO.getGpn())
                .likeIfPresent(UserDelegationUnitDO::getEmail, reqVO.getEmail())
                .eq(StringUtils.isNotEmpty(reqVO.getType()), UserDelegationUnitDO::getType, reqVO.getType());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveUserDelegationUnitList(List<UserDelegationUnitVO<UserDelegationUnitSaveReqVO>> saveReqVO) {
        List<UserDelegationUnitDO> entities = new ArrayList<>();
        for (UserDelegationUnitVO<UserDelegationUnitSaveReqVO> userGroupsItem : saveReqVO) {
            checkDelegationUnitData(userGroupsItem);
            deleteUserHis(userGroupsItem);
            initDelegationUnitData(userGroupsItem);
            entities.addAll(DelegationUnitConvert.INSTANCE.convertList2DO(userGroupsItem.getGroups()));
        }
        userDelegationUnitMapper.insertBatch(entities);
    }

    private void deleteUserHis(UserDelegationUnitVO<UserDelegationUnitSaveReqVO> userGroupsItem) {
        Set<Long> deleteUserIds = new HashSet<>();
        // 当前授权用户id
        deleteUserIds.add(userGroupsItem.getUserInfo().getUserId());
        // 更改的有效数据中的用户id
        List<Long> ids = userGroupsItem.getGroups().stream().map(UserDelegationUnitSaveReqVO::getId).filter(Objects::nonNull).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(ids)) {
            List<Long> beforeUserIds = userDelegationUnitMapper.selectBatchIds(ids).stream().map(UserDelegationUnitDO::getUserId).distinct().collect(Collectors.toList());
            deleteUserIds.addAll(beforeUserIds);
        }
        userDelegationUnitMapper.delete(new LambdaQueryWrapperX<UserDelegationUnitDO>()
                .in(UserDelegationUnitDO::getUserId, deleteUserIds)
                .eq(UserDelegationUnitDO::getAppId, userGroupsItem.getAppId()));
    }

    private void initDelegationUnitData(UserDelegationUnitVO<UserDelegationUnitSaveReqVO> userGroupsItem) {
        // fill common
        userGroupsItem.getGroups().forEach(group -> group.setId(null).setUserId(userGroupsItem.getUserInfo().getUserId())
                .setUserName(userGroupsItem.getUserInfo().getUserName())
                .setEmail(userGroupsItem.getUserInfo().getEmail())
                .setGpn(userGroupsItem.getUserInfo().getGpn())
                .setAppId(userGroupsItem.getAppId()));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteUserDelegationUnitList(List<UserDelegationUnitVO<UserDelegationUnitSaveReqVO>> deleteReqVO) {
        if (CollUtil.isEmpty(deleteReqVO)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.DELEGATION_ARGUMENTS_NOT_NULL);
        }
        List<Long> userIds = deleteReqVO.stream().map(x -> x.getUserInfo().getUserId()).collect(Collectors.toList());
        userDelegationUnitMapper.delete(new LambdaUpdateWrapper<UserDelegationUnitDO>()
                .eq(UserDelegationUnitDO::getAppId, deleteReqVO.get(0).getAppId())
                .in(UserDelegationUnitDO::getUserId, userIds));
    }

    @Override
    public List<UserDelegationUnitDTO> getDelegationUnit(String app, String gpn, String type) {
        if (StringUtils.isEmpty(app) || StringUtils.isEmpty(gpn)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.DELEGATION_ARGUMENTS_NOT_NULL);
        }
        UserDelegationUnitReqVO reqParam = new UserDelegationUnitReqVO()
                .setAppId(app).setGpn(gpn).setType(type);
        return DelegationUnitConvert.INSTANCE.convertList2DTO(userDelegationUnitMapper.selectList(getQueryWrapper(reqParam)));
    }

    @Override
    public void export(HttpServletResponse response, UserDelegationUnitReqVO reqVO) throws IOException {
        List<UserDelegationUnitDO> userDelegationUnitEntities = userDelegationUnitMapper.selectList(getQueryWrapper(reqVO));
        List<UserDelegationUnitExcelVO> userDelegationUnitExcelVOList = DelegationUnitConvert.INSTANCE.convertList2ExcelVO(userDelegationUnitEntities);
        convertExcelVOList(userDelegationUnitExcelVOList);
        ExcelUtils.write(response, "DelegationUnit.xls", "delegationUnit", UserDelegationUnitExcelVO.class, userDelegationUnitExcelVOList);
    }

    private void convertExcelVOList(List<UserDelegationUnitExcelVO> userDelegationUnitExcelVOList) {
        if (CollUtil.isNotEmpty(userDelegationUnitExcelVOList)) {
            List<String> typeList = userDelegationUnitExcelVOList.stream().map(UserDelegationUnitExcelVO::getType).collect(Collectors.toList());
            Map<String, List<Map<String, Object>>> categoryValueList = getType2CategoryValueList(typeList);
            for (UserDelegationUnitExcelVO item : userDelegationUnitExcelVOList) {
                // mapping relation
                item.setRelation(analysisRelation(item.getType(), item.getRelation(), categoryValueList));
                // mapping type
                item.setType(DelegationTypeEnum.getByCode(item.getType()).getValue());
            }
        }
    }

    private String analysisRelation(String type, String relation, Map<String, List<Map<String, Object>>> categoryValueList) {
        // type relation 组成
        List<String[]> unitItems = JsonUtils.parseArray(relation, String[].class);
        Map<String, String[]> categoryCodeKeyColumnName = map2categoryCodeKeyColumnName(type);
        Map<String, Map<String, String>> dbData = new HashMap<>(16);
        for (Map.Entry<String, String[]> entry : categoryCodeKeyColumnName.entrySet()) {
            String columnCodeName = entry.getValue() != null ? entry.getValue()[0] : "";
            String columnValueName = entry.getValue() != null ? entry.getValue()[1] : "";
            List<Map<String, Object>> resultValueMapList = categoryValueList.get(entry.getKey());
            // 转成具体列和值 map
            Map<String, String> colData = new HashMap<>(16);
            resultValueMapList.forEach(row -> colData.put((String) row.get(columnCodeName), (String) row.get(columnValueName)));
            dbData.put(entry.getKey(), colData);
        }
        return covertGroupUnit(unitItems, dbData, categoryCodeKeyColumnName);
    }

    private String covertGroupUnit(List<String[]> unitItems, Map<String, Map<String, String>> dbData, Map<String, String[]> categoryCodeKeyColumnName) {
        StringBuilder sb = new StringBuilder();
        // 单元数组categoryCode构成
        String[] units = categoryCodeKeyColumnName.keySet().toArray(new String[0]);
        for (String[] unitItem : unitItems) {
            boolean bl = false;
            for (int index = 0; index < unitItem.length; index++) {
                // 按需按序获取数组中单元
                Map<String, String> colData = dbData.get(units[index]);
                if (colData != null && colData.get(unitItem[index]) != null) {
                    bl = true;
                    sb.append(colData.get(unitItem[index])).append(">");
                }
            }
            if (bl) {
                sb.deleteCharAt((sb.length() - 1));
                sb.append("\r\n");
            }
        }
        return sb.toString();
    }

    private Map<String, List<Map<String, Object>>> getType2CategoryValueList(List<String> typeList) {
        List<String> categoryCodeList = new ArrayList<>();
        typeList.forEach(type -> {
            Map<String, String[]> stringMap = map2categoryCodeKeyColumnName(type);
            categoryCodeList.addAll(stringMap.keySet());
        });
        return categoryDataService.listDefaultCategoryDataByCategoryCodes(categoryCodeList);
    }

    private Map<String, String[]> map2categoryCodeKeyColumnName(String type) {
        DelegationTypeEnum delegationType = DelegationTypeEnum.getByCode(type);
        Map<String, String[]> mapValue = new LinkedHashMap<>(16);
        switch (delegationType) {
            case MU:
                mapValue.put(CategoryCodeEnum.MANAGEMENT_UNIT.getCode(), new String[]{"muCode", "muName"});
                break;
            case OU:
                mapValue.put(CategoryCodeEnum.OPERATING_UNIT.getCode(), new String[]{"ouCode", "ouName"});
                break;
            case SMU:
                mapValue.put(CategoryCodeEnum.MANAGEMENT_REGION.getCode(), new String[]{"mrCode", "mrName"});
                mapValue.put(CategoryCodeEnum.SERVICE_LINE.getCode(), new String[]{"slCode", "slName"});
                mapValue.put(CategoryCodeEnum.SUB_SERVICE_LINE.getCode(), new String[]{"sslCode", "sslName"});
                mapValue.put(CategoryCodeEnum.COMPETENCY.getCode(), new String[]{"competencyCode", "competencyName"});
                mapValue.put(CategoryCodeEnum.SUB_MANAGEMENT_UNIT.getCode(), new String[]{"smuCode", "smuName"});
                break;
            case RANK:
                mapValue.put(CategoryCodeEnum.Rank.getCode(), new String[]{"rankCode", "rankName"});
                break;
            case BU:
                mapValue.put(CategoryCodeEnum.BUSINESS_UNIT.getCode(), new String[]{"buCode", "buName"});
                break;
            case GEO_REGION:
                mapValue.put(CategoryCodeEnum.GEO_REGION.getCode(), new String[]{"geoRegionCode", "geoRegionName"});
                break;
            default:
                return mapValue;
        }
        return mapValue;
    }

    private void checkDelegationUnitData(UserDelegationUnitVO<UserDelegationUnitSaveReqVO> userGroupsItem) {
        List<UserDelegationUnitSaveReqVO> groups = userGroupsItem.getGroups();
        for (UserDelegationUnitSaveReqVO group : groups) {
            if (DelegationTypeEnum.getByCode(group.getType()) == null) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.DELEGATION_UNIT_DATA_ERROR);
            }
        }
    }
}
