package cn.axzo.msg.center.message.service.impl;

import cn.axzo.apollo.core.web.Result;
import cn.axzo.apollo.workspace.api.workspace.OrganizationalNodePractitionerWideApi;
import cn.axzo.apollo.workspace.api.workspace.req.OrganizationalNodePractitionerWideReqParams;
import cn.axzo.apollo.workspace.api.workspace.res.OrganizationalNodePractitionerWideDto;
import cn.axzo.framework.core.util.MapUtil;
import cn.axzo.msg.center.api.enums.MsgTypeEnum;
import cn.axzo.msg.center.common.exception.ServiceException;
import cn.axzo.msg.center.dal.MessageRecordDao;
import cn.axzo.msg.center.dal.PendingMessageRecordDao;
import cn.axzo.msg.center.domain.entity.MessageRecord;
import cn.axzo.msg.center.domain.entity.PendingMessageRecord;
import cn.axzo.msg.center.domain.enums.YesNoEnum;
import cn.axzo.msg.center.message.service.PendingMessageDataInitService;
import cn.axzo.msg.center.service.dto.IdentityDTO;
import cn.axzo.msg.center.service.dto.PersonDTO;
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
import cn.axzo.msg.center.service.enums.IdentityTypeEnum;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
import cn.axzo.msg.center.utils.DateFormatUtil;
import cn.axzo.msg.center.utils.PersonIdentityUtil;
import cn.axzo.msg.center.utils.UUIDUtil;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import jodd.util.concurrent.ThreadFactoryBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author cold_blade
 * @version 1.0
 * @date 2023/11/17
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PendingMessageDataInitServiceImpl implements PendingMessageDataInitService {

    private final ThreadFactory asyncSendMsgThreadFactory = ThreadFactoryBuilder.create()
            .setDaemon(true).setNameFormat("ASYNC_TRANSFORM_OLD_DATA_%d").get();
    private final ExecutorService asyncSendMsgExecutorService = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1024), asyncSendMsgThreadFactory);

    private final Environment environment;
    private final OrganizationalNodePractitionerWideApi organizationalNodePractitionerWideApi;

    private static final PersonDTO SYSTEM = PersonDTO.builder()
            .id(0L)
            .name("系统")
            .identity(IdentityDTO.builder().id(0L).type(IdentityTypeEnum.NOT_SUPPORT).build())
            .build();

    private final MessageRecordDao messageRecordDao;
    private final PendingMessageRecordDao pendingMessageRecordDao;

    @Override
    public void transformPendingMessageRecord(Integer diffDays, Long minRecordId) {
        CompletableFuture.runAsync(() -> doTransformPendingMessageRecord(diffDays, minRecordId),
                asyncSendMsgExecutorService);
    }

    private void doTransformPendingMessageRecord(Integer diffDays, Long minRecordId) {
        LocalDateTime queryFrom = LocalDateTime.now().minusDays(diffDays);
        Long scrollId = Long.MAX_VALUE;
        List<MessageRecord> records = messageRecordDao.lambdaQuery()
                .eq(MessageRecord::getType, MsgTypeEnum.PENDING_MESSAGE)
                .eq(MessageRecord::getIsDelete, YesNoEnum.NO.getCode())
                .ge(MessageRecord::getCreateAt, DateFormatUtil.toDate(queryFrom))
                .lt(MessageRecord::getId, scrollId)
                .gt(MessageRecord::getId, minRecordId)
                .orderByDesc(MessageRecord::getId)
                .last("LIMIT 2000")
                .list();
        final Map<String, Long> ouIdMap = Maps.newHashMap();
        while (!records.isEmpty()) {
            String profile = environment.getProperty("spring.profiles.active");
            if ("dev".equals(profile)) {
                log.warn("系统环境[" + profile + "],不支持该接口操作");
            } else {
                batchSavePendingMessage(records, ouIdMap);
            }
            scrollId = records.stream().min(Comparator.comparing(MessageRecord::getId))
                    .map(MessageRecord::getId).orElse(0L);
            records = messageRecordDao.lambdaQuery()
                    .eq(MessageRecord::getType, MsgTypeEnum.PENDING_MESSAGE)
                    .eq(MessageRecord::getIsDelete, YesNoEnum.NO.getCode())
                    .ge(MessageRecord::getCreateAt, DateFormatUtil.toDate(queryFrom))
                    .lt(MessageRecord::getId, scrollId)
                    .gt(MessageRecord::getId, minRecordId)
                    .orderByDesc(MessageRecord::getId)
                    .last("LIMIT 2000")
                    .list();
        }
    }

    private void batchSavePendingMessage(List<MessageRecord> records, Map<String, Long> ouIdMap) {
        if (CollectionUtils.isEmpty(records)) {
            return;
        }
        List<PendingMessageRecord> pendingMessageRecords = records.stream()
                .map(this::convert)
                .filter(e -> StringUtils.isNotBlank(e.getTemplateCode()))
                .collect(Collectors.toList());
        log.info("[cold_blade] transform rows:[{}]", pendingMessageRecords.size());
        Map<String, Long> executorIdWorkspceIdMap = pendingMessageRecords.stream()
                .filter(e -> Objects.equals(e.getExecutorType(), IdentityTypeEnum.PRACTITIONER))
                .filter(e -> filterWorkspaceId(e.getOrgId()))
                .filter(e -> !ouIdMap.containsKey(getKey(e)))
                .collect(Collectors.toMap(this::getKey, PendingMessageRecord::getExecutorId, (cur, next) -> next));
        Map<String, Long> map = fetchOuId(executorIdWorkspceIdMap);
        if (MapUtil.isNotEmpty(map)) {
            ouIdMap.putAll(map);
        }
        // 设置ouId
        pendingMessageRecords.stream()
                .filter(e -> Objects.equals(e.getExecutorType(), IdentityTypeEnum.PRACTITIONER))
                .filter(e -> ouIdMap.containsKey(getKey(e)))
                .forEach(e -> e.setOuId(ouIdMap.get(getKey(e))));
        //批量插入数据库
        pendingMessageRecordDao.saveOrUpdateBatch(pendingMessageRecords);
    }

    private Map<String, Long> fetchOuId(Map<String, Long> executorIdWorkspceIdMap) {
        if (MapUtil.isEmpty(executorIdWorkspceIdMap)) {
            return Collections.emptyMap();
        }
        OrganizationalNodePractitionerWideReqParams param = new OrganizationalNodePractitionerWideReqParams();
        param.setIdentityIds(Lists.newArrayList(executorIdWorkspceIdMap.values()));
        Result<List<OrganizationalNodePractitionerWideDto>> result = organizationalNodePractitionerWideApi.getList(param);
        if (CollectionUtils.isEmpty(result.getData())) {
            log.info("errCode:[{}], msg:{}", result.getCode(), result.getMsg());
            return Collections.emptyMap();
        }

        Map<String, List<OrganizationalNodePractitionerWideDto>> groupingBy = result.getData().stream()
                .filter(e -> executorIdWorkspceIdMap.containsKey(getKey(e)))
                .collect(Collectors.groupingBy(this::getKey));
        return groupingBy.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0).getOrganizationalUnitId()));
    }

    private String getKey(PendingMessageRecord record) {
        return record.getExecutorId() + "_" + record.getOrgId();
    }

    private String getKey(OrganizationalNodePractitionerWideDto dto) {
        return dto.getIdentityId() + "_" + dto.getWorkspaceId();
    }

    private PendingMessageRecord convert(MessageRecord record) {
        PendingMessageRecord pendingMsgRecord = new PendingMessageRecord();
        pendingMsgRecord.setPromoterPersonId(SYSTEM.getId());
        pendingMsgRecord.setPromoterId(SYSTEM.getIdentity().getId());
        pendingMsgRecord.setPromoterType(SYSTEM.getIdentity().getType());
        pendingMsgRecord.setPromoterName(SYSTEM.getName());
        pendingMsgRecord.setExecutorPersonId(record.getPersonId());
        pendingMsgRecord.setExecutorId(record.getToId());
        pendingMsgRecord.setExecutorType(PersonIdentityUtil.toIdentityType(record.getReceiveType()));
        pendingMsgRecord.setTemplateCode(getTemplateCode(record.getRelationId()));
        pendingMsgRecord.setTitle(record.getTitle());
        pendingMsgRecord.setContent(record.getContent());
        pendingMsgRecord.setOrgType(OrganizationTypeEnum.valueOf(record.getTerminalType().name()));
        pendingMsgRecord.setOrgId(record.getTerminalId());
        pendingMsgRecord.setOrgName(record.getTerminalName());
        pendingMsgRecord.setState(PendingMessageStateEnum.codeOf(record.getState().getCode()));
        if (Objects.nonNull(record.getBizId()) && record.getBizId() > 0L) {
            pendingMsgRecord.setBizCode(String.valueOf(record.getBizId()));
        }
        pendingMsgRecord.setRouterParams(record.getRouterParams());
        pendingMsgRecord.setBizExtParam(record.getExtra());
        pendingMsgRecord.setOuId(record.getTenantId());
        pendingMsgRecord.setBizCategory(BizCategoryEnum.OTHER);
        pendingMsgRecord.setFailCause(String.valueOf(record.getId()));
        pendingMsgRecord.setIdentityCode(UUIDUtil.uuidString());
        pendingMsgRecord.setCreateAt(record.getCreateAt());
        pendingMsgRecord.setUpdateAt(record.getUpdateAt());
        return pendingMsgRecord;
    }

    private String getTemplateCode(Long relationId) {
        // 需要通过映射表获取 https://alidocs.dingtalk.com/i/nodes/YndMj49yWj7q1dB9h793q5dKV3pmz5aA?iframeQuery=utm_source%3Dportal%26utm_medium%3Dportal_recent
        String profile = environment.getProperty("spring.profiles.active");
        if ("dev".equals(profile)) {
            throw new ServiceException("系统环境[" + profile + "],不支持该接口操作");
        }
        return msgTemplateMap.get(Integer.parseInt(String.valueOf(relationId)));
    }

    private boolean filterWorkspaceId(Long workspaceId) {
        return Objects.nonNull(workspaceId)
                && workspaceId > 0
                && EXCLUDE_WORKSPACE_IDS.stream().map(Long::valueOf).noneMatch(e -> e.equals(workspaceId));
    }

    /**
     * 原模板ID与test环境新模板ID映射表
     */
    private static final Map<Integer, String> msgTemplateMap = Maps.newHashMap();

    static {
        msgTemplateMap.put(138, "b8115314233d478ca70e9f50ca0b0dc3");
        msgTemplateMap.put(193, "a97ff39e4d39484ab7fccc55d50ea714");
        msgTemplateMap.put(147, "fc2e584aa5b54fb382ab1894a89bd929");
        msgTemplateMap.put(148, "f40eef2ee88e4a80a699389d009a1561");
        msgTemplateMap.put(360, "e67a037581f948ec9343e2217b828034");
        msgTemplateMap.put(152, "d1d4793c84e14d20ae1cbc399338efa3");
        msgTemplateMap.put(210, "c3a4579f7f004af3bdeabe8f79ffcf1b");
        msgTemplateMap.put(151, "8e20637ab7b242c1a1e5590d8feb50fb");
        msgTemplateMap.put(197, "9ae74eb4e3c44dcbbabc77791b3dbf2d");
        msgTemplateMap.put(221, "5791020e815741e4876328c98aa3bd34");
        msgTemplateMap.put(232, "fdf662e201c945dba040fc54db50a702");
        msgTemplateMap.put(402, "52ae3e8ec48242e485e9389202e102ce");
        msgTemplateMap.put(403, "e5571b2bec7c433d997f70856ecd2929");
        msgTemplateMap.put(224, "367ba552bb374049a73e737ac3b8b08c");
        msgTemplateMap.put(447, "75047e6339484e81bcce244d56fb2363");
        msgTemplateMap.put(570, "472b5a4d89a646eaa1ba862246817366");
        msgTemplateMap.put(557, "e6a3d337a943454685a88dcf7e9f7879");
        msgTemplateMap.put(558, "c8a38bec34774bb39f6419e327136eac");
        msgTemplateMap.put(525, "9b372033b7f146eb9ef8c24cf203fb1f");
        msgTemplateMap.put(526, "9a114a56fa00482a9c4db9f495caa259");
        msgTemplateMap.put(546, "9fef5828b8574bc7b077dac9162817c7");
        msgTemplateMap.put(564, "fc35f169515e43e39d863b34e5226e5e");
    }

    private static final ImmutableSet<Integer> EXCLUDE_WORKSPACE_IDS = ImmutableSet.of(3, 5, 16, 17, 19, 21, 50, 75, 105,
            106, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 119, 120, 122, 123, 124, 125, 126, 131, 132, 140, 141,
            142, 148, 153, 197, 216, 236, 237, 238, 248, 275, 213, 214, 226, 239, 240, 246, 260, 266, 267, 3398);
}
