/**
 * 加密工具模块
 *
 * 功能：
 * - 使用 AES-256-GCM 加密敏感数据（OSS Access Key Secret）
 * - 提供加密和解密函数
 * - 自动处理初始化向量(IV)和认证标签
 *
 * 安全特性：
 * - AES-256-GCM 提供认证加密（AEAD）
 * - 每次加密使用随机 IV，防止模式泄露
 * - 使用认证标签验证数据完整性
 * - 密钥从环境变量读取，不存在硬编码
 *
 * @module utils/encryption
 */

const crypto = require('crypto');

/**
 * 从环境变量获取加密密钥
 *
 * 要求：
 * - 必须是 32 字节的十六进制字符串（64个字符）
 * - 如果未设置或格式错误，启动时抛出错误
 *
 * @returns {Buffer} 32字节的加密密钥
 * @throws {Error} 如果密钥未配置或格式错误
 */
function getEncryptionKey() {
  const keyHex = process.env.ENCRYPTION_KEY;

  if (!keyHex) {
    throw new Error(`
╔═══════════════════════════════════════════════════════════════╗
║                    ⚠️  安全错误  ⚠️                            ║
╠═══════════════════════════════════════════════════════════════╣
║  ENCRYPTION_KEY 未配置！                                      ║
║                                                               ║
║  此密钥用于加密 OSS Access Key Secret 等敏感信息              ║
║                                                               ║
║  生成方法:                                                    ║
║  node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
║                                                               ║
║  在 backend/.env 文件中添加:                                  ║
║  ENCRYPTION_KEY=你生成的64位十六进制密钥                      ║
╚═══════════════════════════════════════════════════════════════╝
    `);
  }

  // 验证密钥格式（必须是64个十六进制字符）
  if (!/^[0-9a-fA-F]{64}$/.test(keyHex)) {
    throw new Error(`
╔═══════════════════════════════════════════════════════════════╗
║                    ⚠️  配置错误  ⚠️                            ║
╠═══════════════════════════════════════════════════════════════╣
║  ENCRYPTION_KEY 格式错误！                                    ║
║                                                               ║
║  要求: 64位十六进制字符串（32字节）                           ║
║  当前长度: ${keyHex.length} 字符                              ║
║                                                               ║
║  正确的生成方法:                                              ║
║  node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
╚═══════════════════════════════════════════════════════════════╝
    `);
  }

  return Buffer.from(keyHex, 'hex');
}

/**
 * 加密明文字符串
 *
 * 使用 AES-256-GCM 算法加密数据，输出格式：
 * - Base64(IV + ciphertext + authTag)
 * - IV: 12字节（随机）
 * - ciphertext: 加密后的数据
 * - authTag: 16字节（认证标签）
 *
 * @param {string} plaintext - 要加密的明文字符串
 * @returns {string} Base64编码的加密结果（包含 IV 和 authTag）
 * @throws {Error} 如果加密失败
 *
 * @example
 * const encrypted = encryptSecret('my-secret-key');
 * // 输出: 'base64-encoded-string-with-iv-and-tag'
 */
function encryptSecret(plaintext) {
  try {
    // 获取加密密钥
    const key = getEncryptionKey();

    // 生成随机初始化向量（IV）
    // GCM 模式推荐 12 字节 IV
    const iv = crypto.randomBytes(12);

    // 创建加密器
    const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);

    // 加密数据
    let encrypted = cipher.update(plaintext, 'utf8', 'binary');
    encrypted += cipher.final('binary');

    // 获取认证标签（用于验证数据完整性）
    const authTag = cipher.getAuthTag();

    // 组合：IV + encrypted + authTag
    const combined = Buffer.concat([
      iv,
      Buffer.from(encrypted, 'binary'),
      authTag
    ]);

    // 返回 Base64 编码的结果
    return combined.toString('base64');
  } catch (error) {
    console.error('[加密] 加密失败:', error);
    throw new Error('数据加密失败: ' + error.message);
  }
}

/**
 * 解密密文字符串
 *
 * 解密由 encryptSecret() 加密的数据
 * 自动验证认证标签，确保数据完整性
 *
 * @param {string} ciphertext - Base64编码的密文（由 encryptSecret 生成）
 * @returns {string} 解密后的明文字符串
 * @throws {Error} 如果解密失败或认证标签验证失败
 *
 * @example
 * const decrypted = decryptSecret(encrypted);
 * // 输出: 'my-secret-key'
 */
function decryptSecret(ciphertext) {
  try {
    // 如果是 null 或 undefined，直接返回
    if (!ciphertext) {
      return ciphertext;
    }

    // 检查是否为加密格式（Base64）
    // 如果不是 Base64，可能是旧数据（明文），直接返回
    if (!/^[A-Za-z0-9+/=]+$/.test(ciphertext)) {
      console.warn('[加密] 检测到未加密的密钥，建议重新加密');
      return ciphertext;
    }

    // 获取加密密钥
    const key = getEncryptionKey();

    // 解析 Base64
    const combined = Buffer.from(ciphertext, 'base64');

    // 提取各部分
    // IV: 前 12 字节
    const iv = combined.slice(0, 12);

    // authTag: 最后 16 字节
    const authTag = combined.slice(-16);

    // ciphertext: 中间部分
    const encrypted = combined.slice(12, -16);

    // 创建解密器
    const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);

    // 设置认证标签
    decipher.setAuthTag(authTag);

    // 解密数据
    let decrypted = decipher.update(encrypted, 'binary', 'utf8');
    decrypted += decipher.final('utf8');

    return decrypted;
  } catch (error) {
    // 如果解密失败，可能是旧数据（明文），直接返回
    console.error('[加密] 解密失败，可能是未加密的旧数据:', error.message);

    // 在开发环境抛出错误，生产环境尝试返回原值
    if (process.env.NODE_ENV === 'production') {
      console.error('[加密] 生产环境中解密失败，返回原值（可能导致 OSS 连接失败）');
      return ciphertext;
    }

    throw new Error('数据解密失败: ' + error.message);
  }
}

/**
 * 验证加密系统是否正常工作
 *
 * 在应用启动时调用，确保：
 * 1. ENCRYPTION_KEY 已配置
 * 2. 加密/解密功能正常
 *
 * @returns {boolean} true 如果验证通过
 * @throws {Error} 如果验证失败
 */
function validateEncryption() {
  try {
    const testData = 'test-secret-123';

    // 测试加密
    const encrypted = encryptSecret(testData);

    // 验证加密结果不为空且不等于原文
    if (!encrypted || encrypted === testData) {
      throw new Error('加密结果异常');
    }

    // 测试解密
    const decrypted = decryptSecret(encrypted);

    // 验证解密结果等于原文
    if (decrypted !== testData) {
      throw new Error('解密结果不匹配');
    }

    console.log('[安全] ✓ 加密系统验证通过');
    return true;
  } catch (error) {
    console.error('[安全] ✗ 加密系统验证失败:', error.message);
    throw error;
  }
}

/**
 * 检查字符串是否已加密
 *
 * 通过格式判断是否为加密数据
 * 注意：这不是加密学验证，仅用于提示
 *
 * @param {string} data - 要检查的数据
 * @returns {boolean} true 如果看起来像是加密数据
 */
function isEncrypted(data) {
  if (!data || typeof data !== 'string') {
    return false;
  }

  // 加密后的数据特征：
  // 1. 是有效的 Base64
  // 2. 长度至少为 (12 + 16) * 4/3 = 38 字符（IV + authTag 的 Base64）
  // 3. 通常会比原文长

  try {
    // 尝试解码 Base64
    const buffer = Buffer.from(data, 'base64');

    // 检查长度（至少包含 IV + authTag）
    // AES-GCM: 12字节IV + 至少1字节密文 + 16字节authTag = 29字节
    // Base64编码后: 29 * 4/3 ≈ 39 字符
    if (buffer.length < 29) {
      return false;
    }

    return true;
  } catch (error) {
    return false;
  }
}

module.exports = {
  encryptSecret,
  decryptSecret,
  validateEncryption,
  isEncrypted,
  getEncryptionKey
};
