package com.wecloud.multimeet.service.impl;

import io.geekidea.springbootplus.framework.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.List;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.common.collect.Lists;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImMultiRtcRoom;
import com.wecloud.im.entity.ImMultiRtcRoomMember;
import com.wecloud.im.param.AgreeToMultiMeetParam;
import com.wecloud.im.param.HeartbeatMultiMeetParam;
import com.wecloud.im.param.InviteToMultiMeetParam;
import com.wecloud.im.param.LeaveFromMultiMeetParam;
import com.wecloud.im.param.NotAnsweredMultiMeetParam;
import com.wecloud.im.param.RejectToMultiMeetParam;
import com.wecloud.im.sdk.enums.MultiRtcMemberStateEnum;
import com.wecloud.im.sdk.enums.MultiRtcRoomStateEnum;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.service.ImMultiRtcRoomMemberService;
import com.wecloud.im.service.ImMultiRtcRoomService;
import com.wecloud.im.ws.cache.UserStateCacheManager;
import com.wecloud.multimeet.entity.dto.RoomMemberDto;
import com.wecloud.multimeet.entity.response.MultiMeetAgreeResponse;
import com.wecloud.multimeet.entity.response.MultiMeetBusyResponse;
import com.wecloud.multimeet.entity.response.MultiMeetInviteResponse;
import com.wecloud.multimeet.entity.response.MultiMeetLeaveResponse;
import com.wecloud.multimeet.entity.response.MultiMeetRejectResponse;
import com.wecloud.multimeet.service.MultiMeetService;
import com.wecloud.multimeet.service.WsMultiMeetWrite;
import com.wecloud.utils.SnowflakeUtil;

/**
 * @Author wenzhida
 * @Date 2022/3/7 18:43
 * @Description 多人音视频接口实现
 */
@Slf4j
@Service
public class MultiMeetServiceImpl implements MultiMeetService {

    @Autowired
    private WsMultiMeetWrite wsMultiMeetWrite;

    @Autowired
    private ImClientService imClientService;

    @Autowired
    private UserStateCacheManager userStateCacheManager;

    @Autowired
    private ImMultiRtcRoomService imMultiRtcRoomService;

    @Autowired
    private ImMultiRtcRoomMemberService imMultiRtcRoomMemberService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void invite(InviteToMultiMeetParam param) {
        ImClient currentClient = imClientService.getCurrentClient();
        //  判断发起方必须在线
        boolean onlineStatus = userStateCacheManager.isOnline(currentClient.getId());
        if (!onlineStatus) {
            log.info("发起方必须在线" + currentClient.getFkAppid() + currentClient.getClientId());
            throw new BusinessException("发起方必须在线");
        }
        List<RoomMemberDto> members = Lists.newArrayList();
        ImMultiRtcRoom rtcRoom = imMultiRtcRoomService.getCachedMultiRtcRoom(currentClient.getFkAppid(), param.getRoomId());
        if (rtcRoom == null) {
            rtcRoom = new ImMultiRtcRoom();
            rtcRoom.setId(SnowflakeUtil.getId());
            rtcRoom.setFkAppid(currentClient.getFkAppid());
            rtcRoom.setRoomId(param.getRoomId());
            rtcRoom.setState(MultiRtcRoomStateEnum.CREATED.getCode());
            rtcRoom.setCreateTime(new Date());
            rtcRoom.setUpdateTime(new Date());
            imMultiRtcRoomService.save(rtcRoom);
        } else {
            List<ImMultiRtcRoomMember> rtcRoomMemberList = imMultiRtcRoomMemberService.list(
                    new QueryWrapper<ImMultiRtcRoomMember>().lambda()
                            .eq(ImMultiRtcRoomMember::getFkRtcRoomId, rtcRoom.getId())
                            .ne(ImMultiRtcRoomMember::getClientId, currentClient.getClientId())
            );
            for (ImMultiRtcRoomMember imMultiRtcRoomMember : rtcRoomMemberList) {
                RoomMemberDto roomMemberDto = new RoomMemberDto();
                roomMemberDto.setFkClientId(imMultiRtcRoomMember.getFkClientId());
                roomMemberDto.setClientId(imMultiRtcRoomMember.getClientId());
                roomMemberDto.setState(imMultiRtcRoomMember.getState());
                members.add(roomMemberDto);
            }
        }
        List<ImMultiRtcRoomMember> roomMembersToSave = Lists.newArrayList();
        for (String toClientId : param.getToClients()) {
            ImClient toClient = imClientService.getCacheImClient(currentClient.getFkAppid(), toClientId);
            if (toClient == null) {
                log.info("查无接收人信息 {}", toClientId);
                continue;
            }
            // 占线 通知邀请方
            List<ImMultiRtcRoomMember> busyMember = imMultiRtcRoomMemberService.list(new QueryWrapper<ImMultiRtcRoomMember>().lambda()
                    .eq(ImMultiRtcRoomMember::getClientId, toClientId)
                    .in(ImMultiRtcRoomMember::getState, Lists.newArrayList(MultiRtcMemberStateEnum.ANSWERED.getCode(), MultiRtcMemberStateEnum.CALLING.getCode())));
            if (CollectionUtils.isNotEmpty(busyMember)) {
                MultiMeetBusyResponse multiMeetBusyResponse = new MultiMeetBusyResponse();
                multiMeetBusyResponse.setConversationId(param.getConversationId());
                multiMeetBusyResponse.setRoomId(param.getRoomId());
                multiMeetBusyResponse.setClientId(currentClient.getClientId());
                multiMeetBusyResponse.setBusyClientId(busyMember.get(0).getClientId());
                multiMeetBusyResponse.setTimestamp(System.currentTimeMillis());
                wsMultiMeetWrite.busy(multiMeetBusyResponse, currentClient.getId());
                continue;
            }
            ImMultiRtcRoomMember rtcRoomMember = imMultiRtcRoomMemberService.getOne(new QueryWrapper<ImMultiRtcRoomMember>().lambda()
                    .eq(ImMultiRtcRoomMember::getClientId, toClientId)
                    .eq(ImMultiRtcRoomMember::getRoomId, param.getRoomId()));
            if (rtcRoomMember != null) {
                // 该房间已邀请过一次，再次发起邀请
                rtcRoomMember.setState(MultiRtcMemberStateEnum.CALLING.getCode());
                rtcRoomMember.setCallTime(new Date());
                imMultiRtcRoomMemberService.updateById(rtcRoomMember);
                continue;
            }
            ImMultiRtcRoomMember roomMember = new ImMultiRtcRoomMember();
            roomMember.setId(SnowflakeUtil.getId());
            roomMember.setFkRtcRoomId(rtcRoom.getId());
            roomMember.setRoomId(param.getRoomId());
            roomMember.setFkClientId(toClient.getId());
            roomMember.setClientId(toClient.getClientId());
            roomMember.setState(MultiRtcMemberStateEnum.CALLING.getCode());
            roomMember.setCallTime(new Date());
            roomMember.setCreateTime(new Date());
            roomMember.setUpdateTime(new Date());
            roomMembersToSave.add(roomMember);
            RoomMemberDto roomMemberDto = new RoomMemberDto();
            roomMemberDto.setFkClientId(toClient.getId());
            roomMemberDto.setClientId(toClient.getClientId());
            roomMemberDto.setState(MultiRtcMemberStateEnum.CALLING.getCode());
            members.add(roomMemberDto);
        }
        // 将邀请人存入房间成员
        ImMultiRtcRoomMember roomMember = new ImMultiRtcRoomMember();
        roomMember.setId(SnowflakeUtil.getId());
        roomMember.setFkRtcRoomId(rtcRoom.getId());
        roomMember.setRoomId(param.getRoomId());
        roomMember.setFkClientId(currentClient.getId());
        roomMember.setClientId(currentClient.getClientId());
        roomMember.setState(MultiRtcMemberStateEnum.ANSWERED.getCode());
        roomMember.setCallTime(new Date());
        roomMember.setHeartbeatTime(new Date());
        roomMember.setCreateTime(new Date());
        roomMember.setUpdateTime(new Date());
        roomMembersToSave.add(roomMember);
        imMultiRtcRoomMemberService.saveBatch(roomMembersToSave);
        for (RoomMemberDto member : members) {
            // ws向接收方发送通知
            MultiMeetInviteResponse multiMeetInviteResponse = new MultiMeetInviteResponse();
            multiMeetInviteResponse.setConversationId(param.getConversationId());
            multiMeetInviteResponse.setRoomId(param.getRoomId());
            multiMeetInviteResponse.setClientId(currentClient.getClientId());
            multiMeetInviteResponse.setTimestamp(System.currentTimeMillis());
            multiMeetInviteResponse.setMemberDtoList(members);
            wsMultiMeetWrite.invite(multiMeetInviteResponse, member.getFkClientId());

            // TODO 待开发 下发安卓和ios系统推送
        }
    }

    @Override
    public void reject(RejectToMultiMeetParam param) {
        ImClient currentClient = imClientService.getCurrentClient();
        imMultiRtcRoomMemberService.changeRoomMemberState(currentClient.getId(), param.getRoomId(), MultiRtcMemberStateEnum.REJECTED.getCode());
        List<ImMultiRtcRoomMember> rtcRoomMemberList = imMultiRtcRoomMemberService.list(
                new QueryWrapper<ImMultiRtcRoomMember>().lambda()
                        .eq(ImMultiRtcRoomMember::getRoomId, param.getRoomId())
                        .in(ImMultiRtcRoomMember::getState, Lists.newArrayList(MultiRtcMemberStateEnum.ANSWERED.getCode(), MultiRtcMemberStateEnum.CALLING.getCode()))
                        .ne(ImMultiRtcRoomMember::getClientId, currentClient.getClientId())
        );
        for (ImMultiRtcRoomMember imMultiRtcRoomMember : rtcRoomMemberList) {
            // ws向接收方发送通知
            MultiMeetRejectResponse multiMeetRejectResponse = new MultiMeetRejectResponse();
            multiMeetRejectResponse.setConversationId(param.getConversationId());
            multiMeetRejectResponse.setRoomId(param.getRoomId());

            multiMeetRejectResponse.setClientId(currentClient.getClientId());
            multiMeetRejectResponse.setTimestamp(System.currentTimeMillis());

            ImClient receiver = imClientService.getCacheImClient(currentClient.getFkAppid(), imMultiRtcRoomMember.getClientId());
            if (receiver == null) {
                log.info("查无接收人信息 {}", imMultiRtcRoomMember.getClientId());
                throw new BusinessException("查无发起会议人信息");
            }
            wsMultiMeetWrite.reject(multiMeetRejectResponse, receiver.getId());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void agree(AgreeToMultiMeetParam param) {
        ImClient currentClient = imClientService.getCurrentClient();
        ImMultiRtcRoom rtcRoom = imMultiRtcRoomService.getCachedMultiRtcRoom(currentClient.getFkAppid(), param.getRoomId());
        if (rtcRoom == null) {
            throw new BusinessException("查无多人音视频房间");
        }
        if (MultiRtcRoomStateEnum.CREATED.getCode().equals(rtcRoom.getState())) {
            imMultiRtcRoomService.deleteMultiRtcRoomCache(currentClient.getFkAppid(), param.getRoomId());
            imMultiRtcRoomService.updateMultiRtcRoomState(param.getRoomId(), MultiRtcRoomStateEnum.MEETING.getCode());
        }
        imMultiRtcRoomMemberService.changeRoomMemberState(currentClient.getId(), param.getRoomId(), MultiRtcMemberStateEnum.ANSWERED.getCode());
        List<ImMultiRtcRoomMember> rtcRoomMemberList = imMultiRtcRoomMemberService.list(
                new QueryWrapper<ImMultiRtcRoomMember>().lambda()
                        .eq(ImMultiRtcRoomMember::getRoomId, param.getRoomId())
                        .in(ImMultiRtcRoomMember::getState, Lists.newArrayList(MultiRtcMemberStateEnum.ANSWERED.getCode(), MultiRtcMemberStateEnum.CALLING.getCode()))
                        .ne(ImMultiRtcRoomMember::getClientId, currentClient.getClientId())
        );
        for (ImMultiRtcRoomMember imMultiRtcRoomMember : rtcRoomMemberList) {
            // ws向接收方发送通知
            MultiMeetAgreeResponse multiMeetAgreeResponse = new MultiMeetAgreeResponse();
            multiMeetAgreeResponse.setConversationId(param.getConversationId());
            multiMeetAgreeResponse.setRoomId(param.getRoomId());

            multiMeetAgreeResponse.setClientId(currentClient.getClientId());
            multiMeetAgreeResponse.setTimestamp(System.currentTimeMillis());
            ImClient receiver = imClientService.getCacheImClient(currentClient.getFkAppid(), imMultiRtcRoomMember.getClientId());
            if (receiver == null) {
                log.info("查无接收人信息 {}", imMultiRtcRoomMember.getClientId());
                throw new BusinessException("查无发起会议人信息");
            }
            wsMultiMeetWrite.agree(multiMeetAgreeResponse, receiver.getId());
        }

    }

    @Override
    public void leave(LeaveFromMultiMeetParam param) {
        ImClient currentClient = imClientService.getCurrentClient();
        imMultiRtcRoomMemberService.changeRoomMemberState(currentClient.getId(), param.getRoomId(), MultiRtcMemberStateEnum.LEAVE.getCode());
        List<ImMultiRtcRoomMember> rtcRoomMemberList = imMultiRtcRoomMemberService.list(
                new QueryWrapper<ImMultiRtcRoomMember>().lambda()
                        .eq(ImMultiRtcRoomMember::getRoomId, param.getRoomId())
                        .in(ImMultiRtcRoomMember::getState, Lists.newArrayList(MultiRtcMemberStateEnum.ANSWERED.getCode(), MultiRtcMemberStateEnum.CALLING.getCode()))
                        .ne(ImMultiRtcRoomMember::getClientId, currentClient.getClientId())
        );
        for (ImMultiRtcRoomMember imMultiRtcRoomMember : rtcRoomMemberList) {
            // ws向接收方发送通知
            MultiMeetLeaveResponse multiMeetLeaveResponse = new MultiMeetLeaveResponse();
            multiMeetLeaveResponse.setConversationId(param.getConversationId());
            multiMeetLeaveResponse.setRoomId(param.getRoomId());

            multiMeetLeaveResponse.setClientId(currentClient.getClientId());
            multiMeetLeaveResponse.setTimestamp(System.currentTimeMillis());
            ImClient receiver = imClientService.getCacheImClient(currentClient.getFkAppid(), imMultiRtcRoomMember.getClientId());
            if (receiver == null) {
                log.info("查无接收人信息 {}", imMultiRtcRoomMember.getClientId());
                throw new BusinessException("查无发起会议人信息");
            }
            wsMultiMeetWrite.leave(multiMeetLeaveResponse, receiver.getId());
        }
    }

    @Override
    public void notAnswered(NotAnsweredMultiMeetParam param) {
        ImClient currentClient = imClientService.getCurrentClient();
        imMultiRtcRoomMemberService.changeRoomMemberState(currentClient.getId(), param.getRoomId(), MultiRtcMemberStateEnum.NOT_ANSWERED.getCode());
    }

    @Override
    public void heartbeat(HeartbeatMultiMeetParam param) {
        ImClient currentClient = imClientService.getCurrentClient();
        imMultiRtcRoomMemberService.update(new UpdateWrapper<ImMultiRtcRoomMember>().lambda()
                .eq(ImMultiRtcRoomMember::getRoomId, param.getRoomId())
                .eq(ImMultiRtcRoomMember::getFkClientId, currentClient.getId())
                .set(ImMultiRtcRoomMember::getHeartbeatTime, new Date()));
    }

    @Override
    public void callingTimeout() {
        // 60 秒之前的时间
        Date beforeTime = DateUtils.addSeconds(new Date(), -60);
        // 获取呼叫时间为120秒之前 并且状态为呼叫中的会议成员
        List<ImMultiRtcRoomMember> rtcRoomMemberList = imMultiRtcRoomMemberService.list(
                new QueryWrapper<ImMultiRtcRoomMember>().lambda()
                        .lt(ImMultiRtcRoomMember::getCallTime, beforeTime)
                        .eq(ImMultiRtcRoomMember::getState, MultiRtcMemberStateEnum.CALLING.getCode())
        );
        if (CollectionUtils.isEmpty(rtcRoomMemberList)) {
            return;
        }
        for (ImMultiRtcRoomMember roomMember : rtcRoomMemberList) {
            roomMember.setState(MultiRtcMemberStateEnum.NOT_ANSWERED.getCode());
        }
        imMultiRtcRoomMemberService.updateBatchById(rtcRoomMemberList);
    }

    @Override
    public void disconnect() {
        // 20 秒之前的时间
        Date beforeTime = DateUtils.addSeconds(new Date(), -20);
        // 获取最后心跳时间为20秒之前 并且状态为接听中的会议成员
        List<ImMultiRtcRoomMember> rtcRoomMemberList = imMultiRtcRoomMemberService.list(
                new QueryWrapper<ImMultiRtcRoomMember>().lambda()
                        .lt(ImMultiRtcRoomMember::getHeartbeatTime, beforeTime)
                        .eq(ImMultiRtcRoomMember::getState, MultiRtcMemberStateEnum.ANSWERED.getCode())
        );
        if (CollectionUtils.isEmpty(rtcRoomMemberList)) {
            return;
        }
        for (ImMultiRtcRoomMember roomMember : rtcRoomMemberList) {
            roomMember.setState(MultiRtcMemberStateEnum.DISCONNECT.getCode());
        }
        imMultiRtcRoomMemberService.updateBatchById(rtcRoomMemberList);
    }

}
