package com.qqchen.deploy.backend.framework.ssh;

import com.qqchen.deploy.backend.framework.dto.CommandResult;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
import net.schmizz.sshj.userauth.password.PasswordUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * SSH命令服务抽象基类（Framework层）
 * 提供通用的SSH连接和命令执行能力
 * 
 * 子类只需实现OS特定的命令逻辑
 */
@Slf4j
public abstract class AbstractSSHCommandService implements ISSHCommandService {
    
    private static final int DEFAULT_TIMEOUT_MS = 10000;
    private static final int COMMAND_TIMEOUT_SECONDS = 5;
    
    @Override
    public SSHClient createConnection(String host, Integer port, String username,
                                      String password, String privateKey, String passphrase) throws Exception {
        SSHClient sshClient = new SSHClient();
        
        try {
            // 配置SSH客户端
            // TODO: 【安全改进】生产环境应使用更安全的主机密钥验证方式
            sshClient.addHostKeyVerifier(new PromiscuousVerifier());
            sshClient.setTimeout(DEFAULT_TIMEOUT_MS);
            sshClient.setConnectTimeout(DEFAULT_TIMEOUT_MS);
            
            // 连接服务器
            int sshPort = port != null ? port : 22;
            log.debug("连接SSH服务器: {}:{}", host, sshPort);
            sshClient.connect(host, sshPort);
            
            // 认证
            if (privateKey != null && !privateKey.isEmpty()) {
                // 密钥认证
                KeyProvider keyProvider;
                if (passphrase != null && !passphrase.isEmpty()) {
                    keyProvider = sshClient.loadKeys(privateKey, null,
                        PasswordUtils.createOneOff(passphrase.toCharArray()));
                } else {
                    keyProvider = sshClient.loadKeys(privateKey, null, null);
                }
                sshClient.authPublickey(username, keyProvider);
                log.debug("使用密钥认证成功: {}", username);
            } else if (password != null && !password.isEmpty()) {
                // 密码认证
                sshClient.authPassword(username, password);
                log.debug("使用密码认证成功: {}", username);
            } else {
                throw new BusinessException(ResponseCode.INVALID_PARAM,
                    new Object[]{"密码和私钥不能同时为空"});
            }
            
            return sshClient;
            
        } catch (net.schmizz.sshj.userauth.UserAuthException e) {
            // 认证失败
            closeConnection(sshClient);
            log.error("SSH认证失败: {}@{}:{}", username, host, port);
            throw new BusinessException(ResponseCode.SSH_FILE_AUTHENTICATION_FAILED);
        } catch (net.schmizz.sshj.transport.TransportException e) {
            // 连接失败（网络问题、主机不可达等）
            closeConnection(sshClient);
            log.error("SSH连接失败: {}@{}:{}, 原因: {}", username, host, port, e.getMessage());
            throw new BusinessException(ResponseCode.SSH_FILE_CONNECTION_FAILED);
        } catch (java.net.UnknownHostException e) {
            // 主机不存在
            closeConnection(sshClient);
            log.error("SSH主机不存在: {}", host);
            throw new BusinessException(ResponseCode.SSH_FILE_CONNECTION_FAILED, 
                new Object[]{"主机地址无效或不可达"});
        } catch (java.net.SocketTimeoutException e) {
            // 连接超时
            closeConnection(sshClient);
            log.error("SSH连接超时: {}@{}:{}", username, host, port);
            throw new BusinessException(ResponseCode.SSH_FILE_TIMEOUT);
        } catch (BusinessException e) {
            // 业务异常直接抛出
            closeConnection(sshClient);
            throw e;
        } catch (Exception e) {
            // 其他未知异常
            closeConnection(sshClient);
            log.error("SSH连接失败: {}@{}:{}", username, host, port, e);
            throw new BusinessException(ResponseCode.SSH_FILE_CONNECTION_FAILED, 
                new Object[]{e.getMessage()});
        }
    }
    
    @Override
    public CommandResult executeCommand(SSHClient sshClient, String command) throws Exception {
        try (Session session = sshClient.startSession()) {
            Session.Command cmd = session.exec(command);
            cmd.join(COMMAND_TIMEOUT_SECONDS, TimeUnit.SECONDS);
            
            Integer exitStatus = cmd.getExitStatus();
            
            if (exitStatus == 0) {
                // 命令执行成功
                String output = new String(cmd.getInputStream().readAllBytes()).trim();
                log.debug("命令执行成功: {} -> {}", command, output);
                return CommandResult.success(output);
            } else {
                // 命令执行失败
                String error = new String(cmd.getErrorStream().readAllBytes()).trim();
                log.warn("命令执行失败: exitCode={}, command={}, error={}", exitStatus, command, error);
                return CommandResult.failure(error, exitStatus);
            }
        } catch (IOException e) {
            log.error("执行SSH命令异常: {}", command, e);
            throw new BusinessException(ResponseCode.ERROR,
                new Object[]{"执行SSH命令异常: " + e.getMessage()});
        }
    }
    
    @Override
    public List<CommandResult> executeCommands(SSHClient sshClient, List<String> commands) throws Exception {
        if (commands == null || commands.isEmpty()) {
            return new ArrayList<>();
        }
        
        log.debug("批量执行{}个命令", commands.size());
        
        List<CommandResult> results = new ArrayList<>();
        
        // 循环执行每个命令，每个命令独立执行（一个失败不影响其他）
        for (int i = 0; i < commands.size(); i++) {
            String command = commands.get(i);
            try {
                CommandResult result = executeCommand(sshClient, command);
                results.add(result);
                
                if (result.isSuccess()) {
                    log.debug("命令[{}/{}]执行成功: {}", i + 1, commands.size(), command);
                } else {
                    log.warn("命令[{}/{}]执行失败: exitCode={}, command={}", 
                        i + 1, commands.size(), result.getExitCode(), command);
                }
            } catch (Exception e) {
                // 命令执行异常（连接问题等），记录失败结果
                log.error("命令[{}/{}]执行异常: {}", i + 1, commands.size(), command, e);
                results.add(CommandResult.failure(e.getMessage(), -1));
            }
        }
        
        log.debug("批量命令执行完成: 总数={}, 成功={}, 失败={}", 
            commands.size(),
            results.stream().filter(CommandResult::isSuccess).count(),
            results.stream().filter(r -> !r.isSuccess()).count());
        
        return results;
    }
    
    @Override
    public void closeConnection(SSHClient sshClient) {
        if (sshClient != null) {
            try {
                if (sshClient.isConnected()) {
                    sshClient.disconnect();
                    log.debug("SSH连接已关闭");
                }
            } catch (IOException e) {
                log.error("关闭SSH连接失败", e);
            }
        }
    }
    
    /**
     * 安全执行命令（子类使用）
     * 返回命令输出，失败时返回null
     */
    protected String safeExecute(SSHClient sshClient, String command) {
        try {
            CommandResult result = executeCommand(sshClient, command);
            return result.isSuccess() ? result.getOutput() : null;
        } catch (Exception e) {
            log.warn("执行命令失败: {}", command, e);
            return null;
        }
    }
    
    /**
     * 解析整数结果
     */
    protected Integer parseInteger(String value) {
        if (value == null || value.isEmpty()) {
            return null;
        }
        try {
            return Integer.parseInt(value.trim());
        } catch (NumberFormatException e) {
            log.warn("解析整数失败: {}", value);
            return null;
        }
    }
    
    /**
     * 解析BigDecimal结果
     */
    protected java.math.BigDecimal parseBigDecimal(String value) {
        if (value == null || value.isEmpty()) {
            return null;
        }
        try {
            return new java.math.BigDecimal(value.trim());
        } catch (NumberFormatException e) {
            log.warn("解析BigDecimal失败: {}", value);
            return null;
        }
    }
}
