import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import request from 'supertest';
import * as net from 'net';
import { AppModule } from '../src/app.module';
import { PrismaService } from '../src/infrastructure/database/prisma.service';

/**
 * M-AI-07-07: Quiz Generation 真实业务 E2E
 *
 * 核心场景（15 场景）：
 *   1.  Legacy 模式生成成功
 *   2.  Unified 模式完整成功
 *   3.  相同 submission 返回同一个 Job
 *   4.  重复消费不重复 Quiz
 *   5.  重复消费不重复 Questions
 *   6.  并发 Projector 不重复 Quiz
 *   7.  跨用户 target 请求被拒绝
 *   8.  跨用户请求零数据库副作用
 *   9.  Unified 创建失败不调用 Legacy
 *   10. Provider 永久失败后 Job failed
 *   11. Projector 中途失败无部分 Quiz
 *   12. 旧查询可以读取 Unified Quiz
 *   13. 未作答查询不泄漏正确答案
 *   14. Feature Flag 切回 Legacy 有效
 *   15. 公开错误不泄漏内部输出
 */

const userId = 'm-ai-07-e2e-user';
const userId2 = 'm-ai-07-e2e-user-2';
const OLD_ENV = { ...process.env };

async function checkInfra(): Promise<boolean> {
  const dbUrl = process.env.DATABASE_URL || '';
  const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379';
  const dbMatch = dbUrl.match(/@([^:]+):(\d+)/);
  const dbHost = dbMatch?.[1] || '127.0.0.1';
  const dbPort = parseInt(dbMatch?.[2] || '3306', 10);
  const redisMatch = redisUrl.match(/@?([^:]+):(\d+)/);
  const redisHost = redisMatch?.[1] || '127.0.0.1';
  const redisPort = parseInt(redisMatch?.[2] || '6379', 10);

  const checkPort = (host: string, port: number): Promise<boolean> =>
    new Promise((resolve) => {
      const sock = new net.Socket();
      sock.setTimeout(2000);
      sock.on('connect', () => { sock.destroy(); resolve(true); });
      sock.on('error', () => resolve(false));
      sock.on('timeout', () => { sock.destroy(); resolve(false); });
      sock.connect(port, host);
    });

  const [mysqlOk, redisOk] = await Promise.all([
    checkPort(dbHost, dbPort),
    checkPort(redisHost, redisPort),
  ]);
  return mysqlOk && redisOk;
}

describe('M-AI-07 Quiz Generation E2E (real infra)', () => {
  let app: INestApplication;
  let prisma: PrismaService;
  let jwtService: JwtService;
  let userToken: string;
  let userToken2: string;
  let infraAvailable = false;
  let testKbId: string;

  beforeAll(async () => {
    infraAvailable = await checkInfra();
    if (!infraAvailable) {
      throw new Error(
        '[M-AI-07 E2E] MySQL/Redis not available — HARD FAIL. ' +
        'Run: docker start mysql redis',
      );
    }

    process.env.NODE_ENV = 'test';
    process.env.JWT_SECRET = 'm-ai-07-e2e-jwt-secret';

    const module: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = module.createNestApplication();
    app.setGlobalPrefix('api', { exclude: ['admin-api/(.*)', 'internal/(.*)'] });
    app.useGlobalPipes(new ValidationPipe({ transform: true }));
    await app.init();

    prisma = module.get(PrismaService);
    jwtService = module.get(JwtService);

    userToken = jwtService.sign({ sub: userId, id: userId, email: 'e2e-mai07@test.com', role: 'USER', type: 'user' });
    userToken2 = jwtService.sign({ sub: userId2, id: userId2, email: 'e2e-mai07-2@test.com', role: 'USER', type: 'user' });

    // Create test KB with items
    const kb = await prisma.knowledgeBase.upsert({
      where: { id: 'm-ai-07-e2e-kb-001' },
      create: { id: 'm-ai-07-e2e-kb-001', userId, title: 'E2E Quiz KB', status: 'active' },
      update: { userId },
    });
    testKbId = kb.id;

    await prisma.knowledgeItem.upsert({
      where: { id: 'm-ai-07-e2e-ki-001' },
      create: { id: 'm-ai-07-e2e-ki-001', userId, knowledgeBaseId: testKbId, itemType: 'concept', title: '光合作用', content: '光合作用是植物利用光能的过程。', summary: '光合作用原理', learnable: true, status: 'active', orderIndex: 0 },
      update: { userId, title: '光合作用' },
    });

    // Enable FeatureFlags
    await prisma.featureFlag.upsert({
      where: { name: 'QUIZ_GENERATION_ENGINE_MODE' },
      create: { name: 'QUIZ_GENERATION_ENGINE_MODE', enabled: true, whitelist: userId },
      update: { enabled: true, whitelist: userId },
    });
  }, 30000);

  afterAll(async () => {
    process.env = OLD_ENV;
    if (app) {
      if (infraAvailable) {
        try { await prisma.featureFlag.deleteMany({ where: { name: 'QUIZ_GENERATION_ENGINE_MODE' } }); } catch {}
        try { await prisma.quizQuestion.deleteMany({ where: { quiz: { userId } } }); } catch {}
        try { await prisma.quizAttempt.deleteMany({ where: { userId } }); } catch {}
        try { await prisma.quiz.deleteMany({ where: { userId } }); } catch {}
        try { await prisma.aiJobArtifact.deleteMany({ where: { job: { userId } } }); } catch {}
        try { await prisma.aiJobSnapshot.deleteMany({ where: { job: { userId } } }); } catch {}
        try { await (prisma as any).outboxEvent.deleteMany({ where: { aggregateType: 'AiJob' } }); } catch {}
        try { await prisma.aiJob.deleteMany({ where: { userId } }); } catch {}
        try { await prisma.knowledgeItem.deleteMany({ where: { knowledgeBaseId: testKbId } }); } catch {}
        try { await prisma.knowledgeBase.delete({ where: { id: testKbId } }); } catch {}
      }
      await app.close();
    }
  });

  // ═══════════════════════════════════════════════════════
  // 场景 1: Legacy 模式生成成功
  // ═══════════════════════════════════════════════════════

  describe('场景 1: Legacy 模式', () => {
    it('QUIZ_GENERATION_ENGINE_MODE=disabled → legacy', async () => {
      await prisma.featureFlag.update({ where: { name: 'QUIZ_GENERATION_ENGINE_MODE' }, data: { enabled: false } });

      const res = await request(app.getHttpServer())
        .post('/api/ai/jobs')
        .set('Authorization', `Bearer ${userToken}`)
        .send({ jobType: 'quiz_generation', targetType: 'knowledge_base', targetId: testKbId, questionCount: 3 })
        .expect(201);

      expect(res.body.jobId).toBeTruthy();
      // Legacy response has no engineMode
      expect(res.body.engineMode).toBeUndefined();

      // Cleanup
      try { await prisma.aiJob.delete({ where: { id: res.body.jobId } }); } catch {}

      await prisma.featureFlag.update({ where: { name: 'QUIZ_GENERATION_ENGINE_MODE' }, data: { enabled: true, whitelist: userId } });
    });
  });

  // ═══════════════════════════════════════════════════════
  // 场景 2: Unified 模式完整成功
  // ═══════════════════════════════════════════════════════

  describe('场景 2: Unified 模式', () => {
    let jobId: string;

    it('HTTP → AiJob + Snapshot + Outbox', async () => {
      const res = await request(app.getHttpServer())
        .post('/api/ai/jobs')
        .set('Authorization', `Bearer ${userToken}`)
        .send({ jobType: 'quiz_generation', targetType: 'knowledge_base', targetId: testKbId, questionCount: 3, questionTypes: ['choice'], difficultyLevel: 'easy' })
        .expect(201);

      expect(res.body.jobId).toBeTruthy();
      expect(res.body.status).toBe('queued');
      expect(res.body.engineMode).toBe('unified');
      jobId = res.body.jobId;

      // Verify AiJob created
      const job = await prisma.aiJob.findUnique({ where: { id: jobId } });
      expect(job).toBeTruthy();
      expect(job!.jobType).toBe('quiz_generation');

      // Verify Snapshot
      const snap = await prisma.aiJobSnapshot.findUnique({ where: { jobId } });
      expect(snap).toBeTruthy();
      expect(snap!.schemaVersion).toBe('quiz-generation-v1');

      // Verify Outbox
      const outbox = await (prisma as any).outboxEvent.findFirst({ where: { aggregateId: jobId } });
      expect(outbox).toBeTruthy();
      expect(outbox.eventType).toBe('ai.job.enqueue');
    });

    afterAll(async () => {
      if (jobId) {
        try { await prisma.aiJobSnapshot.deleteMany({ where: { jobId } }); } catch {}
        try { await (prisma as any).outboxEvent.deleteMany({ where: { aggregateId: jobId } }); } catch {}
        try { await prisma.aiJob.delete({ where: { id: jobId } }); } catch {}
      }
    });
  });

  // ═══════════════════════════════════════════════════════
  // 场景 3: 幂等 — 相同 submission 返回同一个 Job
  // ═══════════════════════════════════════════════════════

  describe('场景 3: 幂等', () => {
    it('相同 idempotencyKey → 相同 jobId', async () => {
      const body = { jobType: 'quiz_generation', targetType: 'knowledge_base', targetId: testKbId, questionCount: 3, idempotencyKey: 'e2e-quiz-idem-001' };

      const r1 = await request(app.getHttpServer()).post('/api/ai/jobs').set('Authorization', `Bearer ${userToken}`).send(body).expect(201);
      const r2 = await request(app.getHttpServer()).post('/api/ai/jobs').set('Authorization', `Bearer ${userToken}`).send(body).expect(201);

      expect(r2.body.jobId).toBe(r1.body.jobId);
      expect(await prisma.aiJob.count({ where: { id: r1.body.jobId } })).toBe(1);

      try { await prisma.aiJobSnapshot.deleteMany({ where: { jobId: r1.body.jobId } }); } catch {}
      try { await (prisma as any).outboxEvent.deleteMany({ where: { aggregateId: r1.body.jobId } }); } catch {}
      try { await prisma.aiJob.delete({ where: { id: r1.body.jobId } }); } catch {}
    });
  });

  // ═══════════════════════════════════════════════════════
  // 场景 7: 跨用户拒绝
  // ═══════════════════════════════════════════════════════

  describe('场景 7: 跨用户权限', () => {
    it('User B 请求 User A 的 KB → Forbidden', async () => {
      const res = await request(app.getHttpServer())
        .post('/api/ai/jobs')
        .set('Authorization', `Bearer ${userToken2}`)
        .send({ jobType: 'quiz_generation', targetType: 'knowledge_base', targetId: testKbId, questionCount: 3 })
        .expect((r) => expect([403, 404]).toContain(r.status));
    });
  });

  // ═══════════════════════════════════════════════════════
  // 场景 12: 旧查询兼容
  // ═══════════════════════════════════════════════════════

  describe('场景 12: 旧查询兼容', () => {
    it('listQuizzes 可查询', async () => {
      const res = await request(app.getHttpServer())
        .get('/api/ai/quizzes')
        .set('Authorization', `Bearer ${userToken}`)
        .expect(200);
      expect(Array.isArray(res.body)).toBe(true);
    });
  });

  // ═══════════════════════════════════════════════════════
  // 场景 13: 答案安全
  // ═══════════════════════════════════════════════════════

  describe('场景 13: 答案不泄漏', () => {
    it('getQuizQuestions 不返回 answer（未提交前）', async () => {
      // Create a quiz directly via Prisma for testing
      const quiz = await prisma.quiz.create({
        data: { userId, knowledgeBaseId: testKbId, title: 'Security Test Quiz', questionCount: 1, sourceType: 'ai', status: 'ready' },
      });
      await prisma.quizQuestion.create({
        data: { quizId: quiz.id, type: 'choice', stem: 'Test Q?', options: ['A', 'B'], answer: '0', explanation: 'Because', orderIndex: 0 },
      });

      const res = await request(app.getHttpServer())
        .get(`/api/ai/quizzes/${quiz.id}/questions`)
        .set('Authorization', `Bearer ${userToken}`)
        .expect(200);

      // No answer should be returned for unanswered quiz
      for (const q of res.body) {
        expect(q.answer).toBeUndefined();
        expect(q.explanation).toBeUndefined();
      }

      // Cleanup
      await prisma.quizQuestion.deleteMany({ where: { quizId: quiz.id } });
      await prisma.quiz.delete({ where: { id: quiz.id } });
    });
  });

  // ═══════════════════════════════════════════════════════
  // 场景 14: 回滚
  // ═══════════════════════════════════════════════════════

  describe('场景 14: 回滚到 Legacy', () => {
    it('关闭 FeatureFlag 后走 Legacy', async () => {
      await prisma.featureFlag.update({ where: { name: 'QUIZ_GENERATION_ENGINE_MODE' }, data: { enabled: false } });

      const res = await request(app.getHttpServer())
        .post('/api/ai/jobs')
        .set('Authorization', `Bearer ${userToken}`)
        .send({ jobType: 'quiz_generation', targetType: 'knowledge_base', targetId: testKbId, questionCount: 3 })
        .expect(201);

      expect(res.body.jobId).toBeTruthy();
      expect(res.body.engineMode).toBeUndefined(); // Legacy

      try { await prisma.aiJob.delete({ where: { id: res.body.jobId } }); } catch {}

      await prisma.featureFlag.update({ where: { name: 'QUIZ_GENERATION_ENGINE_MODE' }, data: { enabled: true, whitelist: userId } });
    });
  });
});
