package com.ruoyi.cai.ws.service;

import cn.hutool.core.date.DateUtil;
import com.ruoyi.cai.domain.Account;
import com.ruoyi.cai.executor.ExecutorConstant;
import com.ruoyi.cai.service.AccountService;
import com.ruoyi.cai.service.AnchorService;
import com.ruoyi.cai.service.UserService;
import com.ruoyi.cai.trd.Agora;
import com.ruoyi.cai.ws.bean.Room;
import com.ruoyi.cai.ws.bean.RoomData;
import com.ruoyi.cai.ws.bean.UserData;
import com.ruoyi.cai.ws.cache.*;
import com.ruoyi.cai.ws.constant.HangUpEnums;
import com.ruoyi.cai.ws.constant.RoomStatusEnums;
import com.ruoyi.cai.ws.util.MapGetUtil;
import com.ruoyi.cai.ws.util.WebSocketUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
@Slf4j
public class RoomService {
    @Autowired
    private RoomDataCache roomDataCache;
    @Autowired
    private UserDataCache userDataCache;
    @Autowired
    private CallerRoomCache callerRoomCache;
    @Autowired
    private AccountService accountService;
    @Autowired
    private FdCtxDataCache fdCtxDataCache;
    @Autowired
    private RoomCtxCache roomCtxCache;
    @Autowired
    private UserService userService;
    @Autowired
    private AnchorService anchorService;
    @Autowired
    private Agora agora;

    public Room load(Long roomId){
        Room room = new Room();
        RoomData roomData = roomDataCache.getByRoomId(roomId);
        if(roomData == null){
            return null;
        }
        room.setRoomData(roomData);
        UserData callerUserData = userDataCache.getCallerUserDataByRoom(roomId);
        if(callerUserData == null){
            return null;
        }
        room.setCallUserData(callerUserData);
        UserData receiverUserData = userDataCache.getReceiverUserDataByRoom(roomId);
        if(receiverUserData == null){
            return null;
        }
        room.setReceiverUserData(receiverUserData);
        return room;
    }

    public void closeAllRoom(Long fromUid){
        Map<Object, Object> all = callerRoomCache.getAll(fromUid);
        for (Map.Entry<Object, Object> entry : all.entrySet()) {
            Long roomId = MapGetUtil.getLong(entry.getValue());
            Room room = load(roomId);
            if(room == null){
                continue;
            }
            if(room.isCanCall()){
                // 给接收方发送对应已取消
            }else if(room.isOnline()){
                // 对接收方发送对方已挂断
            }
            // 挂掉房间
            roomDataCache.hangUp(roomId);
            // 关闭所有的websocket进程

        }
    }

    public void closeAllFd(Long roomId){
        List<String> sessionKeysByRoomId = roomCtxCache.getSessionKeysByRoomId(roomId);
        for (String sessionKey : sessionKeysByRoomId) {
            WebSocketUtils.close(sessionKey);
        }
        // 删除房间FD,避免重连
        roomCtxCache.del(roomId);
    }

    public void delCallRoom(Long fromUid) {
        callerRoomCache.del(fromUid);
    }
    private static final Map<Integer, CheckConnectionDTO> STATUS_TO_HANG_UP = new HashMap<>();
    static {
        STATUS_TO_HANG_UP.put(RoomStatusEnums.STATUS_CALLER_CANCEL.getCode(),new CheckConnectionDTO(HangUpEnums.CANCEL,"通话已取消"));
        STATUS_TO_HANG_UP.put(RoomStatusEnums.STATUS_CONNECT_CANCEL.getCode(),new CheckConnectionDTO(HangUpEnums.TIMEOUT,"通话未接听"));
        STATUS_TO_HANG_UP.put(RoomStatusEnums.STATUS_REFUSE.getCode(),new CheckConnectionDTO(HangUpEnums.REFUSE,"对方已拒绝"));
        STATUS_TO_HANG_UP.put(RoomStatusEnums.STATUS_TIMEOUT_CANCEL.getCode(),new CheckConnectionDTO(HangUpEnums.REFUSE,"通话已拒绝"));
        STATUS_TO_HANG_UP.put(RoomStatusEnums.STATUS_HANGUP.getCode(),new CheckConnectionDTO(HangUpEnums.OTHER,"通话已结束"));
    }

    public CheckConnectionDTO checkConnect(Room room) {
        CheckConnectionDTO checkConnectionDTO = STATUS_TO_HANG_UP.get(room.getRoomData().getStatus());
        if(checkConnectionDTO != null){
            return checkConnectionDTO;
        }
        if(room.getRoomData().getStatus() > RoomStatusEnums.STATUS_AGREE.getCode()){
            return new CheckConnectionDTO(HangUpEnums.OTHER,"通话已结束");
        }
        return null;
    }

    public Long getCallTime(Room room) {
        RoomData roomData = room.getRoomData();
        Long beginTime = roomData.getBeginTime();
        if(beginTime == null){
            return 0L;
        }
        if(roomData.getHangUpTime() != null){
            return roomData.getHangUpTime() - roomData.getBeginTime();
        }
        return DateUtil.currentSeconds() - beginTime;

    }

    public Long canCallTime(Room room) {
        try {
            Long callId = room.getCallUserData().getId();
            Account account = accountService.getByUserId(callId);
            if(account == null){
                return 0L;
            }
            RoomData roomData = room.getRoomData();
            long blockAmount = roomData.getPayCoin() + roomData.getPayIncome();
            long totalAmount = account.getCoin()+account.getIncomeCoin() + blockAmount;
            long totalSecond = (totalAmount / roomData.getCallPrice()) / 60;
            long useTime = 0;
            if(roomData.getBeginTime() != null){
                useTime = DateUtil.currentSeconds() - roomData.getBeginTime();
            }
            return totalSecond - useTime;
        }catch (Exception e){
            log.error("计算可通话时间失败！",e);
            return 0L;
        }
    }

    /**
     * 释放房间资源
     */
    public void releaseRes(Long roomId) {
        Room room = this.load(roomId);
        if(room == null){
            return;
        }
        try {
            UserData receiverUserData = room.getReceiverUserData();
            Long receiverId = receiverUserData.getId();
            Long callerId = room.getCallUserData().getId();
            // 呼叫方释放资源
            callerRoomCache.delRoom(receiverId,roomId);
            userService.updateVideoStatus(callerId,0);
            // 接收方释放资源（已连接的情况下）
            if(receiverUserData.getConnectTime() != null && receiverUserData.getConnectTime() > 0){
                userService.updateVideoStatus(receiverId,0);
                anchorService.updateVideoStatus(receiverId,0);
            }
            // 声网踢人
            if(room.getRoomData().getBeginTime() != null && room.getRoomData().getBeginTime() > 0){
                ExecutorConstant.ROOM_EXECUTOR.execute(() -> {
                    agora.closeChannel(roomId);
                });
            }
            // 修改释放状态
            Map<String,Object> map = new HashMap<>();
            map.put("releaseRes",1);
            roomDataCache.hMSet(roomId,map);
        }catch (Exception e){
            log.error("释放房间资源失败!房间号：{}",roomId,e);
        }

    }
}
