package com.ruoyi.im.service.impl;


import com.ruoyi.common.annotation.OnlineCheck;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.im.constant.RedisKey;
import com.ruoyi.common.im.enums.MessageStatus;
import com.ruoyi.common.im.enums.MessageType;
import com.ruoyi.common.im.enums.WebrtcMode;
import com.ruoyi.common.im.model.IMPrivateMessage;
import com.ruoyi.common.im.model.IMUserInfo;
import com.ruoyi.common.im.session.WebrtcPrivateSession;
import com.ruoyi.common.im.util.BeanUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.im.domain.PrivateMessage;
import com.ruoyi.im.domain.vo.PrivateMessageVO;
import com.ruoyi.im.service.PrivateMessageService;
import com.ruoyi.im.service.WebrtcPrivateService;
import com.ruoyi.im.util.UserStateUtils;
import com.ruoyi.imclient.IMClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.Collections;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class WebrtcPrivateServiceImpl implements WebrtcPrivateService {

    private final IMClient imClient;
    private final RedisTemplate<String, Object> redisTemplate;
    private final PrivateMessageService privateMessageService;
    private final UserStateUtils userStateUtils;

    @OnlineCheck
    @Override
    public void call(Long uid, String mode, String offer) {
        Long userId = SecurityUtils.getUserId();
        log.info("发起呼叫,sid:{},uid:{}", userId, uid);
        // 创建webrtc会话
        WebrtcPrivateSession webrtcSession = new WebrtcPrivateSession();
        webrtcSession.setCallerId(userId);
        webrtcSession.setCallerTerminal(1);
        webrtcSession.setAcceptorId(uid);
        webrtcSession.setMode(mode);
        // 校验
        if (!imClient.isOnline(uid)) {
            this.sendActMessage(webrtcSession, MessageStatus.UNSEND, "未接通");
            log.info("对方不在线,uid:{}", uid);
            throw new GlobalException("对方目前不在线");
        }
        if (userStateUtils.isBusy(uid)) {
            this.sendActMessage(webrtcSession, MessageStatus.UNSEND, "未接通");
            log.info("对方正忙,uid:{}", uid);
            throw new GlobalException("对方正忙");
        }
        // 保存rtc session
        String key = getWebRtcSessionKey(userId, uid);
        redisTemplate.opsForValue().set(key, webrtcSession, 60, TimeUnit.SECONDS);
        // 设置用户忙线状态
        userStateUtils.setBusy(uid);
        userStateUtils.setBusy(userId);
        // 向对方所有终端发起呼叫
        PrivateMessageVO messageInfo = new PrivateMessageVO();
        MessageType messageType =
            mode.equals(WebrtcMode.VIDEO.getValue()) ? MessageType.RTC_CALL_VIDEO : MessageType.RTC_CALL_VOICE;
        messageInfo.setType(messageType.code());
        messageInfo.setRecvId(uid);
        messageInfo.setSendId(userId);
        messageInfo.setContent(offer);

        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setSender(new IMUserInfo(userId, 1));
        sendMessage.setRecvId(uid);
        sendMessage.setSendToSelf(false);
        sendMessage.setSendResult(false);
        sendMessage.setData(messageInfo);
        imClient.sendPrivateMessage(sendMessage);

    }

    @Override
    public void accept(Long uid, @RequestBody String answer) {
        Long userId = SecurityUtils.getUserId();
        log.info("接受通话,sid:{},uid:{}",userId, uid);
        // 查询webrtc会话
        WebrtcPrivateSession webrtcSession = getWebrtcSession(userId, uid);
        // 更新接受者信息
        webrtcSession.setAcceptorId(userId);
        webrtcSession.setAcceptorTerminal(1);
        webrtcSession.setChatTimeStamp(System.currentTimeMillis());
        String key = getWebRtcSessionKey(userId, uid);
        redisTemplate.opsForValue().set(key, webrtcSession, 60, TimeUnit.SECONDS);
        // 向发起人推送接受通话信令
        PrivateMessageVO messageInfo = new PrivateMessageVO();
        messageInfo.setType(MessageType.RTC_ACCEPT.code());
        messageInfo.setRecvId(uid);
        messageInfo.setSendId(userId);
        messageInfo.setContent(answer);

        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setSender(new IMUserInfo(userId, 1));
        sendMessage.setRecvId(uid);
        // 告知其他终端已经接受会话,中止呼叫
        sendMessage.setSendToSelf(true);
        sendMessage.setSendResult(false);
        sendMessage.setRecvTerminals((Collections.singletonList(webrtcSession.getCallerTerminal())));
        sendMessage.setData(messageInfo);
        imClient.sendPrivateMessage(sendMessage);
    }

    @Override
    public void reject(Long uid) {
        Long userId = SecurityUtils.getUserId();
        log.info("拒绝通话,sid:{},uid:{}", userId, uid);
        // 查询webrtc会话
        WebrtcPrivateSession webrtcSession = getWebrtcSession(userId, uid);
        // 删除会话信息
        removeWebrtcSession(uid, userId);
        // 设置用户空闲状态
        userStateUtils.setFree(uid);
        userStateUtils.setFree(userId);
        // 向发起人推送拒绝通话信令
        PrivateMessageVO messageInfo = new PrivateMessageVO();
        messageInfo.setType(MessageType.RTC_REJECT.code());
        messageInfo.setRecvId(uid);
        messageInfo.setSendId(userId);

        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setSender(new IMUserInfo(userId, 1));
        sendMessage.setRecvId(uid);
        // 告知其他终端已经拒绝会话,中止呼叫
        sendMessage.setSendToSelf(true);
        sendMessage.setSendResult(false);
        sendMessage.setRecvTerminals(Collections.singletonList(webrtcSession.getCallerTerminal()));
        sendMessage.setData(messageInfo);
        imClient.sendPrivateMessage(sendMessage);
        // 生成通话消息
        sendActMessage(webrtcSession, MessageStatus.READED, "已拒绝");
    }

    @Override
    public void cancel(Long uid) {
        Long userId = SecurityUtils.getUserId();
        log.info("取消通话,sid:{},uid:{}", userId, uid);
        // 查询webrtc会话
        WebrtcPrivateSession webrtcSession = getWebrtcSession(userId, uid);
        // 删除会话信息
        removeWebrtcSession(userId, uid);
        // 设置用户空闲状态
        userStateUtils.setFree(uid);
        userStateUtils.setFree(userId);
        // 向对方所有终端推送取消通话信令
        PrivateMessageVO messageInfo = new PrivateMessageVO();
        messageInfo.setType(MessageType.RTC_CANCEL.code());
        messageInfo.setRecvId(uid);
        messageInfo.setSendId(userId);

        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setSender(new IMUserInfo(userId, 1));
        sendMessage.setRecvId(uid);
        sendMessage.setSendToSelf(false);
        sendMessage.setSendResult(false);
        sendMessage.setData(messageInfo);
        // 通知对方取消会话
        imClient.sendPrivateMessage(sendMessage);
        // 生成通话消息
        sendActMessage(webrtcSession, MessageStatus.UNSEND, "已取消");
    }

    @Override
    public void failed(Long uid, String reason) {
        Long userId = SecurityUtils.getUserId();
        log.info("通话失败,sid:{},uid:{},reason:{}", userId, uid, reason);
        // 查询webrtc会话
        WebrtcPrivateSession webrtcSession = getWebrtcSession(userId, uid);
        // 删除会话信息
        removeWebrtcSession(uid, userId);
        // 设置用户空闲状态
        userStateUtils.setFree(uid);
        userStateUtils.setFree(userId);
        // 向发起方推送通话失败信令
        PrivateMessageVO messageInfo = new PrivateMessageVO();
        messageInfo.setType(MessageType.RTC_FAILED.code());
        messageInfo.setRecvId(uid);
        messageInfo.setSendId(userId);
        messageInfo.setContent(reason);

        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setSender(new IMUserInfo(userId,1));
        sendMessage.setRecvId(uid);
        sendMessage.setSendToSelf(false);
        sendMessage.setSendResult(false);
        sendMessage.setRecvTerminals(Collections.singletonList(webrtcSession.getCallerTerminal()));
        sendMessage.setData(messageInfo);
        // 通知对方取消会话
        imClient.sendPrivateMessage(sendMessage);
        // 生成消息
        sendActMessage(webrtcSession, MessageStatus.READED, "未接通");
    }

    @Override
    public void handup(Long uid) {
        Long userId = SecurityUtils.getUserId();
        log.info("挂断通话,sid:{},uid:{}", userId, uid);
        // 查询webrtc会话
        WebrtcPrivateSession webrtcSession = getWebrtcSession(userId, uid);
        // 删除会话信息
        removeWebrtcSession(uid, userId);
        // 设置用户空闲状态
        userStateUtils.setFree(uid);
        userStateUtils.setFree(userId);
        // 向对方推送挂断通话信令
        PrivateMessageVO messageInfo = new PrivateMessageVO();
        messageInfo.setType(MessageType.RTC_HANDUP.code());
        messageInfo.setRecvId(uid);
        messageInfo.setSendId(userId);

        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setSender(new IMUserInfo(userId, 1));
        sendMessage.setRecvId(uid);
        sendMessage.setSendToSelf(false);
        sendMessage.setSendResult(false);
        Integer terminal = getTerminalType(uid, webrtcSession);
        sendMessage.setRecvTerminals(Collections.singletonList(terminal));
        sendMessage.setData(messageInfo);
        // 通知对方取消会话
        imClient.sendPrivateMessage(sendMessage);
        // 生成通话消息
        sendActMessage(webrtcSession, MessageStatus.READED, "通话时长 " + chatTimeText(webrtcSession));
    }

    @Override
    public void candidate(Long uid, String candidate) {
        Long userId = SecurityUtils.getUserId();
        // 查询webrtc会话
        WebrtcPrivateSession webrtcSession = getWebrtcSession(userId, uid);
        // 向发起方推送同步candidate信令
        PrivateMessageVO messageInfo = new PrivateMessageVO();
        messageInfo.setType(MessageType.RTC_CANDIDATE.code());
        messageInfo.setRecvId(uid);
        messageInfo.setSendId(userId);
        messageInfo.setContent(candidate);

        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setSender(new IMUserInfo(userId, 1));
        sendMessage.setRecvId(uid);
        sendMessage.setSendToSelf(false);
        sendMessage.setSendResult(false);
        Integer terminal = getTerminalType(uid, webrtcSession);
        sendMessage.setRecvTerminals(Collections.singletonList(terminal));
        sendMessage.setData(messageInfo);
        imClient.sendPrivateMessage(sendMessage);
    }

    @Override
    public void heartbeat(Long uid) {
        Long userId = SecurityUtils.getUserId();
        // 会话续命
        String key = getWebRtcSessionKey(userId, uid);
        redisTemplate.expire(key, 60, TimeUnit.SECONDS);
        // 用户状态续命
        userStateUtils.expire(userId);
    }

    private WebrtcPrivateSession getWebrtcSession(Long userId, Long uid) {
        String key = getWebRtcSessionKey(userId, uid);
        WebrtcPrivateSession webrtcSession = (WebrtcPrivateSession)redisTemplate.opsForValue().get(key);
        if (webrtcSession == null) {
            throw new GlobalException("通话已结束");
        }
        return webrtcSession;
    }

    private void removeWebrtcSession(Long userId, Long uid) {
        String key = getWebRtcSessionKey(userId, uid);
        redisTemplate.delete(key);
    }

    private String getWebRtcSessionKey(Long id1, Long id2) {
        Long minId = id1 > id2 ? id2 : id1;
        Long maxId = id1 > id2 ? id1 : id2;
        return String.join(":", RedisKey.IM_WEBRTC_PRIVATE_SESSION, minId.toString(), maxId.toString());
    }

    private Integer getTerminalType(Long uid, WebrtcPrivateSession webrtcSession) {
        if (uid.equals(webrtcSession.getCallerId())) {
            return webrtcSession.getCallerTerminal();
        }
        return webrtcSession.getAcceptorTerminal();
    }

    private void sendActMessage(WebrtcPrivateSession rtcSession, MessageStatus status, String content) {
        // 保存消息
        PrivateMessage msg = new PrivateMessage();
        msg.setSendId(rtcSession.getCallerId());
        msg.setRecvId(rtcSession.getAcceptorId());
        msg.setContent(content);
        msg.setSendTime(new Date());
        msg.setStatus(status.code());
        MessageType type = rtcSession.getMode().equals(WebrtcMode.VIDEO.getValue()) ? MessageType.ACT_RT_VIDEO
            : MessageType.ACT_RT_VOICE;
        msg.setType(type.code());
        privateMessageService.save(msg);
        // 推给发起人
        PrivateMessageVO messageInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
        IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
        sendMessage.setSender(new IMUserInfo(rtcSession.getCallerId(), rtcSession.getCallerTerminal()));
        sendMessage.setRecvId(rtcSession.getCallerId());
        sendMessage.setSendToSelf(false);
        sendMessage.setSendResult(true);
        sendMessage.setData(messageInfo);
        imClient.sendPrivateMessage(sendMessage);
        // 推给接听方
        sendMessage.setRecvId(rtcSession.getAcceptorId());
        imClient.sendPrivateMessage(sendMessage);
    }

    private String chatTimeText(WebrtcPrivateSession rtcSession) {
        long chatTime = (System.currentTimeMillis() - rtcSession.getChatTimeStamp()) / 1000;
        int min = Math.abs((int)chatTime / 60);
        int sec = Math.abs((int)chatTime % 60);
        String strTime = min < 10 ? "0" : "";
        strTime += min;
        strTime += ":";
        strTime += sec < 10 ? "0" : "";
        strTime += sec;
        return strTime;
    }

}
