/**
 * 网络异常和并发操作测试套件
 *
 * 测试范围：
 * 1. 网络异常处理（超时、断连、OSS连接失败）
 * 2. 并发操作测试（多文件上传、多文件删除、重复提交）
 * 3. 防重复提交测试
 */

const assert = require('assert');
const path = require('path');
const fs = require('fs');

// 测试结果收集器
const testResults = {
  passed: 0,
  failed: 0,
  errors: []
};

// 测试辅助函数
function test(name, fn) {
  try {
    fn();
    testResults.passed++;
    console.log(`  [PASS] ${name}`);
  } catch (error) {
    testResults.failed++;
    testResults.errors.push({ name, error: error.message });
    console.log(`  [FAIL] ${name}: ${error.message}`);
  }
}

async function asyncTest(name, fn) {
  try {
    await fn();
    testResults.passed++;
    console.log(`  [PASS] ${name}`);
  } catch (error) {
    testResults.failed++;
    testResults.errors.push({ name, error: error.message });
    console.log(`  [FAIL] ${name}: ${error.message}`);
  }
}

async function runTests() {

// ============================================================
// 1. OSS 错误格式化测试
// ============================================================

console.log('\n========== 1. OSS 错误格式化测试 ==========\n');

function testOssErrorFormatting() {
  console.log('--- 测试 OSS 错误消息格式化 ---');

  // 模拟 formatOssError 函数
  function formatOssError(error, operation = '操作') {
    const errorMessages = {
      'NoSuchBucket': 'OSS 存储桶不存在，请检查配置',
      'AccessDenied': 'OSS 访问被拒绝，请检查权限配置',
      'InvalidAccessKeyId': 'OSS Access Key 无效，请重新配置',
      'SignatureDoesNotMatch': 'OSS 签名验证失败，请检查 Secret Key',
      'NoSuchKey': '文件或目录不存在',
      'EntityTooLarge': '文件过大，超过了 OSS 允许的最大大小',
      'RequestTimeout': 'OSS 请求超时，请稍后重试',
      'SlowDown': 'OSS 请求过于频繁，请稍后重试',
      'ServiceUnavailable': 'OSS 服务暂时不可用，请稍后重试',
      'InternalError': 'OSS 内部错误，请稍后重试'
    };

    const networkErrors = {
      'ECONNREFUSED': '无法连接到 OSS 服务，请检查网络',
      'ENOTFOUND': 'OSS 服务地址无法解析，请检查 endpoint 配置',
      'ETIMEDOUT': '连接 OSS 服务超时，请检查网络',
      'ECONNRESET': '与 OSS 服务的连接被重置，请重试',
      'EPIPE': '与 OSS 服务的连接中断，请重试',
      'EHOSTUNREACH': '无法访问 OSS 服务主机，请检查网络'
    };

    if (error.name && errorMessages[error.name]) {
      return new Error(`${operation}失败: ${errorMessages[error.name]}`);
    }

    if (error.code && networkErrors[error.code]) {
      return new Error(`${operation}失败: ${networkErrors[error.code]}`);
    }

    if (error.$metadata?.httpStatusCode) {
      const statusCode = error.$metadata.httpStatusCode;
      const statusMessages = {
        400: '请求参数错误',
        401: '认证失败，请检查 Access Key',
        403: '没有权限执行此操作',
        404: '资源不存在',
        429: '请求过于频繁，请稍后重试',
        500: 'OSS 服务内部错误',
        503: 'OSS 服务暂时不可用'
      };
      if (statusMessages[statusCode]) {
        return new Error(`${operation}失败: ${statusMessages[statusCode]}`);
      }
    }

    return new Error(`${operation}失败: ${error.message}`);
  }

  test('NoSuchBucket 错误应该被正确格式化', () => {
    const error = { name: 'NoSuchBucket', message: 'The specified bucket does not exist' };
    const formatted = formatOssError(error, '列出文件');
    assert.ok(formatted.message.includes('存储桶不存在'));
  });

  test('AccessDenied 错误应该被正确格式化', () => {
    const error = { name: 'AccessDenied', message: 'Access Denied' };
    const formatted = formatOssError(error, '上传文件');
    assert.ok(formatted.message.includes('访问被拒绝'));
  });

  test('网络超时错误应该被正确格式化', () => {
    const error = { code: 'ETIMEDOUT', message: 'connect ETIMEDOUT' };
    const formatted = formatOssError(error, '连接');
    assert.ok(formatted.message.includes('超时'));
  });

  test('连接被拒绝错误应该被正确格式化', () => {
    const error = { code: 'ECONNREFUSED', message: 'connect ECONNREFUSED' };
    const formatted = formatOssError(error, '连接');
    assert.ok(formatted.message.includes('无法连接'));
  });

  test('DNS 解析失败应该被正确格式化', () => {
    const error = { code: 'ENOTFOUND', message: 'getaddrinfo ENOTFOUND' };
    const formatted = formatOssError(error, '连接');
    assert.ok(formatted.message.includes('无法解析'));
  });

  test('HTTP 401 错误应该被正确格式化', () => {
    const error = {
      message: 'Unauthorized',
      $metadata: { httpStatusCode: 401 }
    };
    const formatted = formatOssError(error, '认证');
    assert.ok(formatted.message.includes('认证失败'));
  });

  test('HTTP 403 错误应该被正确格式化', () => {
    const error = {
      message: 'Forbidden',
      $metadata: { httpStatusCode: 403 }
    };
    const formatted = formatOssError(error, '访问');
    assert.ok(formatted.message.includes('没有权限'));
  });

  test('HTTP 429 错误（限流）应该被正确格式化', () => {
    const error = {
      message: 'Too Many Requests',
      $metadata: { httpStatusCode: 429 }
    };
    const formatted = formatOssError(error, '请求');
    assert.ok(formatted.message.includes('过于频繁'));
  });

  test('未知错误应该保留原始消息', () => {
    const error = { message: 'Unknown error occurred' };
    const formatted = formatOssError(error, '操作');
    assert.ok(formatted.message.includes('Unknown error occurred'));
  });
}

testOssErrorFormatting();

// ============================================================
// 2. 并发限流测试
// ============================================================

console.log('\n========== 2. 并发限流测试 ==========\n');

async function testConcurrentRateLimiting() {
  console.log('--- 测试并发请求限流 ---');

  // 简化版 RateLimiter
  class RateLimiter {
    constructor(options = {}) {
      this.maxAttempts = options.maxAttempts || 5;
      this.windowMs = options.windowMs || 15 * 60 * 1000;
      this.blockDuration = options.blockDuration || 30 * 60 * 1000;
      this.attempts = new Map();
      this.blockedKeys = new Map();
    }

    isBlocked(key) {
      const blockInfo = this.blockedKeys.get(key);
      if (!blockInfo) return false;
      if (Date.now() > blockInfo.expiresAt) {
        this.blockedKeys.delete(key);
        this.attempts.delete(key);
        return false;
      }
      return true;
    }

    recordFailure(key) {
      const now = Date.now();

      if (this.isBlocked(key)) {
        return { blocked: true, remainingAttempts: 0 };
      }

      let attemptInfo = this.attempts.get(key);
      if (!attemptInfo || now > attemptInfo.windowEnd) {
        attemptInfo = { count: 0, windowEnd: now + this.windowMs };
      }

      attemptInfo.count++;
      this.attempts.set(key, attemptInfo);

      if (attemptInfo.count >= this.maxAttempts) {
        this.blockedKeys.set(key, {
          expiresAt: now + this.blockDuration
        });
        return { blocked: true, remainingAttempts: 0 };
      }

      return {
        blocked: false,
        remainingAttempts: this.maxAttempts - attemptInfo.count
      };
    }

    recordSuccess(key) {
      this.attempts.delete(key);
      this.blockedKeys.delete(key);
    }

    getStats() {
      return {
        activeAttempts: this.attempts.size,
        blockedKeys: this.blockedKeys.size
      };
    }
  }

  await asyncTest('并发失败请求应该正确累计', async () => {
    const limiter = new RateLimiter({ maxAttempts: 5, windowMs: 1000, blockDuration: 1000 });
    const key = 'concurrent-test-1';

    // 模拟并发请求
    const promises = Array(5).fill().map(() =>
      new Promise(resolve => {
        const result = limiter.recordFailure(key);
        resolve(result);
      })
    );

    const results = await Promise.all(promises);

    // 最后一个请求应该触发阻止
    assert.ok(results.some(r => r.blocked), '应该有请求被阻止');
  });

  await asyncTest('不同 IP 的并发请求应该独立计数', async () => {
    const limiter = new RateLimiter({ maxAttempts: 3, windowMs: 1000, blockDuration: 1000 });

    // 模拟来自不同 IP 的请求
    const ips = ['192.168.1.1', '192.168.1.2', '192.168.1.3'];

    for (const ip of ips) {
      limiter.recordFailure(`login:ip:${ip}`);
      limiter.recordFailure(`login:ip:${ip}`);
    }

    // 每个 IP 都应该还有 1 次机会
    for (const ip of ips) {
      const result = limiter.recordFailure(`login:ip:${ip}`);
      assert.strictEqual(result.blocked, true, `IP ${ip} 应该被阻止`);
    }
  });

  await asyncTest('限流器统计应该正确反映状态', async () => {
    const limiter = new RateLimiter({ maxAttempts: 2, windowMs: 1000, blockDuration: 1000 });

    limiter.recordFailure('key1');
    limiter.recordFailure('key2');
    limiter.recordFailure('key2'); // 这会阻止 key2

    const stats = limiter.getStats();
    assert.ok(stats.activeAttempts >= 1, '应该有活动的尝试记录');
    assert.ok(stats.blockedKeys >= 1, '应该有被阻止的 key');
  });
}

await testConcurrentRateLimiting();

// ============================================================
// 3. 文件上传并发测试
// ============================================================

console.log('\n========== 3. 文件上传并发测试 ==========\n');

async function testConcurrentFileOperations() {
  console.log('--- 测试并发文件操作 ---');

  // 模拟文件上传限流器
  class UploadLimiter {
    constructor(maxConcurrent = 5, maxPerHour = 100) {
      this.maxConcurrent = maxConcurrent;
      this.maxPerHour = maxPerHour;
      this.currentUploads = new Map();
      this.hourlyCount = new Map();
    }

    canUpload(userId) {
      const now = Date.now();
      const hourKey = `${userId}:${Math.floor(now / 3600000)}`;

      // 检查小时限制
      const hourlyUsage = this.hourlyCount.get(hourKey) || 0;
      if (hourlyUsage >= this.maxPerHour) {
        return { allowed: false, reason: '每小时上传次数已达上限' };
      }

      // 检查并发限制
      const userUploads = this.currentUploads.get(userId) || 0;
      if (userUploads >= this.maxConcurrent) {
        return { allowed: false, reason: '并发上传数已达上限' };
      }

      return { allowed: true };
    }

    startUpload(userId) {
      const check = this.canUpload(userId);
      if (!check.allowed) {
        return check;
      }

      const now = Date.now();
      const hourKey = `${userId}:${Math.floor(now / 3600000)}`;

      // 增加计数
      this.currentUploads.set(userId, (this.currentUploads.get(userId) || 0) + 1);
      this.hourlyCount.set(hourKey, (this.hourlyCount.get(hourKey) || 0) + 1);

      return { allowed: true };
    }

    endUpload(userId) {
      const current = this.currentUploads.get(userId) || 0;
      if (current > 0) {
        this.currentUploads.set(userId, current - 1);
      }
    }

    getStatus(userId) {
      const now = Date.now();
      const hourKey = `${userId}:${Math.floor(now / 3600000)}`;
      return {
        concurrent: this.currentUploads.get(userId) || 0,
        hourlyUsed: this.hourlyCount.get(hourKey) || 0,
        maxConcurrent: this.maxConcurrent,
        maxPerHour: this.maxPerHour
      };
    }
  }

  await asyncTest('并发上传限制应该生效', async () => {
    const limiter = new UploadLimiter(3, 100);
    const userId = 'user1';

    // 开始 3 个上传
    assert.ok(limiter.startUpload(userId).allowed);
    assert.ok(limiter.startUpload(userId).allowed);
    assert.ok(limiter.startUpload(userId).allowed);

    // 第 4 个应该被拒绝
    const result = limiter.startUpload(userId);
    assert.strictEqual(result.allowed, false);
    assert.ok(result.reason.includes('并发'));
  });

  await asyncTest('完成上传后应该释放并发槽位', async () => {
    const limiter = new UploadLimiter(2, 100);
    const userId = 'user2';

    limiter.startUpload(userId);
    limiter.startUpload(userId);

    // 应该被拒绝
    assert.strictEqual(limiter.startUpload(userId).allowed, false);

    // 完成一个上传
    limiter.endUpload(userId);

    // 现在应该允许
    assert.ok(limiter.startUpload(userId).allowed);
  });

  await asyncTest('每小时上传限制应该生效', async () => {
    const limiter = new UploadLimiter(100, 5); // 最多 5 次每小时
    const userId = 'user3';

    // 上传 5 次
    for (let i = 0; i < 5; i++) {
      limiter.startUpload(userId);
      limiter.endUpload(userId);
    }

    // 第 6 次应该被拒绝
    const result = limiter.startUpload(userId);
    assert.strictEqual(result.allowed, false);
    assert.ok(result.reason.includes('小时'));
  });

  await asyncTest('不同用户的限制应该独立', async () => {
    const limiter = new UploadLimiter(2, 100);

    // 用户 1 达到限制
    limiter.startUpload('userA');
    limiter.startUpload('userA');
    assert.strictEqual(limiter.startUpload('userA').allowed, false);

    // 用户 2 应该不受影响
    assert.ok(limiter.startUpload('userB').allowed);
  });
}

await testConcurrentFileOperations();

// ============================================================
// 4. 防重复提交测试
// ============================================================

console.log('\n========== 4. 防重复提交测试 ==========\n');

async function testDuplicateSubmissionPrevention() {
  console.log('--- 测试防重复提交机制 ---');

  // 简单的请求去重器
  class RequestDeduplicator {
    constructor(windowMs = 1000) {
      this.windowMs = windowMs;
      this.pending = new Map();
    }

    // 生成请求唯一标识
    getRequestKey(userId, action, params) {
      return `${userId}:${action}:${JSON.stringify(params)}`;
    }

    // 检查是否是重复请求
    isDuplicate(userId, action, params) {
      const key = this.getRequestKey(userId, action, params);
      const now = Date.now();

      if (this.pending.has(key)) {
        const lastRequest = this.pending.get(key);
        if (now - lastRequest < this.windowMs) {
          return true;
        }
      }

      this.pending.set(key, now);
      return false;
    }

    // 清除过期记录
    cleanup() {
      const now = Date.now();
      for (const [key, timestamp] of this.pending.entries()) {
        if (now - timestamp > this.windowMs) {
          this.pending.delete(key);
        }
      }
    }
  }

  await asyncTest('快速重复提交应该被检测', async () => {
    const dedup = new RequestDeduplicator(100);

    const isDup1 = dedup.isDuplicate('user1', 'delete', { file: 'test.txt' });
    assert.strictEqual(isDup1, false, '首次请求不应该是重复');

    const isDup2 = dedup.isDuplicate('user1', 'delete', { file: 'test.txt' });
    assert.strictEqual(isDup2, true, '立即重复应该被检测');
  });

  await asyncTest('不同参数的请求不应该被视为重复', async () => {
    const dedup = new RequestDeduplicator(100);

    dedup.isDuplicate('user1', 'delete', { file: 'test1.txt' });
    const isDup = dedup.isDuplicate('user1', 'delete', { file: 'test2.txt' });
    assert.strictEqual(isDup, false, '不同参数不应该是重复');
  });

  await asyncTest('超时后应该允许重新提交', async () => {
    const dedup = new RequestDeduplicator(50);

    dedup.isDuplicate('user1', 'create', { name: 'folder' });

    // 等待超时
    await new Promise(resolve => setTimeout(resolve, 60));

    const isDup = dedup.isDuplicate('user1', 'create', { name: 'folder' });
    assert.strictEqual(isDup, false, '超时后应该允许');
  });

  await asyncTest('不同用户的相同请求不应该冲突', async () => {
    const dedup = new RequestDeduplicator(100);

    dedup.isDuplicate('user1', 'share', { file: 'doc.pdf' });
    const isDup = dedup.isDuplicate('user2', 'share', { file: 'doc.pdf' });
    assert.strictEqual(isDup, false, '不同用户不应该冲突');
  });
}

await testDuplicateSubmissionPrevention();

// ============================================================
// 5. 缓存失效测试
// ============================================================

console.log('\n========== 5. 缓存失效测试 ==========\n');

async function testCacheInvalidation() {
  console.log('--- 测试缓存过期和失效 ---');

  // TTL 缓存类
  class TTLCache {
    constructor(defaultTTL = 3600000) {
      this.cache = new Map();
      this.defaultTTL = defaultTTL;
    }

    set(key, value, ttl = this.defaultTTL) {
      const expiresAt = Date.now() + ttl;
      this.cache.set(key, { value, expiresAt });
    }

    get(key) {
      const item = this.cache.get(key);
      if (!item) return undefined;

      if (Date.now() > item.expiresAt) {
        this.cache.delete(key);
        return undefined;
      }

      return item.value;
    }

    has(key) {
      return this.get(key) !== undefined;
    }

    delete(key) {
      return this.cache.delete(key);
    }

    size() {
      return this.cache.size;
    }

    cleanup() {
      const now = Date.now();
      let cleaned = 0;
      for (const [key, item] of this.cache.entries()) {
        if (now > item.expiresAt) {
          this.cache.delete(key);
          cleaned++;
        }
      }
      return cleaned;
    }
  }

  await asyncTest('缓存应该在 TTL 内有效', async () => {
    const cache = new TTLCache(100);
    cache.set('key1', 'value1');
    assert.strictEqual(cache.get('key1'), 'value1');
  });

  await asyncTest('缓存应该在 TTL 后过期', async () => {
    const cache = new TTLCache(50);
    cache.set('key2', 'value2');

    await new Promise(resolve => setTimeout(resolve, 60));

    assert.strictEqual(cache.get('key2'), undefined);
  });

  await asyncTest('手动删除应该立即生效', async () => {
    const cache = new TTLCache(10000);
    cache.set('key3', 'value3');
    cache.delete('key3');
    assert.strictEqual(cache.get('key3'), undefined);
  });

  await asyncTest('cleanup 应该清除所有过期项', async () => {
    const cache = new TTLCache(50);
    cache.set('a', 1);
    cache.set('b', 2);
    cache.set('c', 3);

    await new Promise(resolve => setTimeout(resolve, 60));

    const cleaned = cache.cleanup();
    assert.strictEqual(cleaned, 3);
    assert.strictEqual(cache.size(), 0);
  });

  await asyncTest('不同 TTL 的项应该分别过期', async () => {
    const cache = new TTLCache(1000);
    cache.set('short', 'value', 30);
    cache.set('long', 'value', 1000);

    await new Promise(resolve => setTimeout(resolve, 50));

    assert.strictEqual(cache.get('short'), undefined, '短 TTL 应该过期');
    assert.strictEqual(cache.get('long'), 'value', '长 TTL 应该有效');
  });
}

await testCacheInvalidation();

// ============================================================
// 6. 超时处理测试
// ============================================================

console.log('\n========== 6. 超时处理测试 ==========\n');

async function testTimeoutHandling() {
  console.log('--- 测试请求超时处理 ---');

  // 带超时的 Promise 包装器
  function withTimeout(promise, ms, errorMessage = '操作超时') {
    let timeoutId;
    const timeoutPromise = new Promise((_, reject) => {
      timeoutId = setTimeout(() => {
        reject(new Error(errorMessage));
      }, ms);
    });

    return Promise.race([promise, timeoutPromise]).finally(() => {
      clearTimeout(timeoutId);
    });
  }

  await asyncTest('快速操作应该成功完成', async () => {
    const fastOperation = new Promise(resolve => {
      setTimeout(() => resolve('success'), 10);
    });

    const result = await withTimeout(fastOperation, 100);
    assert.strictEqual(result, 'success');
  });

  await asyncTest('慢速操作应该触发超时', async () => {
    const slowOperation = new Promise(resolve => {
      setTimeout(() => resolve('success'), 200);
    });

    try {
      await withTimeout(slowOperation, 50);
      assert.fail('应该抛出超时错误');
    } catch (error) {
      assert.ok(error.message.includes('超时'));
    }
  });

  await asyncTest('自定义超时消息应该正确显示', async () => {
    const slowOperation = new Promise(resolve => {
      setTimeout(() => resolve('success'), 200);
    });

    try {
      await withTimeout(slowOperation, 50, 'OSS 连接超时');
    } catch (error) {
      assert.ok(error.message.includes('OSS'));
    }
  });

  await asyncTest('超时后原始 Promise 的完成不应该影响结果', async () => {
    let completed = false;
    const operation = new Promise(resolve => {
      setTimeout(() => {
        completed = true;
        resolve('done');
      }, 100);
    });

    try {
      await withTimeout(operation, 20);
    } catch (error) {
      // 超时了
    }

    // 等待原始 Promise 完成
    await new Promise(resolve => setTimeout(resolve, 150));
    assert.ok(completed, '原始 Promise 应该完成');
  });
}

await testTimeoutHandling();

// ============================================================
// 7. 重试机制测试
// ============================================================

console.log('\n========== 7. 重试机制测试 ==========\n');

async function testRetryMechanism() {
  console.log('--- 测试操作重试机制 ---');

  // 带重试的函数执行器
  async function withRetry(fn, options = {}) {
    const {
      maxAttempts = 3,
      delayMs = 100,
      backoff = 1.5,
      shouldRetry = (error) => true
    } = options;

    let lastError;
    let delay = delayMs;

    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      try {
        return await fn();
      } catch (error) {
        lastError = error;

        if (attempt === maxAttempts || !shouldRetry(error)) {
          throw error;
        }

        await new Promise(resolve => setTimeout(resolve, delay));
        delay *= backoff;
      }
    }

    throw lastError;
  }

  await asyncTest('成功操作不应该重试', async () => {
    let attempts = 0;
    const result = await withRetry(async () => {
      attempts++;
      return 'success';
    });

    assert.strictEqual(result, 'success');
    assert.strictEqual(attempts, 1);
  });

  await asyncTest('失败操作应该重试指定次数', async () => {
    let attempts = 0;

    try {
      await withRetry(async () => {
        attempts++;
        throw new Error('always fail');
      }, { maxAttempts: 3, delayMs: 10 });
    } catch (error) {
      // 预期会失败
    }

    assert.strictEqual(attempts, 3);
  });

  await asyncTest('重试后成功应该返回结果', async () => {
    let attempts = 0;
    const result = await withRetry(async () => {
      attempts++;
      if (attempts < 3) {
        throw new Error('not yet');
      }
      return 'finally success';
    }, { maxAttempts: 5, delayMs: 10 });

    assert.strictEqual(result, 'finally success');
    assert.strictEqual(attempts, 3);
  });

  await asyncTest('shouldRetry 为 false 时不应该重试', async () => {
    let attempts = 0;

    try {
      await withRetry(async () => {
        attempts++;
        const error = new Error('fatal');
        error.code = 'FATAL';
        throw error;
      }, {
        maxAttempts: 5,
        delayMs: 10,
        shouldRetry: (error) => error.code !== 'FATAL'
      });
    } catch (error) {
      // 预期会失败
    }

    assert.strictEqual(attempts, 1, '不应该重试 FATAL 错误');
  });
}

await testRetryMechanism();

// ============================================================
// 测试总结
// ============================================================

console.log('\n========================================');
console.log('测试总结');
console.log('========================================');
console.log(`通过: ${testResults.passed}`);
console.log(`失败: ${testResults.failed}`);

if (testResults.errors.length > 0) {
  console.log('\n失败的测试:');
  testResults.errors.forEach((e, i) => {
    console.log(`  ${i + 1}. ${e.name}: ${e.error}`);
  });
}

console.log('\n');

return testResults;
}

// 运行测试
runTests().then(testResults => {
  process.exit(testResults.failed > 0 ? 1 : 0);
}).catch(err => {
  console.error('测试执行错误:', err);
  process.exit(1);
});
