import * as ConnectionRepository from '../repositories/connection.repository';
import { encrypt, decrypt } from '../utils/crypto';
import { AuditLogService } from './audit.service';
import * as SshKeyService from './ssh_key.service'; // +++ Import SshKeyService +++
import {
    ConnectionBase,
    ConnectionWithTags,
    CreateConnectionInput,
    UpdateConnectionInput,
    FullConnectionData
} from '../types/connection.types'; // 从集中类型文件导入

export type { ConnectionBase, ConnectionWithTags, CreateConnectionInput, UpdateConnectionInput };


const auditLogService = new AuditLogService(); // 实例化 AuditLogService

/**
 * 获取所有连接（包含标签）
 */
export const getAllConnections = async (): Promise<ConnectionWithTags[]> => {
    // Repository now returns ConnectionWithTags including 'type'
    // Explicit type assertion to ensure compatibility
    return ConnectionRepository.findAllConnectionsWithTags() as Promise<ConnectionWithTags[]>;
};

/**
 * 根据 ID 获取单个连接（包含标签）
 */
export const getConnectionById = async (id: number): Promise<ConnectionWithTags | null> => {
    // Repository now returns ConnectionWithTags including 'type'
    // Explicit type assertion to ensure compatibility
    return ConnectionRepository.findConnectionByIdWithTags(id) as Promise<ConnectionWithTags | null>;
};

/**
 * 创建新连接
 */
export const createConnection = async (input: CreateConnectionInput): Promise<ConnectionWithTags> => {
    // +++ Define a local type alias for clarity, including ssh_key_id +++
    type ConnectionDataForRepo = Omit<FullConnectionData, 'id' | 'created_at' | 'updated_at' | 'last_connected_at' | 'tag_ids'>;

    console.log('[Service:createConnection] Received input:', JSON.stringify(input, null, 2)); // Log input
    // 1. 验证输入 (包含 type)
    // Convert type to uppercase for validation and consistency
    const connectionType = input.type?.toUpperCase() as 'SSH' | 'RDP' | undefined; // Ensure type safety
    if (!connectionType || !['SSH', 'RDP'].includes(connectionType)) {
        throw new Error('必须提供有效的连接类型 (SSH 或 RDP)。');
    }
    if (!input.host || !input.username) {
        throw new Error('缺少必要的连接信息 (host, username)。');
    }
    // Type-specific validation using the uppercase version
    if (connectionType === 'SSH') {
        if (!input.auth_method || !['password', 'key'].includes(input.auth_method)) {
             throw new Error('SSH 连接必须提供有效的认证方式 (password 或 key)。');
        }
        if (input.auth_method === 'password' && !input.password) {
            throw new Error('SSH 密码认证方式需要提供 password。');
        }
        // If using ssh_key_id, private_key is not required in the input
        if (input.auth_method === 'key' && !input.ssh_key_id && !input.private_key) {
            throw new Error('SSH 密钥认证方式需要提供 private_key 或选择一个已保存的密钥 (ssh_key_id)。');
        }
        if (input.auth_method === 'key' && input.ssh_key_id && input.private_key) {
            throw new Error('不能同时提供 private_key 和 ssh_key_id。');
        }
    } else if (connectionType === 'RDP') {
        if (!input.password) {
             throw new Error('RDP 连接需要提供 password。');
        }
        // For RDP, we'll ignore auth_method, private_key, passphrase from input if provided
    }

    // 2. 处理凭证和 ssh_key_id (根据 type)
    let encryptedPassword = null;
    let encryptedPrivateKey = null;
    let encryptedPassphrase = null;
    let sshKeyIdToSave: number | null = null; // +++ Variable for ssh_key_id +++
    // Default to 'password' for DB compatibility, especially for RDP
    let authMethodForDb: 'password' | 'key' = 'password';

    if (connectionType === 'SSH') {
        authMethodForDb = input.auth_method!; // Already validated above
        if (input.auth_method === 'password') {
            encryptedPassword = encrypt(input.password!);
            sshKeyIdToSave = null; // Password auth cannot use ssh_key_id
        } else { // auth_method is 'key'
            if (input.ssh_key_id) {
                // Validate the provided ssh_key_id
                const keyExists = await SshKeyService.getSshKeyDbRowById(input.ssh_key_id);
                if (!keyExists) {
                    throw new Error(`提供的 SSH 密钥 ID ${input.ssh_key_id} 无效或不存在。`);
                }
                sshKeyIdToSave = input.ssh_key_id;
                // When using ssh_key_id, connection's own key fields should be null
                encryptedPrivateKey = null;
                encryptedPassphrase = null;
            } else if (input.private_key) {
                // Encrypt the provided private key and passphrase
                encryptedPrivateKey = encrypt(input.private_key!);
                if (input.passphrase) {
                    encryptedPassphrase = encrypt(input.passphrase);
                }
                sshKeyIdToSave = null; // Ensure ssh_key_id is null if providing key directly
            } else {
                 // This case should be caught by validation above, but as a safeguard:
                 throw new Error('SSH 密钥认证方式内部错误：未提供 private_key 或 ssh_key_id。');
            }
        }
    } else { // RDP (connectionType is 'RDP')
        encryptedPassword = encrypt(input.password!);
        // authMethodForDb remains 'password' for RDP to satisfy DB constraint
        // Ensure SSH specific fields are null for RDP
        encryptedPrivateKey = null;
        encryptedPassphrase = null;
    }

    // 3. 准备仓库数据
    const defaultPort = input.type === 'RDP' ? 3389 : 22;
    // +++ Explicitly type connectionData using the local alias +++
    const connectionData: ConnectionDataForRepo = {
        name: input.name || '',
        type: connectionType,
        host: input.host,
        port: input.port ?? defaultPort, // Use type-specific default port
        username: input.username,
        auth_method: authMethodForDb, // Use determined auth method
        encrypted_password: encryptedPassword,
        encrypted_private_key: encryptedPrivateKey, // Null if using ssh_key_id or RDP
        encrypted_passphrase: encryptedPassphrase, // Null if using ssh_key_id or RDP
        ssh_key_id: sshKeyIdToSave, // +++ Add ssh_key_id +++
        proxy_id: input.proxy_id ?? null,
    };
    // Remove ssh_key_id property if it's null before logging/saving if repository expects exact type match without optional nulls
    const finalConnectionData = { ...connectionData };
    if (finalConnectionData.ssh_key_id === null) {
        delete (finalConnectionData as any).ssh_key_id; // Adjust based on repository function signature if needed
    }
    console.log('[Service:createConnection] Data to be saved:', JSON.stringify(finalConnectionData, null, 2)); // Log data before saving

    // 4. 在仓库中创建连接记录
    // Pass the potentially modified finalConnectionData
    const newConnectionId = await ConnectionRepository.createConnection(finalConnectionData as Omit<ConnectionRepository.FullConnectionData, 'id' | 'created_at' | 'updated_at' | 'last_connected_at' | 'tag_ids'>);

    // 5. 处理标签
    const tagIds = input.tag_ids?.filter(id => typeof id === 'number' && id > 0) ?? [];
    if (tagIds.length > 0) {
        await ConnectionRepository.updateConnectionTags(newConnectionId, tagIds);
    }

    // 6. 记录审计操作
    const newConnection = await getConnectionById(newConnectionId);
    if (!newConnection) {
        // 如果创建成功，这理论上不应该发生
        console.error(`[Audit Log Error] Failed to retrieve connection ${newConnectionId} after creation.`);
        throw new Error('创建连接后无法检索到该连接。');
    }
    auditLogService.logAction('CONNECTION_CREATED', { connectionId: newConnection.id, type: newConnection.type, name: newConnection.name, host: newConnection.host }); // Add type to audit log

    // 7. 返回新创建的带标签的连接
    return newConnection;
};

/**
 * 更新连接信息
 */
export const updateConnection = async (id: number, input: UpdateConnectionInput): Promise<ConnectionWithTags | null> => {
    // 1. 获取当前连接数据（包括加密字段）以进行比较
    const currentFullConnection = await ConnectionRepository.findFullConnectionById(id);
    if (!currentFullConnection) {
        return null; // 未找到连接
    }

    // 2. 准备更新数据
    // Explicitly type dataToUpdate to match the repository's expected input, including ssh_key_id
    const dataToUpdate: Partial<Omit<ConnectionRepository.FullConnectionData & { ssh_key_id?: number | null }, 'id' | 'created_at' | 'last_connected_at' | 'tag_ids'>> = {};
    let needsCredentialUpdate = false;
    // Determine the final type, converting input type to uppercase if provided
    const targetType = input.type?.toUpperCase() as 'SSH' | 'RDP' | undefined || currentFullConnection.type;

    // 更新非凭证字段
    if (input.name !== undefined) dataToUpdate.name = input.name || '';
    // Update type if changed, using the uppercase version
    if (input.type !== undefined && targetType !== currentFullConnection.type) dataToUpdate.type = targetType;
    if (input.host !== undefined) dataToUpdate.host = input.host;
    if (input.port !== undefined) dataToUpdate.port = input.port;
    if (input.username !== undefined) dataToUpdate.username = input.username;
    if (input.proxy_id !== undefined) dataToUpdate.proxy_id = input.proxy_id;
    // Handle ssh_key_id update (can be set to null or a new ID)
    if (input.ssh_key_id !== undefined) dataToUpdate.ssh_key_id = input.ssh_key_id;

    // 处理认证方法更改或凭证更新 (根据 targetType)
    // Use the validated targetType for logic
    if (targetType === 'SSH') {
        const currentAuthMethod = currentFullConnection.auth_method;
        const inputAuthMethod = input.auth_method;

        // Determine the final auth method for SSH
        const finalAuthMethod = inputAuthMethod || currentAuthMethod;
        if (finalAuthMethod !== currentAuthMethod) {
             dataToUpdate.auth_method = finalAuthMethod; // Update auth_method if it changed
        }

        if (finalAuthMethod === 'password') {
            // If switching to password or updating password
            if (input.password !== undefined) { // Check if password was provided in input
                 if (!input.password && finalAuthMethod !== currentAuthMethod) {
                     // Switching to password requires a password
                     throw new Error('切换到密码认证时需要提供 password。');
                 }
                 // Encrypt if password is not empty, otherwise set to null (to clear)
                 dataToUpdate.encrypted_password = input.password ? encrypt(input.password) : null;
                 needsCredentialUpdate = true;
            }
            // When switching to password, clear key fields and ssh_key_id
            if (finalAuthMethod !== currentAuthMethod) {
                dataToUpdate.encrypted_private_key = null;
                dataToUpdate.encrypted_passphrase = null;
                dataToUpdate.ssh_key_id = null; // Clear ssh_key_id when switching to password
            }
        } else { // finalAuthMethod is 'key'
            // Handle ssh_key_id selection or direct key input
            if (input.ssh_key_id !== undefined) {
                // User selected a stored key
                if (input.ssh_key_id === null) {
                    // User explicitly wants to clear the stored key association
                    dataToUpdate.ssh_key_id = null;
                    // If clearing ssh_key_id, we might need a direct key, but validation should handle this?
                    // Or assume clearing means switching back to direct key input (which might be empty)
                    // Let's assume clearing ssh_key_id means we expect a direct key or nothing
                    if (input.private_key === undefined) {
                        // If no direct key provided when clearing ssh_key_id, clear connection's key fields
                        dataToUpdate.encrypted_private_key = null;
                        dataToUpdate.encrypted_passphrase = null;
                    } else {
                        // Encrypt the direct key provided alongside clearing ssh_key_id
                        dataToUpdate.encrypted_private_key = input.private_key ? encrypt(input.private_key) : null;
                        dataToUpdate.encrypted_passphrase = input.passphrase ? encrypt(input.passphrase) : null;
                    }
                } else {
                    // Validate the provided ssh_key_id
                    const keyExists = await SshKeyService.getSshKeyDbRowById(input.ssh_key_id);
                    if (!keyExists) {
                        throw new Error(`提供的 SSH 密钥 ID ${input.ssh_key_id} 无效或不存在。`);
                    }
                    dataToUpdate.ssh_key_id = input.ssh_key_id;
                    // Clear direct key fields when selecting a stored key
                    dataToUpdate.encrypted_private_key = null;
                    dataToUpdate.encrypted_passphrase = null;
                }
                needsCredentialUpdate = true; // Changing key source is a credential update
            } else if (input.private_key !== undefined) {
                // User provided a direct key
                if (!input.private_key && finalAuthMethod !== currentAuthMethod) {
                    // Switching to key requires a private key if not using ssh_key_id
                    throw new Error('切换到密钥认证时需要提供 private_key 或选择一个已保存的密钥。');
                }
                // Encrypt if key is not empty, otherwise set to null (to clear)
                dataToUpdate.encrypted_private_key = input.private_key ? encrypt(input.private_key) : null;
                // Update passphrase only if direct key was provided OR passphrase itself was provided
                if (input.passphrase !== undefined) {
                    dataToUpdate.encrypted_passphrase = input.passphrase ? encrypt(input.passphrase) : null;
                } else if (input.private_key) {
                    // If only private_key is provided, clear passphrase
                    dataToUpdate.encrypted_passphrase = null;
                }
                dataToUpdate.ssh_key_id = null; // Clear ssh_key_id when providing direct key
                needsCredentialUpdate = true;
            } else if (input.passphrase !== undefined && !input.ssh_key_id && currentFullConnection.encrypted_private_key) {
                 // Only passphrase provided, and not using ssh_key_id, and a direct key already exists
                 dataToUpdate.encrypted_passphrase = input.passphrase ? encrypt(input.passphrase) : null;
                 needsCredentialUpdate = true;
            }

            // When switching to key, clear password field
            if (finalAuthMethod !== currentAuthMethod) {
                dataToUpdate.encrypted_password = null;
            }
        }
    } else { // targetType is 'RDP'
        // RDP only uses password
        if (input.password !== undefined) { // Check if password was provided
             // Encrypt if password is not empty, otherwise set to null (to clear)
             dataToUpdate.encrypted_password = input.password ? encrypt(input.password) : null;
             needsCredentialUpdate = true;
        }
        // Ensure SSH specific fields are nullified if switching to RDP or updating RDP
        if (targetType !== currentFullConnection.type || needsCredentialUpdate) {
            dataToUpdate.auth_method = 'password'; // RDP uses password auth method in DB
            dataToUpdate.encrypted_private_key = null;
            dataToUpdate.encrypted_passphrase = null;
            dataToUpdate.ssh_key_id = null; // RDP cannot use ssh_key_id
        }
    }

    // 3. 如果有更改，则更新连接记录
    const hasNonTagChanges = Object.keys(dataToUpdate).length > 0;
    let updatedFieldsForAudit: string[] = []; // 跟踪审计日志的字段
    if (hasNonTagChanges) {
        updatedFieldsForAudit = Object.keys(dataToUpdate); // 在更新调用之前获取字段
        const updated = await ConnectionRepository.updateConnection(id, dataToUpdate);
        if (!updated) {
            // 如果 findFullConnectionById 成功，则不应发生这种情况，但这是良好的实践
            throw new Error('更新连接记录失败。');
        }
    }

    // 4. 如果提供了 tag_ids，则处理标签更新
    if (input.tag_ids !== undefined) {
        const validTagIds = input.tag_ids.filter(tagId => typeof tagId === 'number' && tagId > 0);
        await ConnectionRepository.updateConnectionTags(id, validTagIds);
    }
    // 如果 tag_ids 已更新，则将其添加到审计日志
    if (input.tag_ids !== undefined) {
        updatedFieldsForAudit.push('tag_ids');
    }


    // 5. 如果进行了任何更改，则记录审计操作
    if (hasNonTagChanges || input.tag_ids !== undefined) {
         // Add type to audit log if it was updated
         const auditDetails: any = { connectionId: id, updatedFields: updatedFieldsForAudit };
         if (dataToUpdate.type) {
             auditDetails.newType = dataToUpdate.type;
         }
         auditLogService.logAction('CONNECTION_UPDATED', auditDetails);
    }

    // 6. 获取并返回更新后的连接
    return getConnectionById(id);
};


/**
 * 删除连接
 */
export const deleteConnection = async (id: number): Promise<boolean> => {
    const deleted = await ConnectionRepository.deleteConnection(id);
    if (deleted) {
        // 删除成功后记录审计操作
        auditLogService.logAction('CONNECTION_DELETED', { connectionId: id });
    }
    return deleted;
};

/**
 * 获取连接信息（包含标签）以及解密后的凭证（如果适用）
 * @param id 连接 ID
 * @returns 包含 ConnectionWithTags 和解密后密码/密钥的对象，或 null
 */
export const getConnectionWithDecryptedCredentials = async (
    id: number
): Promise<{ connection: ConnectionWithTags; decryptedPassword?: string; decryptedPrivateKey?: string; decryptedPassphrase?: string } | null> => {
    // 1. 获取完整的连接数据（包含加密字段和可能的 ssh_key_id）
    const fullConnectionDbRow = await ConnectionRepository.findFullConnectionById(id);
    if (!fullConnectionDbRow) {
        console.log(`[Service:getConnWithDecrypt] Connection not found for ID: ${id}`);
        return null;
    }
    // Convert DbRow to the stricter FullConnectionData type expected by the service/types file
    // Handle potential undefined by defaulting to null
    const fullConnection: FullConnectionData = {
        ...fullConnectionDbRow,
        encrypted_password: fullConnectionDbRow.encrypted_password ?? null,
        encrypted_private_key: fullConnectionDbRow.encrypted_private_key ?? null, // May be null if using ssh_key_id
        encrypted_passphrase: fullConnectionDbRow.encrypted_passphrase ?? null, // May be null if using ssh_key_id
        ssh_key_id: fullConnectionDbRow.ssh_key_id ?? null, // +++ Include ssh_key_id +++
        // Ensure other fields match FullConnectionData if necessary
    } as FullConnectionData & { ssh_key_id: number | null }; // Type assertion

    // 2. 获取带标签的连接数据（用于返回给调用者）
    const connectionWithTags: ConnectionWithTags | null = await ConnectionRepository.findConnectionByIdWithTags(id);
    if (!connectionWithTags) {
         // This shouldn't happen if findFullConnectionById succeeded, but good practice to check
         console.error(`[Service:getConnWithDecrypt] Mismatch: Full connection found but tagged connection not found for ID: ${id}`);
         // Consider throwing an error or returning a specific error state
         return null;
    }

    // 3. 解密凭证
    let decryptedPassword: string | undefined = undefined;
    let decryptedPrivateKey: string | undefined = undefined;
    let decryptedPassphrase: string | undefined = undefined;

    try {
        // Decrypt password if method is 'password' and encrypted password exists
        if (fullConnection.auth_method === 'password' && fullConnection.encrypted_password) {
            decryptedPassword = decrypt(fullConnection.encrypted_password);
        }
        // Decrypt key and passphrase if method is 'key'
        else if (fullConnection.auth_method === 'key') {
            if (fullConnection.ssh_key_id) {
                // +++ If using ssh_key_id, fetch and decrypt the stored key +++
                console.log(`[Service:getConnWithDecrypt] Connection ${id} uses stored SSH key ID: ${fullConnection.ssh_key_id}. Fetching key...`);
                const storedKeyDetails = await SshKeyService.getDecryptedSshKeyById(fullConnection.ssh_key_id);
                if (!storedKeyDetails) {
                    // This indicates an inconsistency, as the ssh_key_id should be valid
                    console.error(`[Service:getConnWithDecrypt] Error: Connection ${id} references non-existent SSH key ID ${fullConnection.ssh_key_id}`);
                    throw new Error(`关联的 SSH 密钥 (ID: ${fullConnection.ssh_key_id}) 未找到。`);
                }
                decryptedPrivateKey = storedKeyDetails.privateKey;
                decryptedPassphrase = storedKeyDetails.passphrase;
                console.log(`[Service:getConnWithDecrypt] Successfully fetched and decrypted stored SSH key ${fullConnection.ssh_key_id} for connection ${id}.`);
            } else if (fullConnection.encrypted_private_key) {
                // Decrypt the key stored directly in the connection record
                decryptedPrivateKey = decrypt(fullConnection.encrypted_private_key);
                // Only decrypt passphrase if it exists alongside the direct key
                if (fullConnection.encrypted_passphrase) {
                    decryptedPassphrase = decrypt(fullConnection.encrypted_passphrase);
                }
            } else {
                 console.warn(`[Service:getConnWithDecrypt] Connection ${id} uses key auth but has neither ssh_key_id nor encrypted_private_key.`);
                 // No key available to decrypt
            }
        }
    } catch (error: any) { // Catch decryption or key fetching errors
        console.error(`[Service:getConnWithDecrypt] Failed to decrypt credentials for connection ID ${id}:`, error);
        // Decide how to handle decryption errors. Throw? Return null password?
        // For now, we'll log and continue, returning undefined credentials.
        // Consider throwing an error if credentials are required but decryption fails.
        // Or return a specific error structure: return { error: 'Decryption failed' };
    }

    console.log(`[Service:getConnWithDecrypt] Returning data for ID: ${id}, Auth Method: ${fullConnection.auth_method}`);
    return {
        connection: connectionWithTags,
        decryptedPassword,
        decryptedPrivateKey,
        decryptedPassphrase,
    };
};
// 注意：testConnection、importConnections、exportConnections 逻辑
// 将分别移至 SshService 和 ImportExportService。
