/**
 * 安全测试 - 测试系统安全防护能力
 */
import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
import { VotingEngine, loadVotingScripts } from '../services/voting.engine';
import { redis } from '../config/redis';

describe('安全测试', () => {
  let votingEngine: VotingEngine;

  beforeAll(async () => {
    await loadVotingScripts();
    votingEngine = new VotingEngine();
  });

  beforeEach(async () => {
    const keys = await redis.keys('vote:*security_test*');
    if (keys.length > 0) await redis.del(...keys);
  });

  describe('防刷票测试 [高优先级]', () => {
    const userId = 'security_test_user';
    const programId = 'security_test_program';

    it('同一用户同一票种重复投票应被拦截', async () => {
      await votingEngine.castVote(userId, programId, 'creative');
      const result = await votingEngine.castVote(userId, programId, 'creative');
      expect(result.is_duplicate).toBe(true);
    });

    it('同一用户不同票种投同一节目应被拦截', async () => {
      const uniqueUser = 'security_test_diff_ticket_' + Date.now();
      const uniqueProgram = 'security_test_prog_' + Date.now();
      await votingEngine.castVote(uniqueUser, uniqueProgram, 'creative');
      const result = await votingEngine.castVote(uniqueUser, uniqueProgram, 'visual');
      expect(result.success).toBe(false);
      expect(result.error).toBe('ALREADY_VOTED_PROGRAM');
    });

    it('快速连续投票应被分布式锁拦截', async () => {
      const promises = Array(5).fill(null).map((_, i) =>
        votingEngine.castVote(`security_test_rapid_${i}`, programId, 'creative')
      );
      const results = await Promise.all(promises);
      const successCount = results.filter(r => r.success).length;
      expect(successCount).toBeGreaterThan(0);
    });

    it('每个票种只能投一个节目', async () => {
      const prog1 = 'security_test_prog_1';
      const prog2 = 'security_test_prog_2';

      await votingEngine.castVote(userId + '_ticket', prog1, 'creative');
      const result = await votingEngine.castVote(userId + '_ticket', prog2, 'creative');

      // 应该撤销旧票，投新票
      expect(result.success).toBe(true);
      expect(result.revoked).toBe(true);
    });
  });

  describe('接口鉴权测试 [高优先级]', () => {
    it('无效票种应被拒绝', async () => {
      const result = await votingEngine.castVote(
        'security_test_auth',
        'test_program',
        'hacked_ticket' as any
      );
      expect(result.success).toBe(false);
      expect(result.error).toBe('INVALID_TICKET_TYPE');
    });

    it('SQL注入尝试应被安全处理', async () => {
      const maliciousId = "'; DROP TABLE votes; --";
      const result = await votingEngine.castVote(
        maliciousId,
        'test_program',
        'creative'
      );
      // 系统应该安全处理，不会崩溃
      expect(result).toBeDefined();
    });

    it('XSS攻击尝试应被安全处理', async () => {
      const xssPayload = '<script>alert("xss")</script>';
      const result = await votingEngine.castVote(
        xssPayload,
        'test_program',
        'creative'
      );
      expect(result).toBeDefined();
    });
  });

  describe('数据防篡改测试 [中优先级]', () => {
    it('投票计数应该原子性增加', async () => {
      const programId = 'security_test_atomic';
      const results = await Promise.all([
        votingEngine.castVote('security_test_atomic_1', programId, 'creative'),
        votingEngine.castVote('security_test_atomic_2', programId, 'visual'),
        votingEngine.castVote('security_test_atomic_3', programId, 'atmosphere'),
      ]);
      const successResults = results.filter(r => r.success);
      expect(successResults.length).toBe(3);
    });

    it('撤票后计数应该正确减少', async () => {
      const userId = 'security_test_revoke';
      const prog1 = 'security_test_revoke_1';
      const prog2 = 'security_test_revoke_2';

      const first = await votingEngine.castVote(userId, prog1, 'creative');
      expect(first.success).toBe(true);

      const second = await votingEngine.castVote(userId, prog2, 'creative');
      expect(second.success).toBe(true);
      expect(second.revoked).toBe(true);
    });

    it('投票记录应该包含时间戳', async () => {
      const result = await votingEngine.castVote(
        'security_test_timestamp',
        'test_program',
        'creative'
      );
      expect(result.success).toBe(true);
    });
  });
});
