package com.ruoyi.web.controller.monitor;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.SaTerminalInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.paginate.Page;
import com.ruoyi.common.constants.CacheConstant;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.web.controller.BaseController;
import com.ruoyi.common.web.domain.PageData;
import com.ruoyi.common.web.domain.entity.UserOnline;
import com.ruoyi.common.web.domain.model.LoginUser;
import com.ruoyi.framework.annotation.RyLog;
import org.noear.solon.annotation.*;
import org.noear.solon.auth.annotation.AuthPermissions;
import org.noear.solon.core.handle.Result;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 在线用户监控
 * @author ruoyi
 */
@Controller
@Mapping("monitor/online")
public class UserOnlineController extends BaseController {

    /**
     * 查询在线用户列表
     */
    @AuthPermissions("monitor:online:list")
    @Get
    @Mapping("list")
    public PageData<UserOnline> list(Page<UserOnline> page, UserOnline userOnline) {
        List<UserOnline> userOnlineList = getList(userOnline);
        // 分页
        List<UserOnline> list = userOnlineList.stream()
                .skip((page.getPageNumber() - 1) * page.getPageSize())
                .limit(page.getPageSize())
                .collect(Collectors.toList());
        return getPageData(list).total(userOnlineList.size());
    }

    /**
     * 强退所有用户
     */
    @RyLog(title = "在线用户", businessType = BusinessType.FORCE)
    @AuthPermissions("monitor:online:batchLogout")
    @Delete
    @Mapping("forceLogoutAll/{userIds}")
    public Result<Void> forceLogoutAll(@Path Long[] userIds) {
        for (Long userId : userIds) {
            StpUtil.logout(userId);
        }
        return Result.succeed();
    }

    /**
     * 强退用户
     */
    @AuthPermissions("monitor:online:forceLogout")
    @RyLog(title = "在线用户", businessType = BusinessType.FORCE)
    @Delete
    @Mapping("{token}")
    public Result<Void> forceLogout(@Path String token) {
        StpUtil.logoutByTokenValue(token);
        return Result.succeed();
    }

    private List<UserOnline> getList(UserOnline userOnline) {
        // 获取所有已登录的会话id（同一账号在多个设备登录只算作一个会话id）
        List<String> sessionIdList = StpUtil.searchSessionId("", 0, -1, false);
        List<UserOnline> userOnlineList = new ArrayList<>();
        for (String sessionId : sessionIdList) {
            // 根据会话id，查询对应的 SaSession 对象，此处一个 SaSession 对象即代表一个登录的账号
            SaSession session = StpUtil.getSessionBySessionId(sessionId);
            UserOnline uo = new UserOnline();
            List<UserOnline.Device> devices = new ArrayList<>();
            // 查询这个账号都在哪些设备登录了（terminalList.size()即为登录的设备数）
            List<SaTerminalInfo> terminalList = session.terminalListCopy();
            for (SaTerminalInfo saTerminalInfo : terminalList) {
                // 获取指定 Token 的 Token-Session 对象
                SaSession tokenSession = StpUtil.getTokenSessionByToken(saTerminalInfo.getTokenValue());
                LoginUser loginUser = tokenSession.getModel(CacheConstant.LOGIN_USER_KEY, LoginUser.class);
                uo.setUserid(loginUser.getUserid());
                uo.setUserName(loginUser.getUsername());
                uo.setNickName(loginUser.getSysUser().getNickName());
                uo.setDeptName(loginUser.getSysUser().getDept().getDeptName());
                UserOnline.Device device = uo.new Device();
                device.setToken(tokenSession.getToken());
                device.setDeviceType(saTerminalInfo.getDeviceType());
                device.setLoginIp(loginUser.getIpaddr());
                device.setLoginTime(loginUser.getLoginTime());
                devices.add(device);
            }
            uo.setDevices(devices);
            userOnlineList.add(uo);
        }
        // 根据查询条件检索
        return userOnlineList.stream()
                .filter(online -> StrUtil.isEmpty(userOnline.getUserName()) || online.getUserName().equals(userOnline.getUserName()))
                .filter(online -> StrUtil.isEmpty(userOnline.getDeptName()) || online.getDeptName().equals(userOnline.getDeptName()))
                .collect(Collectors.toList());
    }

}
