package cd.casic.module.terminal.host.terminal.handler;

import cd.casic.framework.security.core.LoginUser;
import cd.casic.framework.websocket.core.util.WebSocketFrameworkUtils;
import cd.casic.module.machine.dal.dataobject.MachineInfoDO;
import cd.casic.module.machine.dal.mysql.MachineInfoMapper;
import cd.casic.module.terminal.common.*;
import cd.casic.module.terminal.dal.dataobject.TerminalConnectLogDO;
import cd.casic.module.terminal.define.operator.TerminalOperatorType;

import cd.casic.module.terminal.controller.dto.TerminalConnectDTO;
import cd.casic.module.terminal.enums.TerminalConnectTypeEnum;
import cd.casic.module.terminal.host.terminal.constant.TerminalMessage;
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
import cd.casic.module.terminal.host.terminal.enums.TerminalConnectStatusEnum;
import cd.casic.module.terminal.host.terminal.model.request.TerminalCheckRequest;
import cd.casic.module.terminal.host.terminal.model.request.host.TerminalConnectLogCreateRequest;
import cd.casic.module.terminal.host.terminal.model.response.TerminalCheckResponse;
import cd.casic.module.terminal.host.terminal.session.ITerminalSession;
import cd.casic.module.terminal.host.terminal.utils.TerminalUtils;
import cd.casic.module.terminal.service.HostConnectService;
import cd.casic.module.terminal.service.TerminalConnectLogService;
import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException;
import cn.orionsec.kit.lang.utils.Exceptions;
import cn.orionsec.kit.lang.utils.collect.Maps;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.DisabledException;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
import java.util.Map;

/**
 * 终端连接检查
 *
 * @author Yuru Pu
 * @version 1.0
 * @since 2025/7/21 14:17
 */
@Slf4j
@Component
public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckRequest> {

    @Resource
    private MachineInfoMapper machineInfoMapper;

    @Resource
    private HostConnectService hostConnectService;

    @Resource
    private TerminalConnectLogService terminalConnectLogService;


    @Override
    public void handle(WebSocketSession channel, TerminalCheckRequest payload) {
        Long hostId = payload.getHostId();

        LoginUser loginUser = WebSockets.getAttr(channel, WebSocketFrameworkUtils.ATTRIBUTE_LOGIN_USER);
        Long userId = loginUser.getId();
        long startTime = System.currentTimeMillis();
        TerminalConnectTypeEnum connectType = TerminalConnectTypeEnum.of(payload.getConnectType());
        String sessionId = payload.getSessionId();
        log.info("TerminalCheckHandler-handle start userId: {}, hostId: {}, sessionId: {}", loginUser.getId(), hostId, sessionId);
        // 检查 session 是否存在
        if (this.checkSession(channel, payload)) {
            log.info("TerminalCheckHandler-handle present session userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
            return;
        }
        // 获取主机信息
        MachineInfoDO host = this.checkHost(channel, payload, hostId);
        if (host == null) {
            log.info("TerminalCheckHandler-handle unknown host userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
            return;
        }
        TerminalConnectDTO connect = null;
        Exception ex = null;
        try {
            // 获取连接信息
            connect = hostConnectService.getSshConnectInfo(host,userId);
            connect.setConnectType(connectType.name());
            // 设置到缓存中
            channel.getAttributes().put(sessionId, connect);
            log.info("TerminalCheckHandler-handle success userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
        } catch (InvalidArgumentException e) {
            ex = e;
            log.error("TerminalCheckHandler-handle start error userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId, e);
        } catch (DisabledException e) {
            ex = Exceptions.runtime(TerminalMessage.CONFIG_DISABLED);
            log.error("TerminalCheckHandler-handle disabled error userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
        } catch (Exception e) {
            ex = Exceptions.runtime(TerminalMessage.CONNECTION_FAILED);
            log.error("TerminalCheckHandler-handle exception userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId, e);
        }
        // 记录主机日志
        TerminalConnectLogDO connectLog = this.saveHostLog(channel, userId, host, startTime, ex, sessionId, connectType);
        if (connect != null) {
            connect.setLogId(connectLog.getId());
        }
        // 响应检查结果
        this.send(channel,
                OutputTypeEnum.CHECK,
                TerminalCheckResponse.builder()
                        .sessionId(payload.getSessionId())
                        .result(BooleanBit.of(ex == null).getValue())
                        .msg(ex == null ? null : ex.getMessage())
                        .build());
    }

    /**
     * 检查会话是否存在
     *
     * @param channel channel
     * @param payload payload
     * @return 是否存在
     */
    private boolean checkSession(WebSocketSession channel, TerminalCheckRequest payload) {
        ITerminalSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
        if (session != null) {
            this.sendCheckFailedMessage(channel, payload, ErrorMessage.SESSION_PRESENT);
            return true;
        }
        return false;
    }

    /**
     * 获取主机信息
     *
     * @param channel channel
     * @param payload payload
     * @param hostId  hostId
     * @return host
     */
    private MachineInfoDO checkHost(WebSocketSession channel, TerminalCheckRequest payload, Long hostId) {
        // 查询主机信息
        MachineInfoDO machineDO = machineInfoMapper.selectById(hostId);
//        HostMyDO host = hostDAO.selectById(hostId);
        // 不存在返回错误信息
        if (machineDO == null) {
            this.sendCheckFailedMessage(channel, payload, ErrorMessage.HOST_ABSENT);
        }
        return machineDO;
    }

    /**
     * 发送检查失败消息
     *
     * @param channel channel
     * @param payload payload
     * @param msg     msg
     */
    private void sendCheckFailedMessage(WebSocketSession channel, TerminalCheckRequest payload, String msg) {
        TerminalCheckResponse resp = TerminalCheckResponse.builder()
                .sessionId(payload.getSessionId())
                .result(BooleanBit.FALSE.getValue())
                .msg(msg)
                .build();
        // 发送
        this.send(channel, OutputTypeEnum.CHECK, resp);
    }

    /**
     * 记录主机日志
     *
     * @param channel     channel
     * @param userId      userId
     * @param host        host
     * @param startTime   startTime
     * @param ex          ex
     * @param sessionId   sessionId
     * @param connectType connectType
     * @return connectLog
     */
    private TerminalConnectLogDO saveHostLog(WebSocketSession channel,
                                             Long userId,
                                             MachineInfoDO host,
                                             long startTime,
                                             Exception ex,
                                             String sessionId,
                                             TerminalConnectTypeEnum connectType) {
        Long hostId = host.getId();
        String hostName = host.getName();
        String username = WebSockets.getAttr(channel, ExtraFieldConst.USERNAME);
        // 额外参数
        Map<String, Object> extra = Maps.newMap();
        extra.put(OperatorLogs.HOST_ID, hostId);
        extra.put(OperatorLogs.HOST_NAME, hostName);
        extra.put(OperatorLogs.CONNECT_TYPE, connectType.name());
        extra.put(OperatorLogs.CHANNEL_ID, channel.getId());
        extra.put(OperatorLogs.SESSION_ID, sessionId);
        // 日志参数
        OperatorLogModel logModel = TerminalUtils.getOperatorLogModel(channel, extra,
                TerminalOperatorType.CONNECT, startTime, ex);
        // 记录操作日志
//        operatorLogFrameworkService.insert(logModel);
        // 记录连接日志
        TerminalConnectLogCreateRequest connectLog = TerminalConnectLogCreateRequest.builder()
                .userId(userId)
                .username(username)
                .hostId(hostId)
                .hostName(hostName)
                .hostAddress(host.getHostIp())
                .status(ex == null ? TerminalConnectStatusEnum.CONNECTING.name() : TerminalConnectStatusEnum.FAILED.name())
                .sessionId(sessionId)
                .extra(extra)
                .build();
        // 记录连接日志
        return terminalConnectLogService.create(connectType, connectLog);
    }

}
