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

import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.enums.SSHFileErrorType;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.ssh.ISSHCommandService;
import com.qqchen.deploy.backend.framework.ssh.SSHCommandServiceFactory;
import com.qqchen.deploy.backend.framework.ssh.websocket.SSHTarget;
import lombok.extern.slf4j.Slf4j;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.SFTPClient;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * SSH文件操作抽象基类（Framework层）
 * 
 * 提供SFTP文件操作的通用能力：
 * - 连接管理（复用现有SSH连接逻辑）
 * - 模板方法模式
 * - 路径安全验证
 * - 统一异常处理
 * - 资源自动释放
 * 
 * 子类只需实现业务逻辑和钩子方法
 */
@Slf4j
public abstract class AbstractSSHFileOperations {
    
    /**
     * SSH命令服务工厂（用于创建连接）
     */
    protected final SSHCommandServiceFactory sshCommandServiceFactory;
    
    /**
     * 构造函数
     * 
     * @param sshCommandServiceFactory SSH命令服务工厂
     */
    protected AbstractSSHFileOperations(SSHCommandServiceFactory sshCommandServiceFactory) {
        this.sshCommandServiceFactory = sshCommandServiceFactory;
    }
    
    // ========== 核心模板方法 ==========
    
    /**
     * 执行文件操作（模板方法）
     * 
     * 统一管理：
     * 1. SSH连接创建
     * 2. SFTP客户端创建
     * 3. 前置钩子
     * 4. 实际操作
     * 5. 后置钩子
     * 6. 异常处理
     * 7. 资源释放
     * 
     * @param target SSH目标
     * @param operation 文件操作
     * @param <T> 返回值类型
     * @return 操作结果
     */
    protected <T> T executeFileOperation(SSHTarget target, FileOperation<T> operation) {
        validateSSHTarget(target);
        
        SSHClient sshClient = null;
        SFTPClient sftpClient = null;
        
        try {
            // 1. 创建SSH连接（复用现有逻辑）
            sshClient = createSSHConnection(target);
            
            // 2. 创建SFTP客户端
            sftpClient = createSFTPClient(sshClient);
            
            // 3. 前置钩子
            beforeFileOperation(target, operation.getName());
            
            // 4. 执行具体操作
            T result = operation.execute(sftpClient);
            
            // 5. 后置钩子
            afterFileOperation(target, operation.getName(), result);
            
            return result;
            
        } catch (Exception e) {
            // 6. 错误处理钩子
            onFileOperationError(target, operation.getName(), e);
            
            // 转换为统一异常
            throw convertException(e, operation.getName());
            
        } finally {
            // 7. 资源释放
            closeQuietly(sftpClient);
            closeQuietly(sshClient);
        }
    }
    
    // ========== SSH/SFTP连接管理 ==========
    
    /**
     * 创建SSH连接（复用现有逻辑）
     */
    protected SSHClient createSSHConnection(SSHTarget target) throws Exception {
        ISSHCommandService sshService = sshCommandServiceFactory.getService(target.getOsType());
        
        String password = null, privateKey = null, passphrase = null;
        
        switch (target.getAuthType()) {
            case PASSWORD:
                if (target.getPassword() == null || target.getPassword().isEmpty()) {
                    throw new BusinessException(ResponseCode.INVALID_PARAM,
                        new Object[]{"SSH密码不能为空"});
                }
                password = target.getPassword();
                break;
                
            case KEY:
                if (target.getPrivateKey() == null || target.getPrivateKey().isEmpty()) {
                    throw new BusinessException(ResponseCode.INVALID_PARAM,
                        new Object[]{"SSH私钥不能为空"});
                }
                privateKey = target.getPrivateKey();
                passphrase = target.getPassphrase();
                break;
                
            default:
                throw new BusinessException(ResponseCode.INVALID_PARAM,
                    new Object[]{"不支持的认证类型: " + target.getAuthType()});
        }
        
        return sshService.createConnection(
            target.getHost(),
            target.getPort(),
            target.getUsername(),
            password,
            privateKey,
            passphrase
        );
    }
    
    /**
     * 从SSH客户端创建SFTP客户端
     */
    protected SFTPClient createSFTPClient(SSHClient sshClient) throws IOException {
        return sshClient.newSFTPClient();
    }
    
    /**
     * 验证SSH目标参数
     */
    protected void validateSSHTarget(SSHTarget target) {
        if (target == null) {
            throw new BusinessException(ResponseCode.INVALID_PARAM, new Object[]{"SSH目标不能为空"});
        }
        if (target.getHost() == null || target.getHost().isEmpty()) {
            throw new BusinessException(ResponseCode.INVALID_PARAM, new Object[]{"主机地址不能为空"});
        }
        if (target.getUsername() == null || target.getUsername().isEmpty()) {
            throw new BusinessException(ResponseCode.INVALID_PARAM, new Object[]{"用户名不能为空"});
        }
        if (target.getAuthType() == null) {
            throw new BusinessException(ResponseCode.INVALID_PARAM, new Object[]{"认证方式不能为空"});
        }
        if (target.getOsType() == null) {
            throw new BusinessException(ResponseCode.INVALID_PARAM, new Object[]{"操作系统类型不能为空"});
        }
    }
    
    // ========== 路径安全验证 ==========
    
    /**
     * 验证路径安全性（防止路径遍历攻击）
     * 
     * @param path 待验证的路径
     * @throws SSHFileException 路径不安全时抛出
     */
    protected void validatePath(String path) {
        if (path == null || path.isEmpty()) {
            throw new SSHFileException(
                SSHFileErrorType.INVALID_PATH,
                ResponseCode.SSH_FILE_INVALID_PATH
            );
        }
        
        // 检查路径遍历攻击
        if (path.contains("..")) {
            throw new SSHFileException(
                SSHFileErrorType.INVALID_PATH,
                ResponseCode.SSH_FILE_INVALID_PATH
            );
        }
        
        // 规范化路径
        try {
            Path normalizedPath = Paths.get(path).normalize();
            String normalized = normalizedPath.toString();
            
            // 确保规范化后的路径仍然是绝对路径（Unix风格）
            if (!normalized.startsWith("/")) {
                throw new SSHFileException(
                    SSHFileErrorType.INVALID_PATH,
                    ResponseCode.SSH_FILE_INVALID_PATH
                );
            }
        } catch (Exception e) {
            throw new SSHFileException(
                SSHFileErrorType.INVALID_PATH,
                ResponseCode.SSH_FILE_INVALID_PATH,
                e
            );
        }
    }
    
    // ========== 钩子方法（子类可选实现） ==========
    
    /**
     * 文件操作前置钩子
     * 
     * @param target SSH目标
     * @param operationName 操作名称
     */
    protected void beforeFileOperation(SSHTarget target, String operationName) {
        log.debug("开始文件操作: operation={}, host={}", operationName, target.getHost());
    }
    
    /**
     * 文件操作后置钩子
     * 
     * @param target SSH目标
     * @param operationName 操作名称
     * @param result 操作结果
     */
    protected void afterFileOperation(SSHTarget target, String operationName, Object result) {
        log.debug("文件操作完成: operation={}, host={}", operationName, target.getHost());
    }
    
    /**
     * 文件操作错误钩子
     * 
     * @param target SSH目标
     * @param operationName 操作名称
     * @param error 异常
     */
    protected void onFileOperationError(SSHTarget target, String operationName, Exception error) {
        log.error("文件操作失败: operation={}, host={}, error={}",
            operationName, target.getHost(), error.getMessage(), error);
    }
    
    // ========== 资源管理 ==========
    
    /**
     * 安静关闭SFTP客户端
     */
    protected void closeQuietly(SFTPClient sftpClient) {
        if (sftpClient != null) {
            try {
                sftpClient.close();
                log.debug("SFTP客户端已关闭");
            } catch (IOException e) {
                log.warn("关闭SFTP客户端失败", e);
            }
        }
    }
    
    /**
     * 安静关闭SSH客户端
     */
    protected void closeQuietly(SSHClient sshClient) {
        if (sshClient != null) {
            try {
                if (sshClient.isConnected()) {
                    sshClient.disconnect();
                    log.debug("SSH客户端已关闭");
                }
            } catch (IOException e) {
                log.warn("关闭SSH客户端失败", e);
            }
        }
    }
    
    // ========== 异常转换 ==========
    
    /**
     * 将各种异常转换为统一的SSHFileException
     */
    protected SSHFileException convertException(Exception e, String operationName) {
        if (e instanceof SSHFileException) {
            return (SSHFileException) e;
        }
        
        String message = e.getMessage();
        if (message == null) {
            message = "未知错误";
        }
        
        // 根据异常消息判断错误类型并返回对应的ResponseCode
        if (message.contains("No such file") || message.contains("not found")) {
            return new SSHFileException(
                SSHFileErrorType.FILE_NOT_FOUND,
                ResponseCode.SSH_FILE_NOT_FOUND,
                e
            );
        } else if (message.contains("Permission denied") || message.contains("Access denied")) {
            return new SSHFileException(
                SSHFileErrorType.PERMISSION_DENIED,
                ResponseCode.SSH_FILE_PERMISSION_DENIED,
                e
            );
        } else if (message.contains("Disk full") || message.contains("No space left")) {
            return new SSHFileException(
                SSHFileErrorType.DISK_FULL,
                ResponseCode.SSH_FILE_DISK_FULL,
                e
            );
        } else if (message.contains("timeout") || message.contains("timed out")) {
            return new SSHFileException(
                SSHFileErrorType.TIMEOUT,
                ResponseCode.SSH_FILE_TIMEOUT,
                e
            );
        } else if (message.contains("Auth fail") || message.contains("Authentication")) {
            return new SSHFileException(
                SSHFileErrorType.AUTHENTICATION_FAILED,
                ResponseCode.SSH_FILE_AUTHENTICATION_FAILED,
                e
            );
        } else if (message.contains("Connection") || message.contains("connect")) {
            return new SSHFileException(
                SSHFileErrorType.CONNECTION_FAILED,
                ResponseCode.SSH_FILE_CONNECTION_FAILED,
                e
            );
        } else {
            return new SSHFileException(
                SSHFileErrorType.UNKNOWN,
                ResponseCode.SSH_FILE_OPERATION_FAILED,
                e
            );
        }
    }
    
    // ========== 内部接口 ==========
    
    /**
     * 文件操作接口（函数式接口）
     */
    @FunctionalInterface
    protected interface FileOperation<T> {
        /**
         * 执行文件操作
         * 
         * @param sftpClient SFTP客户端
         * @return 操作结果
         * @throws Exception 操作失败时抛出
         */
        T execute(SFTPClient sftpClient) throws Exception;
        
        /**
         * 获取操作名称（用于日志和错误信息）
         */
        default String getName() {
            return "文件操作";
        }
    }
}
