package com.sikey.wa04.business.application.service.message;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sikey.wa04.business.application.converter.MessageConverter;
import com.sikey.wa04.business.domain.entity.UserId;
import com.sikey.wa04.business.domain.entity.message.Message;
import com.sikey.wa04.business.domain.entity.message.MessageReceivedLog;
import com.sikey.wa04.business.infrastructure.adapter.CacheConnectionStateManager;
import com.sikey.wa04.business.infrastructure.dto.model.MessageRecvModel;
import com.sikey.wa04.business.infrastructure.dto.model.PushRegistrationModel;
import com.sikey.wa04.business.infrastructure.dto.request.publish.PublishSubscribeRequest;
import com.sikey.wa04.business.infrastructure.dto.request.publish.PublishUnsubscribeRequest;
import com.sikey.wa04.business.infrastructure.repository.MessageRecvRepository;
import com.sikey.wa04.business.infrastructure.repository.PushRegistrationRepository;
import com.sikey.wa04.common.dto.header.GenericManufacturerHeader;
import com.sikey.wa04.common.enums.MessageStatus;
import com.sikey.wa04.common.enums.MessageType;
import com.sikey.wa04.common.enums.MessageTypeFilterEnum;
import com.sikey.wa04.common.interceptor.CurrentRequestUser;
import jakarta.annotation.Resource;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.time.Instant;
import java.util.List;
import java.util.Objects;

/**
 * 消息订阅
 *
 * @author luoyangwei
 */
@Slf4j
@Service
public class MessagingSubscribeService {

    @Resource
    private PushRegistrationRepository pushRegistrationRepository;

    @Resource
    private CacheConnectionStateManager cacheConnectionStateManager;

    @Resource
    private MessageRecvRepository messageRecvRepository;

    @Resource
    private ObjectMapper objectMapper;

    /**
     * 订阅消息
     *
     * @param request 订阅请求
     * @param headers 消息头
     */
    @Transactional
    public void subscribe(PublishSubscribeRequest request, GenericManufacturerHeader headers) throws Exception {
        String userId = CurrentRequestUser.get().getId();

        PushRegistrationModel model = pushRegistrationRepository.findFirstByUserId(userId);
        if (Objects.isNull(model)) {
            model = new PushRegistrationModel();
            model.setUserId(userId);
        }
        model.setRegId(request.regId());
        model.setRegistrantPlatformType(request.registrantPlatformType());
        model.setLanguage(headers.getLocalization());
        model.setDeviceModel(request.deviceModel());
        model.setDeviceManufacturer(headers.getManufacturer());
        model.setDeviceSystemVersion(headers.getSystemVersion());
        model.setDeviceSystem(headers.getSystem());
        pushRegistrationRepository.save(model);
    }

    /**
     * 取消订阅
     */
    @Transactional
    public void unsubscribe(PublishUnsubscribeRequest request) {
        pushRegistrationRepository.deleteByUserId(request.userId());
    }

    /**
     * 消费消息
     *
     * @param message 消息
     */
    public void consumeMessage(Message message) {
        log.info("Consume message: {}", message);

        /*ObjectMapper objectMapper = new ObjectMapper();
        MessageTypeSerializer messageTypeSerializer = new MessageTypeSerializer();
        MessageTypeDeserializer messageTypeDeserializer = new MessageTypeDeserializer();
        messageTypeSerializer.registerModule(objectMapper);
        messageTypeDeserializer.registerModule(objectMapper);*/

        try {
            for (MessageReceivedLog messageReceivedLog : message.getReceivedLogs()) {
                String receiverId = messageReceivedLog.getReceiverId();
                Instant receivedTime = Instant.now();

                // 加入数据库并且获取 ackId
                MessageRecvModel messageRecvModel = new MessageRecvModel();
                messageRecvModel.setMsgId(message.getId().getId());
                messageRecvModel.setRecvId(receiverId);
                messageRecvModel.setSendId(message.getSenderId());
                messageRecvModel.setGroupId(message.getGroupId());
                messageRecvModel.setMsgType(message.getType().getValue());
                messageRecvModel.setContent(objectMapper.convertValue(message.getContent(), new TypeReference<>() {
                }));
                messageRecvModel.setSendTime(message.getSendTime());
                messageRecvModel.setStatus(MessageStatus.DEFAULT.getValue());
                messageRecvModel.setCreatedAt(receivedTime);
                messageRecvModel.setUpdatedAt(receivedTime);
                messageRecvRepository.save(messageRecvModel);

                Long ackId = messageRecvModel.getId();
                messageReceivedLog.setAckId(ackId);


                if (cacheConnectionStateManager.isOnline(new UserId(receiverId))) {
                    WebSocketSession session = cacheConnectionStateManager.getSession(new UserId(receiverId));
                    //log.info("[发送推送消息]User {} is online, send directly，session：{}", receiverId, JSONUtil.toJsonPrettyStr(session));
                    if (Objects.nonNull(session)) {

                        // 群聊组
                        boolean isGroup = message.isGroup();
                        if (isGroup) {
                            message.setReceiverId(List.of(message.getGroupId()));
                        }
                        message.setGroup(isGroup);

                        message.setAckId(ackId);
                        session.sendMessage(
                                new TextMessage(objectMapper.writeValueAsString(MessageConverter.toMessaging(message))));
                        messageRecvRepository.updateStatusById(MessageStatus.WAIT_ACK.getValue(), messageReceivedLog.getAckId());
                        //log.info("[发送推送消息]成功，User {} is online, send directly，session：{}", receiverId, JSONUtil.toJsonPrettyStr(session));
                        log.info("[发送推送消息]User {} 在线，推送成功", receiverId);
                        continue;
                    }
                } else {
                    log.info("[发送推送消息]User {} 不在线; ", receiverId,  message.getType().getValue());
                }

                //  log.info("User {} is offline", receiverId);

                // 只有白名单类型消息需要存入离线队列
                if (MessageType.OfflineQueueFilterMessageTypes().contains(message.getType().getValue())) {
                    messageRecvRepository.updateStatusById(MessageStatus.OFFLINE_QUEUE.getValue(), messageReceivedLog.getAckId());
                }
            }

            log.info("Consumption completed");
        } catch (Exception e) {
            log.error("消费消息出现异常：message：{}， err: {}", JSONUtil.toJsonPrettyStr(message), e.getMessage());
        }
    }

}
