package com.wecloud.im.service.impl;

import io.geekidea.springbootplus.framework.shiro.signature.SignUtils;

import java.net.URI;
import java.util.Date;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import org.yaml.snakeyaml.util.UriEncoder;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.id.NanoId;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImConversation;
import com.wecloud.im.entity.ImConversationMembers;
import com.wecloud.im.entity.ImMessage;
import com.wecloud.im.param.ClientOnlineStatusChangeDto;
import com.wecloud.im.sdk.enums.ChatTypeEnum;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImCallbackService;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.service.ImConversationMembersService;
import com.wecloud.im.service.ImConversationService;
import com.wecloud.im.ws.enums.MsgTypeEnum;

/**
 * 回调服务实现类
 * @Author luozh
 * @Date 2022年04月22日 09:11
 * @Version 1.0
 */
@Service
public class ImCallbackServiceImpl implements ImCallbackService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ImApplicationService applicationService;

    @Autowired
    private ImClientService imClientService;

    @Autowired
    private ImConversationService conversationService;

    @Autowired
    private ImConversationMembersService conversationMembersService;

    /**
     * 全量消息路由
     * @Author luozh
     * @Date 2022年04月22日 09:35:24
     * @param
     * @Return
     */
    @Override
    public Boolean fullMessageRouting(Long applicationId, ImMessage message) {

        ImApplication application = applicationService.getById(applicationId);
        if (application == null) {
            return false;
        }
        ImConversation conversation = conversationService.getById(message.getFkConversationId());
        if (!isNeedSync(conversation, message)) {
            return false;
        }

        String subscribeUrl = application.getFullMessageSubscribeUrl();
        if (StringUtils.isNotBlank(subscribeUrl)) {
            String appKey = application.getAppKey();
            String appSecret = application.getAppSecret();

            callback(subscribeUrl, appKey, appSecret, buildMsgBody(conversation, message));
        }
        return true;
    }

    /**
     * 用户在线状态
     * @Author luozh
     * @Date 2022年04月22日 09:35:47
     * @param applicationId  应用id
     * @param clientId  客户端id
     * @param status 状态：1：online 上线、0：offline 离线、2：logout 登出
     * @param deviceType 设备类型
     * @param time 发生时间
     * @param clientIp 用户当前的 IP 地址及端口
     * @Return
     */
    @Override
    public Boolean clientOnlineStatusChange(Long applicationId, Long clientId, Integer status,
                                            Integer deviceType, Long time, String clientIp) {

        ImApplication application = applicationService.getById(applicationId);
        if (application == null) {
            return false;
        }

        ImClient client = imClientService.getCacheImClient(clientId);

        String subscribeUrl = application.getOnlineStatusSubscribeUrl();
        if (StringUtils.isNotBlank(subscribeUrl)) {
            String appKey = application.getAppKey();
            String appSecret = application.getAppSecret();

            ClientOnlineStatusChangeDto body = ClientOnlineStatusChangeDto.builder()
                    .userId(client.getClientId())
                    .status(status)
                    .os("")
                    .time(time)
                    .clientIp(clientIp)
                    .build();
            callback(subscribeUrl, appKey, appSecret, body);
        }
        return true;
    }

    /**
     * 构建回调请求
     * @Author luozh
     * @Date 2022年04月22日 10:58:42
     * @param
     * @Return
     */
    private String buildFinalUrl(String url, String appKey, String appSecret) {
        // 计算 Signature (数据签名)
        String nonce = NanoId.randomNanoId();
        String date = DateUtil.formatHttpDate(new Date());
        String signature = SignUtils.buildSignature(appKey, appSecret, nonce, date);

        String finalUrl =
                url + "?appKey=" + appKey + "&nonce=" + nonce + "&date=" + date + "&signature=" + UriEncoder.encode(signature);
        return finalUrl;
    }


    private Boolean isNeedSync(ImConversation conversation, ImMessage message) {
        int chatType = conversation.getChatType();
        if (!(ChatTypeEnum.SINGLE.getCode().equals(chatType) || ChatTypeEnum.NORMAL_GROUP.getCode().equals(chatType))) {
            return false;
        }
        int msgType = message.getMsgType();
        // 只需要同步 文本消息 图像消息 音频消息 视频消息 文件消息
        if (MsgTypeEnum.MEDIA_TYPE_TEXT.getUriCode() == msgType
                || MsgTypeEnum.MEDIA_TYPE_IMAGE.getUriCode() == msgType
                || MsgTypeEnum.MEDIA_TYPE_AUDIO.getUriCode() == msgType
                || MsgTypeEnum.MEDIA_TYPE_VIDEO.getUriCode() == msgType
                || MsgTypeEnum.MEDIA_TYPE_FILE.getUriCode() == msgType) {
            return true;
        }
        return false;
    }

    private JSONObject buildMsgBody(ImConversation conversation, ImMessage message) {
        ImClient sender = imClientService.getById(message.getSender());

        JSONObject body = new JSONObject();
        body.put("id", message.getId());
        body.put("fromUserId", sender.getClientId());

        if (Objects.equals(ChatTypeEnum.SINGLE.getCode(), conversation.getChatType())) {
            // 查找出目标id
            ImConversationMembers anotherMember =
                    conversationMembersService.list(Wrappers.<ImConversationMembers>lambdaQuery()
                                    .eq(ImConversationMembers::getFkConversationId, conversation.getId()))
                            .stream()
                            .filter(member -> !member.getClientId().equals(sender.getClientId())).collect(Collectors.toList()).get(0);

            body.put("targetId", anotherMember.getClientId());
            body.put("chatType", ChatTypeEnum.SINGLE);
        } else {
            body.put("chatType", ChatTypeEnum.NORMAL_GROUP);
            body.put("targetId", conversation.getId());
        }
        int msgType = message.getMsgType();
        body.put("msgType", MsgTypeEnum.getByCode(msgType));
        body.put("content", message.getContent());
        body.put("msgSendTime", DateUtil.formatDateTime(message.getCreateTime()));
        body.put("withdrawTime", DateUtil.formatDateTime(message.getWithdrawTime()));
        return body;
    }

    private void callback(String subscribeUrl, String appKey, String appSecret, Object body) {
        try {
            UriComponentsBuilder uriComponentsBuilder =
                    UriComponentsBuilder.fromHttpUrl(subscribeUrl);
            URI uri = uriComponentsBuilder.build(true).toUri();

            HttpHeaders headers = buildHeader(subscribeUrl, appKey, appSecret);

            HttpEntity<Object> entity = new HttpEntity<>(body, headers);
            ResponseEntity<Object> response = restTemplate.exchange(uri, HttpMethod.POST, entity, Object.class);

            // 同步在线状态时需要接收服务提供应答，只要有 HTTP 应答码 200 即认为状态已经同步
            if (response.getStatusCode().equals(HttpStatus.OK)) {
                // do nothing
            } else {
                // 如果应答超时 5 秒，会再尝试推送 2 次，如果仍然失败，将不再同步此条状态。如短时间内有大面积超时，将暂停推送，1 分钟后会继续推送。
            }
        } catch (Exception e) {
            // do nothing is ok
        }
    }

    private HttpHeaders buildHeader(String url, String appKey, String appSecret) {
        // 计算 Signature (数据签名)
        String nonce = NanoId.randomNanoId();
        String date = DateUtil.formatHttpDate(new Date());
        String signature = SignUtils.buildSignature(appKey, appSecret, nonce, date);
        HttpHeaders headers = new HttpHeaders();
        headers.add("WECLOUD-IM-APPKEY", appKey);
        headers.add("WECLOUD-IM-NONCE", nonce);
        headers.add("WECLOUD-IM-DATE", date);
        headers.add("WECLOUD-IM-SIGNATURE", signature);
        return headers;
    }
}
