package cn.axzo.msg.center.inside.notices.service.impl.v3.msg;

import cn.axzo.framework.jackson.utility.JSON;
import cn.axzo.im.center.api.feign.MessageApi;
import cn.axzo.im.center.api.vo.ApiChannel;
import cn.axzo.im.center.api.vo.PersonAccountAttribute;
import cn.axzo.im.center.api.vo.req.ExcludePushPayload;
import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam;
import cn.axzo.im.center.common.enums.AppTypeEnum;
import cn.axzo.msg.center.api.enums.MsgStateV3Enum;
import cn.axzo.msg.center.api.request.v4.MessageSendBasicInfoV4;
import cn.axzo.msg.center.api.request.v4.MessageSendRequestV4;
import cn.axzo.msg.center.common.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.common.utils.BizAssertions;
import cn.axzo.msg.center.common.utils.PlaceholderResolver;
import cn.axzo.msg.center.domain.entity.MessageRecordV3;
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.CardSupport;
import cn.axzo.msg.center.message.service.impl.v3.ModelV3Parser;
import cn.axzo.msg.center.message.service.impl.v3.UrlParser;
import cn.axzo.msg.center.nimpush.NimPushService;
import cn.axzo.msg.center.nimpush.PushChannel;
import cn.axzo.msg.center.nimpush.PushPeer;
import cn.axzo.msg.center.nimpush.device.PushDeviceSnapshots;
import cn.axzo.msg.center.nimpush.payload.intent.Intent;
import cn.axzo.msg.center.push.PushData;
import cn.axzo.msg.center.service.dto.PersonV3DTO;
import cn.axzo.msg.center.service.enums.Channel;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedModelV3;
import cn.axzo.msg.center.utils.UUIDUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

/**
 * @author yanglin
 */
@Slf4j
@RequiredArgsConstructor
public class TemplateMessage {

    private final MessageSendRequestV4 req;
    private final String batchNo;
    @Getter
    private final Channel channel;
    @Getter
    private final TemplateModelV3 templateModel;
    private final ApplicationContext beanFactory;

    private List<MessageRecordV3> records;
    private String title;
    private String content;

    String getTemplateCode() {
        return templateModel.getTemplateCode();
    }

    Collection<Long> getMessageIds() {
        return getMessageRecords().stream()
                .map(MessageRecordV3::getId)
                .collect(toSet());
    }

    Collection<Long> getReceiverPersonIds() {
        return getMessageRecords().stream()
                .map(MessageRecordV3::getReceiverPersonId)
                .collect(toList());
    }

    List<MessageRecordV3> getMessageRecords() {
        if (records != null) {
            return records;
        }
        records = new ArrayList<>();
        MessageSendBasicInfoV4 sendBasicInfo = req.getSendBasicInfo();
        for (PersonV3DTO receiver : sendBasicInfo.distinctReceivers()) {
            PersonV3DTO sender = sendBasicInfo.getSender();
            MessageRecordV3 message = new MessageRecordV3();
            records.add(message);
            message.setBatchNo(batchNo);
            message.setIdentityCode(UUIDUtil.uuidString());
            message.setSenderPersonId(sender == null ? null : sender.getId());
            message.setSenderOuId(sendBasicInfo.getSenderOuId());
            message.setSenderWorkspaceId(sendBasicInfo.getSenderWorkspaceId());
            message.setReceiverPersonId(receiver.getId());
            message.setReceiverOuId(sendBasicInfo.determineReceiversOuId());
            message.setReceiverWorkspaceId(sendBasicInfo.determineReceiversWorkspaceId());
            message.setBizEventMappingCode(req.getEventMappingCode());
            message.setTemplateCode(getTemplateCode());
            message.setTitle(parseTitle());
            message.setContent(parseContent());
            OrganizationTypeEnum orgType = sendBasicInfo.getReceiversOrgType();
            if (orgType == null) {
                orgType = OrganizationTypeEnum.UNKNOWN;
            }
            message.setReceiverOrgType(orgType.stringCode());
            message.setSubtitle(sendBasicInfo.getSubtitle());
            message.setState(MsgStateV3Enum.UNSENT);
            message.setBizCode(sendBasicInfo.determineBizCode());
            message.setRouterParams(sendBasicInfo.determineRouterParams());
            message.setBizExtParams(sendBasicInfo.determineBizExtParams());
            message.setMsgExtInfo(getMsgExtInfo());
            message.setFailCause(null);
            message.setCreateAt(new Date());
            message.setUpdateAt(new Date());
            message.setIsDelete(TableIsDeleteEnum.NORMAL.value);
            // 这里不设置发送时间, 有发送结果的时候再设置, 比较准确
        }
        return records;
    }

    SendTemplateMessageParam buildImRequest(MessageTemplateParserV3 templateParser,
                                            PushDeviceSnapshots deviceSnapshots,
                                            AppTypeEnum appType) {
        MessageRecordV3 sample = getMessageRecords().get(0);
        GeneralMessagePushVO sendVo = templateParser.parse(sample, templateModel);

        MessageSendBasicInfoV4 sendBasicInfo = req.getSendBasicInfo();
        SendTemplateMessageParam imRequest = new SendTemplateMessageParam();
        imRequest.setBizId(String.format("%s:%s", CardSupport.getBizIdPrefix(getTemplateCode()), sendBasicInfo.determineBizCode()));
        imRequest.setSendPriority(templateModel.getTemplate().determineImSendPriority());
        imRequest.setMsgHeader(parseTitle());
        imRequest.setMsgContent(parseContent());
        imRequest.setMsgTemplateId(getTemplateCode());
        imRequest.setMsgTemplateContent(JSON.toJSONString(sendVo));
        imRequest.setExcludePushPayloads(new ArrayList<>());
        // 接收人
        ArrayList<PersonAccountAttribute> receivers = new ArrayList<>();
        Set<Long> cmUnique = new HashSet<>();
        Set<OuAndPerson> cmpUnique = new HashSet<>();

        PushData pushData = templateModel.getTemplate().parsePushData();
        boolean pushable = pushData.determinePushable(log, getTemplateCode());

        // 扩展信息
        for (PersonV3DTO receiver : sendBasicInfo.receivers()) {
            PersonV3DTO.ReceiveModel imReceiveModel = receiver.getImReceiveModel();
            Long ouId = imReceiveModel == null ? sendBasicInfo.determineReceiversOuId() : imReceiveModel.getOuId();
            if (appType == AppTypeEnum.CM && !cmUnique.add(receiver.getId())) {
                continue;
            }
            if (appType == AppTypeEnum.CMP && !cmpUnique.add(new OuAndPerson(ouId, receiver.getId()))) {
                continue;
            }

            boolean excludePayload = pushable &&
                    !deviceSnapshots.getDevice(receiver.getId())
                            .shouldPush(appType, PushChannel.NIM);
            if (excludePayload) {
                ExcludePushPayload excludePush = new ExcludePushPayload();
                excludePush.setPersonId(receiver.getId() + "");
                excludePush.setAppType(appType);
                imRequest.getExcludePushPayloads().add(excludePush);
            }

            PersonAccountAttribute receiverAccount = new PersonAccountAttribute();
            receiverAccount.setPersonId(receiver.getId() + "");
            receiverAccount.setOuId(ouId);
            receiverAccount.setAppType(appType);
            receivers.add(receiverAccount);
            if (appType == AppTypeEnum.CM)
                cmUnique.add(receiver.getId());
            else if (appType == AppTypeEnum.CMP)
                cmpUnique.add(new OuAndPerson(ouId, receiver.getId()));
        }
        imRequest.setReceivePersons(receivers);
        JSONObject ext = new JSONObject();
        ext.put("minAppVersion", templateModel.getTemplate().getMinAppVersion());
        if (sample.getReceiverWorkspaceId() != null) {
            ext.put("workspaceId", String.valueOf(sample.getReceiverWorkspaceId()));
        }
        if (sample.getReceiverOuId() != null) {
            ext.put("ouId", String.valueOf(sample.getReceiverOuId()));
        }
        imRequest.setExt(ext);
        if (pushable) {
            imRequest.setPayload(buildPayload(sample, appType));
            if (StringUtils.isNotBlank(pushData.getVoiceFile()))
                ext.put(Intent.INTENT_SOUND, pushData.getVoiceFile());
        }
        return imRequest;
    }

    private String buildPayload(MessageRecordV3 sample, AppTypeEnum appType) {
        ModelV3Parser modelV3Parser = beanFactory.getBean(ModelV3Parser.class);
        ParsedModelV3 parsedModelV3 = modelV3Parser.parseModel(templateModel,
                sample.getBizExtParams(), new UrlParser(sample.getRouterParams()));
        NimPushService nimPushService = beanFactory.getBean(NimPushService.class);
        MessageApi messageApi = beanFactory.getBean(MessageApi.class);
        String imSenderAccount = BizAssertions.assertResponse(
                messageApi.findTemplateRobotImAccount(getTemplateCode()));
        PushPeer peer = new PushPeer();
        peer.setSenderImAccount(imSenderAccount);
        peer.setOuId(sample.getReceiverOuId());
        peer.setWorkspaceId(sample.getReceiverWorkspaceId());
        peer.setAppType(appType);
        peer.setApiChannel(ApiChannel.COMMON_MESSAGE);
        return nimPushService.buildPayloadString(parsedModelV3, peer);
    }

    // ------------------------------- 辅助方法

    String parseTitle() {
        if (title == null) {
            title = PlaceholderResolver.tryResolve(
                    templateModel.getTemplate().getTitle(), req.getSendBasicInfo().getBizExtParams());
        }
        return title;
    }

    String parseContent() {
        if (content == null) {
            content = PlaceholderResolver.tryResolve(
                    templateModel.getTemplate().getContent(), req.getSendBasicInfo().getBizExtParams());
        }
        return content;
    }

    /**
     * 预留
     */
    JSONObject getMsgExtInfo() {
        return new JSONObject();
    }

    @Data
    private static class OuAndPerson {
        private final Long ouId;
        private final Long personId;
    }

    @Override
    public String toString() {
        HashMap<String, Object> values = new HashMap<>();
        values.put("batchNo", batchNo);
        values.put("bizEventMappingCode", req.getEventMappingCode());
        values.put("bizCode", req.getSendBasicInfo().getBizCode());
        values.put("templateCode", templateModel.getTemplate().getCode());
        values.put("messageIds", getMessageIds());
        values.put("receiverPersonIds", getReceiverPersonIds());
        return JSON.toJSONString(values);
    }
}