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

import cn.axzo.framework.jackson.utility.JSON;
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam;
import cn.axzo.im.center.common.enums.AppTypeEnum;
import cn.axzo.im.center.common.enums.YesOrNo;
import cn.axzo.maokai.api.util.Ref;
import cn.axzo.msg.center.common.utils.BizAssertions;
import cn.axzo.msg.center.dal.CardIdempotentDao;
import cn.axzo.msg.center.domain.entity.Card;
import cn.axzo.msg.center.domain.entity.CardIdempotent;
import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig;
import cn.axzo.msg.center.message.domain.dto.TemplateModelV3;
import cn.axzo.msg.center.message.domain.vo.GeneralMessagePushVO;
import cn.axzo.msg.center.message.service.card.domain.CardGroup;
import cn.axzo.msg.center.message.service.card.domain.CardSendModel;
import cn.axzo.msg.center.message.service.card.exception.CardIdempotentException;
import cn.axzo.msg.center.message.service.impl.v3.ModelV3Parser;
import cn.axzo.msg.center.message.service.impl.v3.ModelV3Service;
import cn.axzo.msg.center.message.service.impl.v3.UrlParser;
import cn.axzo.msg.center.nimpush.NimPushService;
import cn.axzo.msg.center.nimpush.device.PushDeviceSnapshots;
import cn.axzo.msg.center.push.PushData;
import cn.axzo.msg.center.service.domain.card.AppVersionConfig;
import cn.axzo.msg.center.service.dto.PeerPerson;
import cn.axzo.msg.center.service.enums.MessageCategoryEnum;
import cn.axzo.msg.center.service.enums.MessageChannel;
import cn.axzo.msg.center.service.pending.request.CardContent;
import cn.axzo.msg.center.service.pending.request.CardSendRequest;
import cn.axzo.msg.center.service.pending.response.CardInfo;
import cn.axzo.msg.center.service.pending.response.v3.ParsedModel3Visitor;
import cn.axzo.msg.center.service.pending.response.v3.ParsedModelV3Walker;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedModelV3;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedTemplateV3;
import cn.axzo.msg.center.service.util.IdBuilder;
import cn.axzo.msg.center.utils.UUIDUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;

/**
 * @author yanglin
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CardSupport {

    public static final String BIZ_ID_PREFIX = "msg-center";

    private final ModelV3Service modelV3Service;
    private final ModelV3Parser modelV3Parser;
    private final CardParser cardParser;
    private final NimPushService nimPushService;
    private final CardIdempotentDao cardIdempotentDao;
    private final CardProps cardProps;
    private final PendingMessageBizConfig cfg;

    public static String getBizIdPrefix(String templateCode) {
        return IdBuilder.builder()
                .append(BIZ_ID_PREFIX)
                .append(templateCode)
                .build();
    }

    void ensureCardIdempotent(CardSendRequest request) {
        if (StringUtils.isBlank(request.getIdempotentCode()))
            return;
        if (!cardProps.isEnableCardIdempotent()
                || cardProps.isIdempotentFree(request.getTemplateCode()))
            return;
        CardIdempotent idempotent = new CardIdempotent();
        idempotent.setAppCode(request.getAppCode());
        idempotent.setTemplateCode(request.getTemplateCode());
        idempotent.setIdempotentCode(request.getIdempotentCode());
        try {
            cardIdempotentDao.save(idempotent);
        } catch (DuplicateKeyException e) {
            log.warn("重复创建卡片, request={}", request);
            throw new CardIdempotentException(403, String.format("重复创建卡片: %s", idempotent));
        }
    }

    void deleteCardIdempotent(CardSendRequest request) {
        cardIdempotentDao.deleteIdempotent(request.getAppCode(),
                request.getTemplateCode(), request.getIdempotentCode());
    }

    void buildCardsForSend(CardSendModel sendModel) {
        GeneralMessagePushVO cardContent = cardParser.parseCardContent(
                sendModel.getCardTemplate(), sendModel.getRequest());
        sendModel.setCardContent(cardContent);
        Supplier<Card> cardFactory = () -> {
            Card card = new Card();
            sendModel.addCard(card);
            card.setBatchNo(sendModel.getRequestContext().getBatchNo());
            card.setAppCode(sendModel.getRequest().getAppCode());
            card.setImTaskId(0L);
            card.setBizMessageId("");
            card.setIdentityCode(UUIDUtil.uuidString());
            card.setBizCode(sendModel.getRequest().determineBizCode());
            card.setSubBizCode(sendModel.getRequest().determineSubBizCode());
            card.setTemplateCode(sendModel.getRequest().getTemplateCode());
            card.setBizState(sendModel.getRequest().getStateInfo().getBizState());
            card.setCardState(sendModel.getRequest().getStateInfo().getCardState());
            card.setTitle(sendModel.getTemplate().getTitle());
            card.setContent(sendModel.getTemplate().getContent());
            card.setCardContent(cardContent);
            card.setBizParam(sendModel.getRequest().getBizParam());
            card.setRouterParam(sendModel.getRequest().getRouterParam());
            card.setSenderAppType(AppTypeEnum.SYSTEM);
            card.setIsSenderRobot(YesOrNo.YES);
            card.setSenderPersonId(sendModel.getRequest().getSender().getPersonIdOrDefault());
            card.setSenderOuId(sendModel.getRequest().getSender().getOuIdOrDefault());
            card.setSenderWorkspaceId(sendModel.getRequest().getSender().getWorkspaceIdOrDefault());
            card.setSubtitle(sendModel.getRequest().getSubtitle());
            card.setButtonStates(Collections.emptyList());
            card.setUpdatable(sendModel.getCardTemplate().isUpdatable() ? YesOrNo.YES : YesOrNo.NO);
            return card;
        };
        for (PeerPerson person : sendModel.getRequest().receiversOrEmpty()) {
            for (AppTypeEnum appType : sendModel.getCardTemplate().ensureAppTypesPresent()) {
                Card card = cardFactory.get();
                card.setReceiverAppType(appType);
                card.setReceiverPersonId(person.getPersonIdOrDefault());
                card.setReceiverOuId(person.getOuIdOrDefault());
                card.setReceiverWorkspaceId(person.getWorkspaceIdOrDefault());
                card.setImReceiveAccount("");
            }
        }
        for (String imAccount : sendModel.getRequest().imReceiveAccountsOrEmpty()) {
            Card card = cardFactory.get();
            card.setReceiverAppType(AppTypeEnum.NONE);
            card.setReceiverPersonId(-1L);
            card.setReceiverOuId(-1L);
            card.setReceiverWorkspaceId(-1L);
            card.setImReceiveAccount(imAccount);
        }
    }

    SendTemplateMessageParam buildImSendRequest(CardSendModel sendModel,
                                                CardGroup group,
                                                PushDeviceSnapshots deviceSnapshots) {
        ParsedTemplateV3 template = sendModel.getTemplate();
        Supplier<JSONObject> extBuilder = () -> {
            JSONObject ext = new JSONObject();
            ext.put("minAppVersion", determineMinAppVersion(
                    sendModel.getParsedModel(), group.getGroupKey().getAppType()));
            if (group.getGroupKey().getOuId() != null)
                ext.put("ouId", String.valueOf(group.getGroupKey().getOuId()));
            if (group.getGroupKey().getWorkspaceId() != null)
                ext.put("workspaceId", String.valueOf(group.getGroupKey().getWorkspaceId()));
            return ext;
        };
        PushData pushData = template.parsePushData();
        SendTemplateMessageParam imRequest = new SendTemplateMessageParam();
        PersonAccountAttribute sender = new PersonAccountAttribute();
        sender.setPersonId(sendModel.getRequest().getSender().getPersonIdOrDefault() + "");
        sender.setOuId(sendModel.getRequest().getSender().getOuIdOrDefault());
        sender.setWorkspaceId(sendModel.getRequest().getSender().getWorkspaceIdOrDefault());
        sender.setAppType(sendModel.getRequest().getImSenderAccountAppType());
        imRequest.setSender(sender);
        imRequest.setMsgHeader(template.getTitle());
        imRequest.setMsgContent(template.getContent());
        imRequest.setMsgTemplateId(template.getCode());
        imRequest.setSendPriority(template.determineImSendPriority());
        imRequest.setMsgTemplateContent(JSON.toJSONString(sendModel.getCardContent()));
        String bizCode = sendModel.getRequest().determineBizCode();
        String subBizCode = sendModel.getRequest().determineSubBizCode();
        imRequest.setBizId(IdBuilder.builder()
                .append(getBizIdPrefix(template.getCode()))
                .append(bizCode)
                .append(subBizCode)
                .build());
        imRequest.setReceivePersons(group.buildReceiverAccounts());
        imRequest.setImReceiveAccounts(group.collectImReceiveAccounts());
        imRequest.setExt(extBuilder.get());
        imRequest.setUpdatable(sendModel.getCardTemplate().isUpdatable());
        if (pushData.determinePushable(log, template.getCode())) {
            imRequest.setPushContent(nimPushService.buildPushContent(sendModel.getParsedModel(), false));
            imRequest.setExcludePushPayloads(group.buildNimPushExcludes(deviceSnapshots));
            if (StringUtils.isNotBlank(pushData.getVoiceFile()))
                imRequest.getExt().put("sound", pushData.getVoiceFile());
        }
        return imRequest;
    }

    private String determineMinAppVersion(ParsedModelV3 model,
                                          AppTypeEnum appType) {
        Ref<String> ref = Ref.create("");
        ParsedModelV3Walker.walkDown(model, new ParsedModel3Visitor() {
            @Override
            public void visitAppVersionConfig(AppVersionConfig cfg) {
                if (cfg.getAppType() == appType)
                    ref.set(cfg.getMinVersion());
            }
        });
        String minVersion = ref.get();
        if (StringUtils.isBlank(minVersion)
                && model.getTemplate().getMsgCategory() == MessageCategoryEnum.APPROVAL_PENDING_MESSAGE) {
            return appType == AppTypeEnum.CMP
                    ? cfg.getWorkflowIMChannelAppMinVersionCmp()
                    : cfg.getWorkflowIMChannelAppMinVersionCm();
        }
        return minVersion;
    }

    TemplateModelV3 ensureImChannelPresent(String templateCode) {
        TemplateModelV3 templateModel = modelV3Service.findEnabledByCode(templateCode).orElse(null);
        BizAssertions.assertNotNull(templateModel, "模板不存在或已下线 {} ", templateCode);
        //noinspection DataFlowIssue
        List<MessageChannel> channels = templateModel.getTemplate().determineChannels();
        BizAssertions.assertTrue(channels.contains(MessageChannel.IM),
                "模版未配置IM通道 {} ", templateModel.getTemplateCode());
        return templateModel;
    }

    CardTemplate parseCardTemplate(TemplateModelV3 templateModel, CardContent card) {
        ParsedModelV3 parsedModelV3 = modelV3Parser.parseModel(
                templateModel, card.getBizParam(), new UrlParser(card.getRouterParam()));
        return CardTemplate.wrap(parsedModelV3);
    }

    CardInfo convertCardInfo(Card card) {
        CardInfo info = new CardInfo();
        info.setId(card.getId());
        info.setIdentityCode(card.getIdentityCode());
        info.setBizMessageId(card.getBizMessageId());
        info.setReceiverPersonId(card.getReceiverPersonId());
        info.setReceiverOuId(card.getReceiverOuId());
        info.setReceiverWorkspaceId(card.getReceiverWorkspaceId());
        info.setReceiverAppType(card.getReceiverAppType());
        info.setBizState(card.getBizState());
        info.setImTaskId(card.getImTaskId());
        info.setTemplateCode(card.getTemplateCode());
        return info;
    }
}