package com.qqchen.deploy.backend.deploy.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.qqchen.deploy.backend.deploy.dto.SSHAuditLogDTO;
import com.qqchen.deploy.backend.deploy.entity.SSHAuditLog;
import com.qqchen.deploy.backend.deploy.entity.Server;
import com.qqchen.deploy.backend.deploy.query.SSHAuditLogQuery;
import com.qqchen.deploy.backend.deploy.repository.ISSHAuditLogRepository;
import com.qqchen.deploy.backend.deploy.service.ISSHAuditLogService;
import com.qqchen.deploy.backend.framework.annotation.ServiceType;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.framework.utils.JsonUtils;
import com.qqchen.deploy.backend.system.entity.User;
import com.qqchen.deploy.backend.system.service.IUserService;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SSH审计日志服务实现
 */
@Slf4j
@Service
@ServiceType(ServiceType.Type.DATABASE)
public class SSHAuditLogServiceImpl 
    extends BaseServiceImpl<SSHAuditLog, SSHAuditLogDTO, SSHAuditLogQuery, Long>
    implements ISSHAuditLogService {

    private final ISSHAuditLogRepository auditLogRepository;

    @Resource
    private IUserService userService;
    
    /**
     * 每个sessionId的锁对象，用于防止并发创建重复的审计日志
     * Key: sessionId, Value: 锁对象
     */
    private final ConcurrentHashMap<String, Object> sessionLocks = new ConcurrentHashMap<>();

    public SSHAuditLogServiceImpl(ISSHAuditLogRepository auditLogRepository) {
        this.auditLogRepository = auditLogRepository;
    }
    
    /**
     * 获取指定sessionId的锁对象（如果不存在则创建）
     */
    private Object getSessionLock(String sessionId) {
        return sessionLocks.computeIfAbsent(sessionId, k -> new Object());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createAuditLog(Long userId, Server server, String sessionId, String clientIp, String userAgent) {
        // ⚠️ 关键：使用 sessionId 粒度的锁，确保同一个 sessionId 的创建操作串行执行
        Object lock = getSessionLock(sessionId);
        
        synchronized (lock) {
            log.info("创建SSH审计日志: userId={}, serverId={}, sessionId={}", userId, server.getId(), sessionId);

            // ⚠️ 双重检查：在锁内再次检查是否已存在（防止并发重复创建）
            SSHAuditLog existing = auditLogRepository.findBySessionId(sessionId);
            if (existing != null) {
                log.warn("SSH审计日志已存在，跳过创建: id={}, sessionId={}", existing.getId(), sessionId);
                return existing.getId();
            }

            SSHAuditLog auditLog = new SSHAuditLog();
            
            // 用户信息
            auditLog.setUserId(userId);
            User user = userService.findEntityById(userId);
            if (user != null) {
                auditLog.setUsername(user.getUsername());
            }
            
            // 服务器信息
            auditLog.setServerId(server.getId());
            auditLog.setServerName(server.getServerName());
            auditLog.setServerIp(server.getHostIp());
            
            // 会话信息
            auditLog.setSessionId(sessionId);
            auditLog.setConnectTime(LocalDateTime.now());
            
            // 客户端信息
            auditLog.setClientIp(clientIp);
            auditLog.setUserAgent(userAgent);
            
            // 初始化
            auditLog.setCommandCount(0);
            auditLog.setCommands("[]");
            auditLog.setStatus("CONNECTED");
            
            SSHAuditLog saved = auditLogRepository.save(auditLog);
            log.info("SSH审计日志创建成功: id={}", saved.getId());
            
            return saved.getId();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recordCommand(String sessionId, String command) {
        try {
            SSHAuditLog auditLog = auditLogRepository.findBySessionId(sessionId);
            if (auditLog == null) {
                log.warn("未找到审计日志: sessionId={}", sessionId);
                return;
            }
            
            // 解析现有命令列表
            List<CommandRecord> commands = parseCommands(auditLog.getCommands());
            
            // 添加新命令
            CommandRecord record = new CommandRecord();
            record.setTimestamp(LocalDateTime.now());
            record.setCommand(command);
            commands.add(record);
            
            // 更新
            auditLog.setCommands(toJson(commands));
            auditLog.setCommandCount(commands.size());
            auditLogRepository.save(auditLog);
            
            log.debug("记录命令: sessionId={}, commandCount={}", sessionId, commands.size());
            
        } catch (Exception e) {
            log.error("记录命令失败: sessionId={}", sessionId, e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeAuditLog(String sessionId, String status, String errorMessage) {
        try {
            SSHAuditLog auditLog = auditLogRepository.findBySessionId(sessionId);
            if (auditLog == null) {
                log.warn("未找到审计日志: sessionId={}", sessionId);
                return;
            }
            
            auditLog.setDisconnectTime(LocalDateTime.now());
            auditLog.setStatus(status);
            auditLog.setErrorMessage(errorMessage);
            
            // 计算会话时长
            if (auditLog.getConnectTime() != null) {
                Duration duration = Duration.between(auditLog.getConnectTime(), auditLog.getDisconnectTime());
                auditLog.setDurationSeconds((int) duration.getSeconds());
            }
            
            auditLogRepository.save(auditLog);
            
            log.info("SSH审计日志关闭: sessionId={}, status={}, duration={}秒", 
                     sessionId, status, auditLog.getDurationSeconds());
            
            // ⚠️ 清理锁对象，避免内存泄漏
            sessionLocks.remove(sessionId);
            log.debug("清理sessionId锁对象: sessionId={}", sessionId);
            
        } catch (Exception e) {
            log.error("关闭审计日志失败: sessionId={}", sessionId, e);
        }
    }

    @Override
    public long countUserActiveSessions(Long userId) {
        return auditLogRepository.countByUserIdAndDisconnectTimeIsNull(userId);
    }

    @Override
    public long countUserActiveSessionsForServer(Long userId, Long serverId) {
        return auditLogRepository.countByUserIdAndServerIdAndDisconnectTimeIsNull(userId, serverId);
    }

    /**
     * 解析命令JSON
     */
    private List<CommandRecord> parseCommands(String commandsJson) {
        try {
            if (commandsJson == null || commandsJson.trim().isEmpty() || "[]".equals(commandsJson.trim())) {
                return new ArrayList<>();
            }
            List<CommandRecord> result = JsonUtils.fromJson(commandsJson, new TypeReference<List<CommandRecord>>() {});
            return result != null ? result : new ArrayList<>();
        } catch (Exception e) {
            log.error("解析命令JSON失败", e);
            return new ArrayList<>();
        }
    }

    /**
     * 转换为JSON
     */
    private String toJson(List<CommandRecord> commands) {
        String json = JsonUtils.toJson(commands);
        return json != null ? json : "[]";
    }

    /**
     * 命令记录内部类
     */
    @Data
    private static class CommandRecord {
        private LocalDateTime timestamp;
        private String command;
    }
}
