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';

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

async function checkInfra(): Promise<boolean> {
  const dbUrl = process.env.DATABASE_URL || '';
  const dbMatch = dbUrl.match(/@([^:]+):(\d+)/);
  const dbHost = dbMatch?.[1] || '127.0.0.1';
  const dbPort = parseInt(dbMatch?.[2] || '3306', 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);
    });
  return checkPort(dbHost, dbPort);
}

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

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

    process.env.NODE_ENV = 'test';
    process.env.JWT_SECRET = 'm-ai-08-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-mai08@test.com', role: 'USER', type: 'user' });

    const kb = await prisma.knowledgeBase.upsert({
      where: { id: 'm-ai-08-e2e-kb-001' },
      create: { id: 'm-ai-08-e2e-kb-001', userId, title: 'M-AI-08 E2E KB', status: 'active' },
      update: { userId },
    });
    testKbId = kb.id;

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

  afterAll(async () => {
    process.env = OLD_ENV;
    if (app && infraAvailable) {
      try { await prisma.featureFlag.deleteMany({ where: { name: 'LEARNING_ANALYSIS_ENGINE_MODE' } }); } catch {}
      try { await prisma.aiLearningAnalysis.deleteMany({ where: { userId } }); } catch {}
      try { await prisma.weakPointCandidate.deleteMany({ where: { userId } }); } catch {}
      try { await prisma.nextActionRecommendation.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.knowledgeBase.delete({ where: { id: testKbId } }); } catch {}
      await app.close();
    }
  });

  describe('场景 1: Legacy 模式', () => {
    it('LEARNING_ANALYSIS_ENGINE_MODE=disabled → legacy', async () => {
      await prisma.featureFlag.update({ where: { name: 'LEARNING_ANALYSIS_ENGINE_MODE' }, data: { enabled: false } });
      const res = await request(app.getHttpServer())
        .post('/api/ai/jobs')
        .set('Authorization', `Bearer ${userToken}`)
        .send({ jobType: 'learning_state_analysis', targetType: 'knowledge_base', targetId: testKbId })
        .expect(201);
      expect(res.body.jobId).toBeTruthy();
      expect(res.body.engineMode).toBeUndefined();
      if (res.body.jobId) try { await prisma.aiJob.delete({ where: { id: res.body.jobId } }); } catch {}
      await prisma.featureFlag.update({ where: { name: 'LEARNING_ANALYSIS_ENGINE_MODE' }, data: { enabled: true, whitelist: userId } });
    });
  });

  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: 'learning_state_analysis', targetType: 'knowledge_base', targetId: testKbId })
        .expect(201);
      expect(res.body.jobId).toBeTruthy();
      expect(res.body.engineMode).toBe('unified');
      jobId = res.body.jobId;

      const job = await prisma.aiJob.findUnique({ where: { id: jobId } });
      expect(job).toBeTruthy();
      expect(job!.jobType).toBe('learning_analysis');

      const snap = await prisma.aiJobSnapshot.findUnique({ where: { jobId } });
      expect(snap).toBeTruthy();

      const outbox = await (prisma as any).outboxEvent.findFirst({ where: { aggregateId: jobId } });
      expect(outbox).toBeTruthy();
    });
    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 {}
      }
    });
  });

  describe('场景 3: 幂等', () => {
    it('相同 idempotencyKey → 相同 jobId', async () => {
      const body = { jobType: 'learning_state_analysis', targetType: 'knowledge_base', targetId: testKbId, idempotencyKey: 'e2e-la-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);
      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 {}
    });
  });

  describe('场景 4: 回滚', () => {
    it('切回 Legacy 有效', async () => {
      await prisma.featureFlag.update({ where: { name: 'LEARNING_ANALYSIS_ENGINE_MODE' }, data: { enabled: false } });
      const res = await request(app.getHttpServer())
        .post('/api/ai/jobs')
        .set('Authorization', `Bearer ${userToken}`)
        .send({ jobType: 'learning_state_analysis', targetType: 'knowledge_base', targetId: testKbId })
        .expect(201);
      expect(res.body.engineMode).toBeUndefined();
      if (res.body.jobId) try { await prisma.aiJob.delete({ where: { id: res.body.jobId } }); } catch {}
      await prisma.featureFlag.update({ where: { name: 'LEARNING_ANALYSIS_ENGINE_MODE' }, data: { enabled: true, whitelist: userId } });
    });
  });
});
