
import * as ConnectionRepository from '../repositories/connection.repository';
import * as ProxyRepository from '../repositories/proxy.repository';
import * as TagService from '../services/tag.service'; 
import { getDbInstance, runDb, getDb as getDbRow, allDb } from '../database/connection';
import { decrypt, getEncryptionKeyBuffer as getCryptoKeyBuffer } from '../utils/crypto'; 
import { getAllDecryptedSshKeys, DecryptedSshKeyDetails } from '../services/ssh_key.service'; 
import archiver from 'archiver';
archiver.registerFormat('zip-encrypted', require("archiver-zip-encrypted"));




interface ImportedConnectionData {
    name: string;
    type: 'SSH' | 'RDP' | 'VNC'; 
    host: string;
    port: number;
    username: string;
    auth_method: 'password' | 'key'; // For SSH
    // Plaintext fields for export
    password?: string | null;
    private_key?: string | null;
    passphrase?: string | null;
    // Encrypted fields might still be part of the base ImportedConnectionData if it's used elsewhere
    encrypted_password?: string | null;
    encrypted_private_key?: string | null;
    encrypted_passphrase?: string | null;
    tag_ids?: number[];
    proxy?: {
        name: string;
        type: 'SOCKS5' | 'HTTP';
        host: string;
        port: number;
        username?: string | null;
        auth_method?: 'none' | 'password' | 'key'; // For proxy
        // Plaintext fields for proxy export
        password?: string | null;
        private_key?: string | null; // If proxy uses key auth
        passphrase?: string | null; // If proxy key has passphrase
        // Encrypted fields for proxy
        encrypted_password?: string | null;
        encrypted_private_key?: string | null;
        encrypted_passphrase?: string | null;
    } | null;
}

// This will represent the structure of the data *before* it's put into the JSON for export,
// containing plaintext sensitive info.
interface PlaintextExportConnectionData {
    name: string;
    type: 'SSH' | 'RDP' | 'VNC';
    host: string;
    port: number;
    username: string;
    auth_method: 'password' | 'key'; // SSH auth method
    password?: string | null; // Plaintext password
    private_key?: string | null; // Plaintext private key
    passphrase?: string | null; // Plaintext passphrase for key
    ssh_key_id?: number | null; // +++ Add SSH Key ID +++
    tag_ids?: number[];
    proxy?: {
        name: string;
        type: 'SOCKS5' | 'HTTP';
        host: string;
        port: number;
        username?: string | null;
        auth_method?: 'none' | 'password' | 'key'; // Proxy auth method
        password?: string | null; // Plaintext proxy password
        private_key?: string | null; // Plaintext proxy private key
        passphrase?: string | null; // Plaintext proxy key passphrase
    } | null;
}

export interface ImportResult {
    successCount: number;
    failureCount: number;
    errors: { connectionName?: string; message: string }[];
}


/**
 * 获取所有连接的明文数据以供导出。
 * 敏感信息将被解密。
 */
const getPlaintextConnectionsData = async (): Promise<PlaintextExportConnectionData[]> => {
    try {
        const db = await getDbInstance();

        // Ensure ExportRow reflects the updated FullConnectionData (which now includes 'type')
        type ExportRow = ConnectionRepository.FullConnectionData & { // FullConnectionData 包含 ssh_key_id
             proxy_db_id: number | null;
             proxy_name: string | null;
             proxy_type: 'SOCKS5' | 'HTTP' | null; // Proxy type remains the same
             proxy_host: string | null;
             proxy_port: number | null;
             proxy_username: string | null;
             proxy_auth_method: 'none' | 'password' | 'key' | null;
             proxy_encrypted_password?: string | null;
             proxy_encrypted_private_key?: string | null;
             proxy_encrypted_passphrase?: string | null;
        };


        const connectionsWithProxies = await allDb<ExportRow>(db,
            `SELECT
                c.*,
                p.id as proxy_db_id, p.name as proxy_name, p.type as proxy_type,
                p.host as proxy_host, p.port as proxy_port, p.username as proxy_username,
                p.auth_method as proxy_auth_method,
                p.encrypted_password as proxy_encrypted_password,
                p.encrypted_private_key as proxy_encrypted_private_key,
                p.encrypted_passphrase as proxy_encrypted_passphrase
             FROM connections c
             LEFT JOIN proxies p ON c.proxy_id = p.id
             ORDER BY c.name ASC`
        );


        const tagRows = await allDb<{ connection_id: number, tag_id: number }>(db,
            'SELECT connection_id, tag_id FROM connection_tags'
        );


        const tagsMap: { [connId: number]: number[] } = {};
        tagRows.forEach(row => {
            if (!tagsMap[row.connection_id]) tagsMap[row.connection_id] = [];
            tagsMap[row.connection_id].push(row.tag_id);
        });


        const formattedData: PlaintextExportConnectionData[] = connectionsWithProxies.map(row => {
            // Decrypt main connection sensitive data
            let plainPassword = null;
            if (row.encrypted_password) {
                try { plainPassword = decrypt(row.encrypted_password); }
                catch (e) { console.warn(`解密连接 [${row.name}] 密码失败: ${(e as Error).message}`); }
            }
            let plainPrivateKey = null;
            if (row.encrypted_private_key) {
                try { plainPrivateKey = decrypt(row.encrypted_private_key); }
                catch (e) { console.warn(`解密连接 [${row.name}] 私钥失败: ${(e as Error).message}`); }
            }
            let plainPassphrase = null;
            if (row.encrypted_passphrase) {
                try { plainPassphrase = decrypt(row.encrypted_passphrase); }
                catch (e) { console.warn(`解密连接 [${row.name}] 私钥密码失败: ${(e as Error).message}`); }
            }

            const connection: PlaintextExportConnectionData = {
                name: row.name ?? 'Unnamed',
                type: row.type,
                host: row.host,
                port: row.port,
                username: row.username,
                auth_method: row.auth_method, // Keep auth_method as is
                password: plainPassword,
                private_key: plainPrivateKey,
                passphrase: plainPassphrase,
                ssh_key_id: (row.type === 'SSH' && row.auth_method === 'key') ? row.ssh_key_id : null, // +++ Populate SSH Key ID +++
                tag_ids: tagsMap[row.id] || [],
                proxy: null
            };

            if (row.proxy_db_id && row.proxy_name && row.proxy_type && row.proxy_host && row.proxy_port !== null) {
                // Decrypt proxy sensitive data
                let proxyPlainPassword = null;
                if (row.proxy_encrypted_password) {
                    try { proxyPlainPassword = decrypt(row.proxy_encrypted_password); }
                    catch (e) { console.warn(`解密代理 [${row.proxy_name}] 密码失败: ${(e as Error).message}`); }
                }
                let proxyPlainPrivateKey = null;
                if (row.proxy_encrypted_private_key) {
                    try { proxyPlainPrivateKey = decrypt(row.proxy_encrypted_private_key); }
                    catch (e) { console.warn(`解密代理 [${row.proxy_name}] 私钥失败: ${(e as Error).message}`); }
                }
                let proxyPlainPassphrase = null;
                if (row.proxy_encrypted_passphrase) {
                    try { proxyPlainPassphrase = decrypt(row.proxy_encrypted_passphrase); }
                    catch (e) { console.warn(`解密代理 [${row.proxy_name}] 私钥密码失败: ${(e as Error).message}`); }
                }

                connection.proxy = {
                    name: row.proxy_name,
                    type: row.proxy_type,
                    host: row.proxy_host,
                    port: row.proxy_port,
                    username: row.proxy_username,
                    auth_method: row.proxy_auth_method ?? 'none',
                    password: proxyPlainPassword,
                    private_key: proxyPlainPrivateKey,
                    passphrase: proxyPlainPassphrase,
                };
            }
            return connection;
        });

        return formattedData;

    } catch (err: any) {
        console.error('Service: 获取明文连接数据时出错:', err.message);
        throw new Error(`获取明文连接数据失败: ${err.message}`);
    }
};

/**
 * 导出所有连接配置为一个加密的 ZIP 文件。
 * @param includeSshKeys 是否包含 SSH 密钥
 * @returns Buffer 包含加密的 ZIP 文件内容 (IV + Ciphertext + AuthTag)。
 */

// 辅助函数：安全地转义 CLI 参数，如果参数包含空格或引号，则用双引号括起来
function escapeCliArgument(value: string | number | null | undefined): string {
    if (value === null || value === undefined || String(value).trim() === '') {
        return '""'; // 返回空引号对，而不是空字符串，以保持参数位置
    }
    const strValue = String(value);
    // 如果字符串包含空格，或者已经是引号括起来的，或者包含特殊字符，则需要处理
    // 这里简化处理：如果包含空格或双引号，就用双引号包裹并转义内部双引号
    if (strValue.includes(' ') || strValue.includes('"')) {
        return `"${strValue.replace(/"/g, '\\"')}"`;
    }
    return strValue;
}


export const exportConnectionsAsEncryptedZip = async (includeSshKeys: boolean = false): Promise<Buffer> => {
    try {
        const connectionsData = await getPlaintextConnectionsData(); // This now returns PlaintextExportConnectionData[]
        const allTags = await TagService.getAllTags();
        const allSshKeys = includeSshKeys ? await getAllDecryptedSshKeys() : [];

        const tagsMap = new Map(allTags.map(tag => [tag.id, tag.name]));
        const sshKeysMap = new Map(allSshKeys.map(key => [key.id, key.name]));

        const scriptLines: string[] = [];

        for (const conn of connectionsData) {
            let line = `${conn.username}@${conn.host}:${conn.port}`;

            line += ` -type ${conn.type.toUpperCase()}`;
            if (conn.name && conn.name !== `${conn.username}@${conn.host}`) {
                 line += ` -name ${escapeCliArgument(conn.name)}`;
            }

            if (conn.type === 'SSH') {
                if (conn.auth_method === 'password' && conn.password) {
                    line += ` -p ${escapeCliArgument(conn.password)}`;
                } else if (conn.auth_method === 'key') {
                    // PlaintextExportConnectionData now includes ssh_key_id
                    if (conn.ssh_key_id && sshKeysMap.has(conn.ssh_key_id)) {
                        line += ` -k ${escapeCliArgument(sshKeysMap.get(conn.ssh_key_id)!)}`;
                        // Passphrase for named key is not directly supported in simple script line.
                        // It's assumed the key is usable or passphrase handled by agent.
                    } else if (conn.private_key) {
                        // This case (direct private key without a named ref) is not cleanly exportable to the simple script.
                        console.warn(`Connection ${conn.name} uses an SSH key by content, which cannot be directly represented by '-k <keyname>' in script export.`);
                    }
                }
            } else if ((conn.type === 'RDP' || conn.type === 'VNC') && conn.password) {
                line += ` -p ${escapeCliArgument(conn.password)}`;
            }

            if (conn.proxy) {
                line += ` -proxy-name ${escapeCliArgument(conn.proxy.name)}`;
                line += ` -proxy-type ${conn.proxy.type}`;
                line += ` -proxy-host ${escapeCliArgument(conn.proxy.host)}`;
                line += ` -proxy-port ${conn.proxy.port}`;
                if (conn.proxy.username) {
                    line += ` -proxy-username ${escapeCliArgument(conn.proxy.username)}`;
                }
                if (conn.proxy.auth_method && conn.proxy.auth_method !== 'none') {
                    line += ` -proxy-auth-method ${conn.proxy.auth_method}`;
                    if (conn.proxy.auth_method === 'password' && conn.proxy.password) {
                        line += ` -proxy-password ${escapeCliArgument(conn.proxy.password)}`;
                    } else if (conn.proxy.auth_method === 'key' && conn.proxy.private_key) {
                        line += ` -proxy-key ${escapeCliArgument('key-content-not-exported-for-security')}`;
                        if (conn.proxy.passphrase) {
                            line += ` -proxy-passphrase ${escapeCliArgument(conn.proxy.passphrase)}`;
                        }
                    }
                }
            }

            if (conn.tag_ids && conn.tag_ids.length > 0) {
                const tagNames = conn.tag_ids.map(id => tagsMap.get(id)).filter(name => !!name) as string[];
                if (tagNames.length > 0) {
                    line += ` -tags ${tagNames.map(escapeCliArgument).join(' ')}`;
                }
            }
            
            const connWithNotes = conn as PlaintextExportConnectionData & { notes?: string };
            if (connWithNotes.notes) { // notes is already part of PlaintextExportConnectionData
                 line += ` -note ${escapeCliArgument(connWithNotes.notes)}`;
            }
            
            scriptLines.push(line);
        }

        const connectionsScriptContent = scriptLines.join('\n');

        const zipPassword = process.env.ENCRYPTION_KEY;
        if (!zipPassword || zipPassword.trim() === '') {
            console.error('错误：ENCRYPTION_KEY 环境变量未设置或为空！无法为ZIP文件设置密码。');
            throw new Error('ENCRYPTION_KEY is not set or is empty, cannot password-protect the ZIP file.');
        }
        
        return new Promise<Buffer>((resolve, reject) => {
            const archive = archiver.create('zip-encrypted', {
                zlib: { level: 9 },
                encryptionMethod: 'aes256',
                password: zipPassword
            });

            const buffers: Buffer[] = [];

            archive.on('data', (chunk: Buffer) => {
                buffers.push(chunk);
            });

            archive.on('warning', (err: Error) => {
                console.warn('Archiver warning during export:', err);
            });

            // 'error' event should still be listened to for stream errors
            archive.on('error', (err: Error) => {
                console.error('Archiver stream error during export:', err);
                reject(new Error(`Archiver stream failed during export: ${err.message}`));
            });

            // archive.finalize() returns a promise that resolves when the archive is fully written.
            // No need to listen for 'finish' event separately if we await finalize().

            archive.append(connectionsScriptContent, { name: 'connections.txt' });

            if (includeSshKeys && allSshKeys.length > 0) {
                const sshKeysJsonContent = JSON.stringify(allSshKeys, null, 2);
                archive.append(sshKeysJsonContent, { name: 'ssh_keys.json' });
            }

            archive.finalize()
                .then(() => {
                    console.log('Archiver finalized successfully.');
                    resolve(Buffer.concat(buffers));
                })
                .catch(err => {
                    console.error('Error during archive.finalize():', err);
                    reject(new Error(`Failed to finalize archive: ${err.message}`));
                });
        });

    } catch (error: any) {
        console.error('Service: 导出连接 ZIP (outer try-catch) 时发生意外错误:', error);
        throw new Error(`导出连接 ZIP (archiver) 失败: ${error.message}`);
    }
};


/**
 * 导入连接配置
 * @param fileBuffer Buffer containing the JSON file content
 */
export const importConnections = async (fileBuffer: Buffer): Promise<ImportResult> => {
    let importedData: ImportedConnectionData[];
    try {
        const fileContent = fileBuffer.toString('utf8');
        importedData = JSON.parse(fileContent);
        if (!Array.isArray(importedData)) {
            throw new Error('JSON 文件内容必须是一个数组。');
        }
    } catch (error: any) {
        console.error('Service: 解析导入文件失败:', error);
        throw new Error(`解析 JSON 文件失败: ${error.message}`);
    }

    let successCount = 0;
    let failureCount = 0;
    const errors: { connectionName?: string; message: string }[] = [];
    const db = await getDbInstance();

    try {
        await runDb(db, 'BEGIN TRANSACTION');

        const connectionsToInsert: Array<Omit<ConnectionRepository.FullConnectionData, 'id' | 'created_at' | 'updated_at' | 'last_connected_at'> & { tag_ids?: number[] }> = [];
        const proxyCache: { [key: string]: number } = {}; 


        for (const connData of importedData) {
             try {

                // Validate imported data, including type
                if (!connData.type || !['SSH', 'RDP', 'VNC'].includes(connData.type)) {
                    throw new Error('缺少或无效的连接类型 (type)。');
                }
                if (!connData.name || !connData.host || !connData.port || !connData.username) {
                    throw new Error('缺少必要的连接字段 (name, host, port, username)。');
                }
                // Validate SSH specific fields only if type is SSH
                if (connData.type === 'SSH' && (!connData.auth_method || !['password', 'key'].includes(connData.auth_method))) {
                     throw new Error('SSH 连接缺少有效的认证方式 (auth_method)。');
                }
                // RDP specific validation (e.g., password required) could be added here if needed


                let proxyIdToUse: number | null = null;

                if (connData.proxy) {
                    const proxyData = connData.proxy;
                    if (!proxyData.name || !proxyData.type || !proxyData.host || !proxyData.port) {
                        throw new Error('代理信息不完整 (缺少 name, type, host, port)。');
                    }
                    const cacheKey = `${proxyData.name}-${proxyData.type}-${proxyData.host}-${proxyData.port}`;
                    if (proxyCache[cacheKey]) {
                        proxyIdToUse = proxyCache[cacheKey];
                    } else {
                        const existingProxy = await ProxyRepository.findProxyByNameTypeHostPort(proxyData.name, proxyData.type, proxyData.host, proxyData.port);
                        if (existingProxy) {
                            proxyIdToUse = existingProxy.id;
                        } else {
                            const newProxyData: Omit<ProxyRepository.ProxyData, 'id' | 'created_at' | 'updated_at'> = {
                                name: proxyData.name,
                                type: proxyData.type,
                                host: proxyData.host,
                                port: proxyData.port,
                                username: proxyData.username || null,
                                auth_method: proxyData.auth_method || 'none',
                                encrypted_password: proxyData.encrypted_password || null,
                                encrypted_private_key: proxyData.encrypted_private_key || null,
                                encrypted_passphrase: proxyData.encrypted_passphrase || null,
                            };
                            proxyIdToUse = await ProxyRepository.createProxy(newProxyData);
                            console.log(`Service: 导入连接 ${connData.name}: 新代理 ${proxyData.name} 创建成功 (ID: ${proxyIdToUse})`);
                        }
                        if (proxyIdToUse) proxyCache[cacheKey] = proxyIdToUse; 
                    }
                }

                // Prepare data for repository, ensuring correct auth_method for RDP
                const authMethodForDb = (connData.type === 'RDP' || connData.type === 'VNC') ? 'password' : connData.auth_method!;
                connectionsToInsert.push({
                    name: connData.name,
                    type: connData.type, // Add type
                    host: connData.host,
                    port: connData.port,
                    username: connData.username,
                    auth_method: authMethodForDb, // Use determined auth method
                    encrypted_password: connData.encrypted_password || null,
                    encrypted_private_key: connData.encrypted_private_key || null,
                    encrypted_passphrase: connData.encrypted_passphrase || null,
                    proxy_id: proxyIdToUse,
                    tag_ids: connData.tag_ids || []
                });

            } catch (connError: any) {
                failureCount++;
                errors.push({ connectionName: connData.name || '未知连接', message: connError.message });
                console.warn(`Service: 处理导入连接 "${connData.name || '未知'}" 时出错: ${connError.message}`);
            }
        } 
        let insertedResults: { connectionId: number, originalData: any }[] = [];
        if (connectionsToInsert.length > 0) {

             insertedResults = await ConnectionRepository.bulkInsertConnections(db, connectionsToInsert);
             successCount = insertedResults.length;
        }

        const insertTagSql = `INSERT OR IGNORE INTO connection_tags (connection_id, tag_id) VALUES (?, ?)`; 
        for (const result of insertedResults) {
            const originalTagIds = result.originalData?.tag_ids;
            if (Array.isArray(originalTagIds) && originalTagIds.length > 0) {
                const validTagIds = originalTagIds.filter((id: any) => typeof id === 'number' && id > 0);
                if (validTagIds.length > 0) {
                    const tagPromises = validTagIds.map(tagId =>
                        runDb(db, insertTagSql, [result.connectionId, tagId]).catch(tagError => {
                             console.warn(`Service: 导入连接 ${result.originalData.name}: 关联标签 ID ${tagId} 失败: ${tagError.message}`);
                        })
                    );
                    await Promise.all(tagPromises);
                }
            }
        }



        await runDb(db, 'COMMIT');
        console.log(`Service: 导入事务提交。成功: ${successCount}, 失败: ${failureCount}`);
        return { successCount, failureCount, errors };

    } catch (error: any) {

        console.error('Service: 导入事务处理出错，正在回滚:', error);
        try {
            await runDb(db, 'ROLLBACK');
        } catch (rollbackErr: any) {
            console.error("Service: 回滚事务失败:", rollbackErr);
        }
        failureCount = importedData.length;
        successCount = 0;
        errors.push({ message: `事务处理失败: ${error.message}` });
        return { successCount, failureCount, errors };
    }
};
