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

import com.eatthepath.pushy.apns.ApnsClient;
import com.sikey.wa04.business.application.converter.MessageConverter;
import com.sikey.wa04.business.domain.adapter.ChildAdapter;
import com.sikey.wa04.business.domain.adapter.GroupMemberAdapter;
import com.sikey.wa04.business.domain.adapter.MessageAdapter;
import com.sikey.wa04.business.domain.adapter.UserAdapter;
import com.sikey.wa04.business.domain.entity.*;
import com.sikey.wa04.business.domain.entity.message.Message;
import com.sikey.wa04.business.domain.entity.message.MessageId;
import com.sikey.wa04.business.infrastructure.adapter.RabbitMQMessaging;
import com.sikey.wa04.business.infrastructure.adapter.RedisMessageIdIncrementingAdapter;
import com.sikey.wa04.business.infrastructure.adapter.push.*;
import com.sikey.wa04.business.infrastructure.adapter.push.template.NotificationMessageTemplate;
import com.sikey.wa04.business.infrastructure.configuration.PushConfig;
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.messaging.MessagingAckRequest;
import com.sikey.wa04.business.infrastructure.dto.request.messaging.MessagingPublishRequest;
import com.sikey.wa04.business.infrastructure.dto.response.messaging.MessagingPublishResponse;
import com.sikey.wa04.business.infrastructure.repository.MessageRecvRepository;
import com.sikey.wa04.business.infrastructure.repository.PushRegistrationRepository;
import com.sikey.wa04.common.enums.MessageStatus;
import com.sikey.wa04.common.enums.MessageType;
import com.sikey.wa04.common.enums.RegistrantPlatformType;
import com.sikey.wa04.common.enums.UserType;
import com.sikey.wa04.common.interceptor.CurrentRequestUser;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;

@Slf4j
@Service
public class MessagingPublishService {

    @Resource
    private RedisMessageIdIncrementingAdapter messageIdIncrementingAdapter;

    @Resource
    private GroupMemberAdapter groupMemberAdapter;

    @Resource
    private PushRegistrationRepository pushRegistrationRepository;

    @Resource
    private MessageAdapter messageAdapter;

    @Resource
    private RabbitMQMessaging rabbitMQMessaging;

    @Resource
    private MessageRecvRepository messageRecvRepository;

    @Resource
    private PushConfig pushConfig;

    @Resource
    private UserAdapter userAdapter;

    @Resource
    private ChildAdapter childAdapter;

    /**
     * 发布消息
     *
     * @param request 请求
     * @param <T>     消息内容类型
     * @return 响应
     */
    public <T> MessagingPublishResponse publish(MessagingPublishRequest<T> request) {
        MessageType messageType = MessageType.valueOfMsg(request.getMsgType());
        String userId = CurrentRequestUser.get().getId();
        Message message = new Message().setId(new MessageId(messageIdIncrementingAdapter.incr())).setType(messageType).setContent(request.getContent()).setGroup(request.getIsGroup()).setSenderId(userId).setSendTime(Instant.ofEpochMilli(request.getSendTime()));

        if (message.isGroup()) {

            GroupId groupId = new GroupId(request.getRecvId());
            // 注册用户
            List<GroupMember> findMembers = groupMemberAdapter.findMembers(groupId);
            List<String> refIdList = findMembers.stream().filter(m -> Objects.nonNull(m.getType()) && m.getType().getValue() == UserType.USER.getValue().intValue()).map(GroupMember::getRefId).toList();
            List<User> users = userAdapter.findByUserIdIn(refIdList);
            List<String> userIdList = users.stream().filter(u -> u.getIsRegistered()).map(User::getId).map(UserId::getId).toList();
            // 儿童
            List<String> childRefIdList = findMembers.stream().filter(m -> Objects.nonNull(m.getType()) && m.getType().getValue() == UserType.CHILD.getValue().intValue()).map(GroupMember::getRefId).toList();
            List<String> childIdList = childAdapter.findByChildIdIn(childRefIdList).stream().map(Child::getId).toList();
            List<String> allList = new ArrayList<>();
            allList.addAll(userIdList);
            allList.addAll(childIdList);

            message.setReceiverId(findMembers.stream().map(GroupMember::getRefId).filter(allList::contains).toList());

            // 群组id
            message.setGroupId(request.getRecvId());
        } else {
            message.setReceiverId(List.of(request.getRecvId()));
        }
        messageAdapter.save(message);

        // 过滤用户自己
        message.setReceiverId(message.getReceiverId().stream().filter(r -> !r.equals(userId)).toList());

        //  log.info("[发布消息]入参：{}，保存消息：{}", request, message);

        message.getReceiverId().forEach(receiverId -> {
            // 接收消息用户
            message.setReceiverId(List.of(receiverId));
            Object messagingObject = MessageConverter.toMessaging(message);
            log.info("Send message to receiver: {}，messagingObject：{}", receiverId, messagingObject);
            rabbitMQMessaging.publish(request.getHeader(), messagingObject);
        });

        return new MessagingPublishResponse().setMsgId(message.getId().getId());
    }

    /**
     * ack 应答
     *
     * @param request 请求
     */
    public void ack(MessagingAckRequest request) {
        log.info("Message ackId: {}", request.getAckId());
        // 校验消息的状态
        Optional<MessageRecvModel> optional = messageRecvRepository.findById(request.getAckId());
        if (optional.isPresent()) {
            MessageRecvModel messageRecvModel = optional.get();
            MessageStatus messageStatus = MessageStatus.valueOf(messageRecvModel.getStatus());
            if (messageStatus.equals(MessageStatus.WAIT_ACK)) {
                messageRecvRepository.updateStatusById(MessageStatus.RECEIVED_ACK.getValue(), request.getAckId());
                return;
            }
        }
        throw new RuntimeException("Illegal message status");
    }

    /**
     * 发布离线消息
     */
    public void publishOffline() {
        List<MessageRecvModel> messageRecvModels = messageRecvRepository.findTop120ByStatus(MessageStatus.OFFLINE_QUEUE.getValue());
        log.info("[发送三方推送消息]离线消息数量, size: {}", messageRecvModels.size());
        for (MessageRecvModel messageRecvModel : messageRecvModels) {
//            Instant start = Instant.now();
            PushRegistrationModel pushRegistration = pushRegistrationRepository.findFirstByUserId(messageRecvModel.getRecvId());
            if (Objects.isNull(pushRegistration)) {
                log.info("[发送三方推送消息]登录记录不存在-删除离线消息: 离线消息中userId:{}", messageRecvModel.getRecvId());
                //如果不存在登录记录，则删除离线消息
                messageRecvRepository.deleteById(messageRecvModel.getId());
                return;
            }
            try {
                Integer msgType = messageRecvModel.getMsgType();
                // Build notification message template
                NotificationMessageTemplate messageTemplate = new NotificationMessageTemplate();
                Map<String, Object> params = new HashMap<>();
                params.put("ackId", messageRecvModel.getRecvId());
                params.put("msgId", messageRecvModel.getMsgId());
                params.put("msgType", messageRecvModel.getMsgType());
                params.put("sendId", messageRecvModel.getSendId());
                params.put("recvId", messageRecvModel.getRecvId());
                params.put("sendTime", messageRecvModel.getSendTime().toEpochMilli());
                params.put("content", messageRecvModel.getContent());
                params.put("from_push", "true");

                String title = "新消息";
                String body = "点击查看";
                if (Objects.equals(msgType, MessageType.VIDEO_CALL_IN_NOTICE_MESSAGE.getValue()) || Objects.equals(msgType, MessageType.VIDEO_CALL_OUT_NOTICE_MESSAGE.getValue())) {
                    title = "视频通话";
                    body = "点击查看";
                }else if(MessageType.getChatMsgTypes().contains(msgType)) {
                    title = "收到新语聊消息";
                    body = "点击查看";
                }else if (Objects.equals(msgType, MessageType.LOCATION_EXIT_SAFETY_FENCE_NOTICE_MESSAGE.getValue())) {
                    title = "离开安全围栏";
                    body = "点击查看";
                } else if (Objects.equals(msgType, MessageType.LOCATION_ENTER_DANGER_FENCE_NOTICE_MESSAGE.getValue())) {
                    title = "进入危险围栏";
                    body = "点击查看";
                } else if (Objects.equals(msgType, MessageType.DEVICE_LOW_BATTERY_NOTICE_MESSAGE.getValue())) {
                    title = "设备电量过低";
                    body = "请及时充电";
                }
                messageTemplate.setMsgId(messageRecvModel.getMsgId()).setMsgType(messageRecvModel.getMsgType()).setRegId(pushRegistration.getRegId()).setTitle(title).setBody(body).setParams(params);

                RegistrantPlatformType registrantPlatformType = RegistrantPlatformType.valueOf(pushRegistration.getRegistrantPlatformType());
                // log.info("Send offline message to: {}", pushRegistration);
                SendResult result = null;

                switch (registrantPlatformType.getPlatformType()) {
                    case XIAOMI:
                    case VIVO:

                        XiaomiPushNotification xiaomiPushNotification = new XiaomiPushNotification();
                        AccessToken xiaomiAccessToken = xiaomiPushNotification.authenticate(pushConfig);
                        result = xiaomiPushNotification.send(messageTemplate, xiaomiAccessToken);
                        break;

                    case HUAWEI:
                        // Send Huawei notification message
                        HuaweiPushNotification huaweiPushNotification = new HuaweiPushNotification();
                        AccessToken accessToken = huaweiPushNotification.authenticate(pushConfig);
                        result = huaweiPushNotification.send(messageTemplate, accessToken);
                        break;

                    case IOS:

                        // Send IOS notification message
                        IOSPushNotification iosPushNotification = new ANPsPushNotification();
                        ApnsClient client = iosPushNotification.authenticate(pushConfig);
                        result = iosPushNotification.send(messageTemplate, client);
                        break;

                    default:
                        log.warn("[发送三方推送消息不存在的手机型号] userId:{}; type: {}", pushRegistration.getUserId(), registrantPlatformType.getPlatformType());
                }

                // Change message to WAIT_ACK
                if (result != null && result.isSuccessful()) {
                    log.info("[发送三方推送消息成功] userId: {};", pushRegistration.getUserId());
                    messageRecvRepository.updateStatusById(MessageStatus.WAIT_ACK.getValue(), messageRecvModel.getId());
                }

            } catch (Exception e) {
                log.error("[发送三方推送消息失败]userId: {}", pushRegistration.getUserId(), e);
            }
//            long timeConsuming = ChronoUnit.MILLIS.between(start, Instant.now());
//            log.info("[发送三方推送一次发送]，耗时：{} ms", timeConsuming);
        }
    }
}
