/**
 * 分享功能边界条件深度测试（兼容 Cookie + CSRF）
 *
 * 测试场景：
 * 1. 已过期的分享
 * 2. 分享文件不存在
 * 3. 被封禁用户的分享
 * 4. 路径遍历攻击
 * 5. 特殊字符路径
 * 6. 并发密码尝试
 * 7. 分享统计
 * 8. 分享码唯一性
 * 9. 过期时间格式
 */

const http = require('http');
const https = require('https');
const bcrypt = require('bcryptjs');
const { db, ShareDB, UserDB } = require('./database');

const BASE_URL = process.env.TEST_BASE_URL || 'http://127.0.0.1:40001';

const results = {
  passed: 0,
  failed: 0,
  errors: []
};

const adminSession = {
  cookies: {},
  csrfToken: ''
};

let adminUserId = 1;

function makeCookieHeader(cookies) {
  return Object.entries(cookies || {})
    .map(([k, v]) => `${k}=${v}`)
    .join('; ');
}

function storeSetCookies(session, setCookieHeader) {
  if (!session || !setCookieHeader) return;
  const list = Array.isArray(setCookieHeader) ? setCookieHeader : [setCookieHeader];
  for (const raw of list) {
    const first = String(raw || '').split(';')[0];
    const idx = first.indexOf('=');
    if (idx <= 0) continue;
    const key = first.slice(0, idx).trim();
    const value = first.slice(idx + 1).trim();
    session.cookies[key] = value;
    if (key === 'csrf_token') {
      session.csrfToken = value;
    }
  }
}

function isSafeMethod(method) {
  const upper = String(method || '').toUpperCase();
  return upper === 'GET' || upper === 'HEAD' || upper === 'OPTIONS';
}

function request(method, path, options = {}) {
  const {
    data = null,
    session = null,
    headers = {},
    requireCsrf = true
  } = options;

  return new Promise((resolve, reject) => {
    const url = new URL(path, BASE_URL);
    const transport = url.protocol === 'https:' ? https : http;

    const requestHeaders = { ...headers };

    if (session) {
      const cookieHeader = makeCookieHeader(session.cookies);
      if (cookieHeader) {
        requestHeaders.Cookie = cookieHeader;
      }

      if (requireCsrf && !isSafeMethod(method)) {
        const csrfToken = session.csrfToken || session.cookies.csrf_token;
        if (csrfToken) {
          requestHeaders['X-CSRF-Token'] = csrfToken;
        }
      }
    }

    let payload = null;
    if (data !== null && data !== undefined) {
      payload = JSON.stringify(data);
      requestHeaders['Content-Type'] = 'application/json';
      requestHeaders['Content-Length'] = Buffer.byteLength(payload);
    }

    const req = transport.request({
      protocol: url.protocol,
      hostname: url.hostname,
      port: url.port || (url.protocol === 'https:' ? 443 : 80),
      path: url.pathname + url.search,
      method,
      headers: requestHeaders
    }, (res) => {
      let body = '';
      res.on('data', chunk => {
        body += chunk;
      });
      res.on('end', () => {
        if (session) {
          storeSetCookies(session, res.headers['set-cookie']);
        }

        let parsed = body;
        try {
          parsed = body ? JSON.parse(body) : {};
        } catch (e) {
          // keep raw text
        }

        resolve({
          status: res.statusCode,
          data: parsed,
          headers: res.headers
        });
      });
    });

    req.on('error', reject);

    if (payload) {
      req.write(payload);
    }
    req.end();
  });
}

async function initCsrf(session) {
  const res = await request('GET', '/api/csrf-token', {
    session,
    requireCsrf: false
  });

  if (res.status === 200 && res.data && res.data.csrfToken) {
    session.csrfToken = res.data.csrfToken;
  }

  return res;
}

function assert(condition, message) {
  if (condition) {
    results.passed++;
    console.log(`  [PASS] ${message}`);
  } else {
    results.failed++;
    results.errors.push(message);
    console.log(`  [FAIL] ${message}`);
  }
}

function createValidShareCode(prefix = '') {
  for (let i = 0; i < 20; i++) {
    const generated = ShareDB.generateShareCode();
    const code = (prefix + generated).replace(/[^A-Za-z0-9]/g, '').slice(0, 16);
    if (!code || code.length < 6) continue;
    const exists = db.prepare('SELECT 1 FROM shares WHERE share_code = ?').get(code);
    if (!exists) return code;
  }

  return ShareDB.generateShareCode();
}

async function testExpiredShare() {
  console.log('\n[测试] 已过期的分享...');

  const expiredShareCode = createValidShareCode('E');

  try {
    const yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1);
    const expiresAt = yesterday.toISOString().replace('T', ' ').substring(0, 19);

    db.prepare(`
      INSERT INTO shares (user_id, share_code, share_path, share_type, storage_type, expires_at)
      VALUES (?, ?, ?, ?, ?, ?)
    `).run(adminUserId, expiredShareCode, '/expired-test.txt', 'file', 'local', expiresAt);

    console.log(`  创建过期分享: ${expiredShareCode}, 过期时间: ${expiresAt}`);

    const res = await request('POST', `/api/share/${expiredShareCode}/verify`, {
      data: {},
      requireCsrf: false
    });

    assert(res.status === 404, `过期分享应返回 404, 实际: ${res.status}`);
    assert(res.data && res.data.message === '分享不存在', '应提示分享不存在');

    db.prepare('DELETE FROM shares WHERE share_code = ?').run(expiredShareCode);
    return true;
  } catch (error) {
    console.log(`  [ERROR] ${error.message}`);
    results.failed++;
    db.prepare('DELETE FROM shares WHERE share_code = ?').run(expiredShareCode);
    return false;
  }
}

async function testShareWithDeletedFile() {
  console.log('\n[测试] 分享的文件不存在...');

  const shareCode = createValidShareCode('N');

  try {
    db.prepare(`
      INSERT INTO shares (user_id, share_code, share_path, share_type, storage_type)
      VALUES (?, ?, ?, ?, ?)
    `).run(adminUserId, shareCode, '/non_existent_file_xyz.txt', 'file', 'local');

    console.log(`  创建分享: ${shareCode}, 路径: /non_existent_file_xyz.txt`);

    const res = await request('POST', `/api/share/${shareCode}/verify`, {
      data: {},
      requireCsrf: false
    });

    assert(res.status === 500 || res.status === 200, `应返回500或200，实际: ${res.status}`);
    if (res.status === 500) {
      assert(!!res.data?.message, '500时应返回错误消息');
    }

    db.prepare('DELETE FROM shares WHERE share_code = ?').run(shareCode);
    return true;
  } catch (error) {
    console.log(`  [ERROR] ${error.message}`);
    db.prepare('DELETE FROM shares WHERE share_code = ?').run(shareCode);
    return false;
  }
}

async function testShareByBannedUser() {
  console.log('\n[测试] 被封禁用户的分享...');

  let testUserId = null;
  const shareCode = createValidShareCode('B');

  try {
    testUserId = UserDB.create({
      username: `test_banned_${Date.now()}`,
      email: `test_banned_${Date.now()}@test.com`,
      password: 'test123',
      is_verified: 1
    });

    db.prepare(`
      INSERT INTO shares (user_id, share_code, share_path, share_type, storage_type)
      VALUES (?, ?, ?, ?, ?)
    `).run(testUserId, shareCode, '/test.txt', 'file', 'local');

    console.log(`  创建测试用户 ID: ${testUserId}`);
    console.log(`  创建分享: ${shareCode}`);

    UserDB.setBanStatus(testUserId, true);
    console.log(`  封禁用户: ${testUserId}`);

    const res = await request('POST', `/api/share/${shareCode}/verify`, {
      data: {},
      requireCsrf: false
    });

    console.log(`  被封禁用户分享访问状态码: ${res.status}`);

    db.prepare('DELETE FROM shares WHERE share_code = ?').run(shareCode);
    UserDB.delete(testUserId);

    assert(true, '被封禁用户分享测试完成');
    return true;
  } catch (error) {
    console.log(`  [ERROR] ${error.message}`);
    if (shareCode) db.prepare('DELETE FROM shares WHERE share_code = ?').run(shareCode);
    if (testUserId) UserDB.delete(testUserId);
    return false;
  }
}

async function testPathTraversalAttacks() {
  console.log('\n[测试] 路径遍历攻击防护...');

  const shareCode = createValidShareCode('T');

  try {
    db.prepare(`
      INSERT INTO shares (user_id, share_code, share_path, share_type, storage_type)
      VALUES (?, ?, ?, ?, ?)
    `).run(adminUserId, shareCode, '/allowed-folder', 'directory', 'local');

    const attackPaths = [
      '../../../etc/passwd',
      '..\\..\\..\\etc\\passwd',
      '%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd',
      '/allowed-folder/../../../etc/passwd',
      '/allowed-folder/./../../etc/passwd',
      '....//....//....//etc/passwd',
      '/allowed-folder%00.txt/../../../etc/passwd'
    ];

    let blocked = 0;
    for (const attackPath of attackPaths) {
      const res = await request('POST', `/api/share/${shareCode}/download-url`, {
        data: { path: attackPath },
        requireCsrf: false
      });

      if (res.status === 403 || res.status === 400) {
        blocked++;
        console.log(`  [BLOCKED] ${attackPath.substring(0, 40)}...`);
      } else {
        console.log(`  [WARN] 可能未阻止: ${attackPath}, 状态: ${res.status}`);
      }
    }

    assert(blocked >= attackPaths.length - 1, `路径遍历攻击应被阻止 (${blocked}/${attackPaths.length})`);

    db.prepare('DELETE FROM shares WHERE share_code = ?').run(shareCode);
    return true;
  } catch (error) {
    console.log(`  [ERROR] ${error.message}`);
    db.prepare('DELETE FROM shares WHERE share_code = ?').run(shareCode);
    return false;
  }
}

async function testSpecialCharactersInPath() {
  console.log('\n[测试] 特殊字符路径处理...');

  if (!adminSession.cookies.token) {
    console.log('  [WARN] 未登录，跳过特殊字符路径测试');
    assert(true, '未登录时跳过特殊字符路径测试');
    return true;
  }

  const specialPaths = [
    '/文件夹/中文文件.txt',
    '/folder with spaces/file.txt',
    '/folder-with-dashes/file_underscore.txt',
    '/folder.with.dots/file.name.ext.txt',
    "/folder'with'quotes/file.txt"
  ];

  let handled = 0;

  for (const virtualPath of specialPaths) {
    try {
      const createRes = await request('POST', '/api/share/create', {
        data: {
          share_type: 'file',
          file_path: virtualPath
        },
        session: adminSession
      });

      if (createRes.status === 200 || createRes.status === 400) {
        handled++;
        console.log(`  [OK] ${virtualPath.substring(0, 30)}... - 状态: ${createRes.status}`);

        if (createRes.status === 200 && createRes.data?.share_code) {
          const mySharesRes = await request('GET', '/api/share/my', {
            session: adminSession
          });

          const share = mySharesRes.data?.shares?.find(s => s.share_code === createRes.data.share_code);
          if (share) {
            await request('DELETE', `/api/share/${share.id}`, {
              session: adminSession
            });
          }
        }
      }
    } catch (error) {
      console.log(`  [ERROR] ${virtualPath}: ${error.message}`);
    }
  }

  assert(handled === specialPaths.length, '特殊字符路径处理完成');
  return true;
}

async function testConcurrentPasswordAttempts() {
  console.log('\n[测试] 并发密码尝试限流...');

  const shareCode = createValidShareCode('C');

  try {
    const hashedPassword = bcrypt.hashSync('correct123', 10);

    db.prepare(`
      INSERT INTO shares (user_id, share_code, share_path, share_type, share_password, storage_type)
      VALUES (?, ?, ?, ?, ?, ?)
    `).run(adminUserId, shareCode, '/test.txt', 'file', hashedPassword, 'local');

    const promises = [];
    for (let i = 0; i < 20; i++) {
      promises.push(
        request('POST', `/api/share/${shareCode}/verify`, {
          data: { password: `wrong${i}` },
          requireCsrf: false
        })
      );
    }

    const responses = await Promise.all(promises);

    const rateLimited = responses.filter(r => r.status === 429).length;
    const unauthorized = responses.filter(r => r.status === 401).length;

    console.log(`  并发请求: 20, 限流: ${rateLimited}, 401错误: ${unauthorized}`);

    if (rateLimited > 0) {
      assert(true, '限流机制生效');
    } else {
      console.log('  [INFO] 限流未触发（可能配置较宽松）');
      assert(true, '并发测试完成');
    }

    db.prepare('DELETE FROM shares WHERE share_code = ?').run(shareCode);
    return true;
  } catch (error) {
    console.log(`  [ERROR] ${error.message}`);
    db.prepare('DELETE FROM shares WHERE share_code = ?').run(shareCode);
    return false;
  }
}

async function testShareStatistics() {
  console.log('\n[测试] 分享统计功能...');

  const shareCode = createValidShareCode('S');

  try {
    db.prepare(`
      INSERT INTO shares (user_id, share_code, share_path, share_type, storage_type, view_count, download_count)
      VALUES (?, ?, ?, ?, ?, ?, ?)
    `).run(adminUserId, shareCode, '/test.txt', 'file', 'local', 0, 0);

    for (let i = 0; i < 3; i++) {
      await request('POST', `/api/share/${shareCode}/verify`, {
        data: {},
        requireCsrf: false
      });
    }

    for (let i = 0; i < 2; i++) {
      await request('POST', `/api/share/${shareCode}/download`, {
        data: {},
        requireCsrf: false
      });
    }

    const share = db.prepare('SELECT view_count, download_count FROM shares WHERE share_code = ?').get(shareCode);

    assert(share.view_count === 3, `查看次数应为 3, 实际: ${share.view_count}`);
    assert(share.download_count === 2, `下载次数应为 2, 实际: ${share.download_count}`);

    console.log(`  查看次数: ${share.view_count}, 下载次数: ${share.download_count}`);

    db.prepare('DELETE FROM shares WHERE share_code = ?').run(shareCode);
    return true;
  } catch (error) {
    console.log(`  [ERROR] ${error.message}`);
    db.prepare('DELETE FROM shares WHERE share_code = ?').run(shareCode);
    return false;
  }
}

async function testShareCodeUniqueness() {
  console.log('\n[测试] 分享码唯一性...');

  try {
    const codes = new Set();

    for (let i = 0; i < 10; i++) {
      const code = ShareDB.generateShareCode();
      if (codes.has(code)) {
        console.log(`  [WARN] 发现重复分享码: ${code}`);
      }
      codes.add(code);
    }

    assert(codes.size === 10, `应生成 10 个唯一分享码, 实际: ${codes.size}`);
    console.log(`  生成了 ${codes.size} 个唯一分享码`);

    const sampleCode = ShareDB.generateShareCode();
    assert(sampleCode.length === 8, `分享码长度应为 8, 实际: ${sampleCode.length}`);
    assert(/^[a-zA-Z0-9]+$/.test(sampleCode), '分享码应只包含字母数字');

    return true;
  } catch (error) {
    console.log(`  [ERROR] ${error.message}`);
    return false;
  }
}

async function testExpiryTimeFormat() {
  console.log('\n[测试] 过期时间格式...');

  try {
    const testDays = [1, 7, 30, 365];

    for (const days of testDays) {
      const result = ShareDB.create(adminUserId, {
        share_type: 'file',
        file_path: `/test_${days}_days.txt`,
        expiry_days: days
      });

      const share = db.prepare('SELECT expires_at FROM shares WHERE share_code = ?').get(result.share_code);

      const expiresAt = new Date(share.expires_at);
      const now = new Date();
      const diffDays = Math.round((expiresAt - now) / (1000 * 60 * 60 * 24));

      assert(Math.abs(diffDays - days) <= 1, `${days}天过期应正确设置, 实际差异: ${diffDays}天`);

      db.prepare('DELETE FROM shares WHERE share_code = ?').run(result.share_code);
    }

    return true;
  } catch (error) {
    console.log(`  [ERROR] ${error.message}`);
    return false;
  }
}

async function login() {
  console.log('\n[准备] 登录获取认证...');

  try {
    await initCsrf(adminSession);

    const res = await request('POST', '/api/login', {
      data: {
        username: 'admin',
        password: 'admin123'
      },
      session: adminSession,
      requireCsrf: false
    });

    if (res.status === 200 && res.data?.success && adminSession.cookies.token) {
      await initCsrf(adminSession);

      const profileRes = await request('GET', '/api/user/profile', {
        session: adminSession
      });

      if (profileRes.status === 200 && profileRes.data?.user?.id) {
        adminUserId = profileRes.data.user.id;
      }

      console.log('  认证成功');
      return true;
    }

    console.log(`  认证失败: status=${res.status}, message=${res.data?.message || 'unknown'}`);
    return false;
  } catch (error) {
    console.log(`  [ERROR] ${error.message}`);
    return false;
  }
}

async function runTests() {
  console.log('========================================');
  console.log(' 分享功能边界条件深度测试（Cookie + CSRF）');
  console.log('========================================');

  const loggedIn = await login();
  if (!loggedIn) {
    console.log('\n[WARN] 登录失败，部分测试可能无法执行');
  }

  await testExpiredShare();
  await testShareWithDeletedFile();
  await testShareByBannedUser();
  await testPathTraversalAttacks();
  await testSpecialCharactersInPath();
  await testConcurrentPasswordAttempts();
  await testShareStatistics();
  await testShareCodeUniqueness();
  await testExpiryTimeFormat();

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

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

  console.log('\n========================================');

  process.exit(results.failed > 0 ? 1 : 0);
}

runTests().catch(error => {
  console.error('测试执行失败:', error);
  process.exit(1);
});
