import { JobsService } from '../jobs/jobs.service';
import { XiaobaoRiskService } from './xiaobao-risk.service';

describe('XiaobaoRiskService', () => {
  function makeService(xiaobaoAi?: { evaluateSummary: jest.Mock }) {
    const prisma = {
      version: { findUnique: jest.fn() },
      devTask: { findMany: jest.fn() },
      testCase: { findMany: jest.fn() },
      bug: { findMany: jest.fn() },
      workActivity: { findMany: jest.fn() },
      taskWorklog: { findMany: jest.fn() },
      xiaobaoRiskSummary: {
        upsert: jest.fn(),
        count: jest.fn(),
      },
    };
    const jobs = {
      enqueue: jest.fn(),
    };
    return { prisma, jobs, service: new XiaobaoRiskService(prisma as any, jobs as unknown as JobsService, xiaobaoAi as any) };
  }

  it('refreshes a version risk summary from relational rows without opening the page', async () => {
    const { prisma, service } = makeService();
    const now = new Date('2026-07-08T09:00:00.000Z');
    prisma.version.findUnique.mockResolvedValue({
      id: 'version-1',
      name: 'V1.0',
      productId: 'product-1',
      projectId: 'project-1',
      expectedReleaseDate: new Date('2026-07-08T18:00:00.000Z'),
      members: [{ id: 'u1', name: 'Dev' }],
      product: { id: 'product-1', name: 'FTB' },
      project: { id: 'project-1', name: 'PM' },
    });
    prisma.devTask.findMany.mockResolvedValue([
      { id: 'dev-1', title: 'Build API', status: 'in_progress', estimateHours: 16, aiEstimateHours: null, isBlocked: false, updatedAt: now },
    ]);
    prisma.testCase.findMany.mockResolvedValue([
      { id: 'case-1', title: 'Regression', status: 'failed', estimateHours: 4, aiEstimateHours: null, updatedAt: now },
    ]);
    prisma.bug.findMany.mockResolvedValue([
      { id: 'bug-1', title: 'Crash', status: 'open', severity: 'critical', priority: 1, estimateHours: null, aiEstimateHours: null, updatedAt: now },
    ]);
    prisma.workActivity.findMany.mockResolvedValue([{ id: 'activity-1', occurredAt: now }]);
    prisma.taskWorklog.findMany.mockResolvedValue([]);
    prisma.xiaobaoRiskSummary.upsert.mockImplementation(({ create }) => create);

    const result = await service.refreshSummary('version-1', { now });

    expect(result.riskLevel).toBe('blocked');
    expect(result.riskScore).toBeGreaterThanOrEqual(75);
    expect(prisma.xiaobaoRiskSummary.upsert).toHaveBeenCalledWith({
      where: { versionId: 'version-1' },
      update: expect.objectContaining({
        dirty: false,
        riskLevel: 'blocked',
        riskSignature: expect.stringContaining('version-1'),
      }),
      create: expect.objectContaining({
        versionId: 'version-1',
        dirty: false,
        riskLevel: 'blocked',
      }),
    });
  });

  it('marks a summary dirty and enqueues a deduped refresh job', async () => {
    const { prisma, jobs, service } = makeService();
    prisma.xiaobaoRiskSummary.upsert.mockResolvedValue({});
    jobs.enqueue.mockResolvedValue({ id: 'job-1' });

    await service.markDirtyAndEnqueue('version-1');

    expect(prisma.xiaobaoRiskSummary.upsert).toHaveBeenCalledWith(expect.objectContaining({
      where: { versionId: 'version-1' },
      update: { dirty: true },
    }));
    expect(jobs.enqueue).toHaveBeenCalledWith({
      type: 'xiaobao.summary.refresh',
      dedupeKey: 'version-1',
      payload: { versionId: 'version-1' },
      maxAttempts: 5,
    });
  });

  it('evaluates AI interpretation enqueue policy after refreshing the summary', async () => {
    const xiaobaoAi = {
      evaluateSummary: jest.fn().mockResolvedValue({ enqueued: true, reason: 'queued' }),
    };
    const { prisma, service } = makeService(xiaobaoAi);
    const now = new Date('2026-07-08T09:00:00.000Z');
    prisma.version.findUnique.mockResolvedValue({
      id: 'version-1',
      name: 'V1.0',
      productId: 'product-1',
      projectId: 'project-1',
      expectedReleaseDate: new Date('2026-07-08T18:00:00.000Z'),
      product: { id: 'product-1', name: 'FTB' },
      project: { id: 'project-1', name: 'PM' },
    });
    prisma.devTask.findMany.mockResolvedValue([
      { id: 'dev-1', status: 'in_progress', estimateHours: 16, aiEstimateHours: null, isBlocked: false },
    ]);
    prisma.testCase.findMany.mockResolvedValue([]);
    prisma.bug.findMany.mockResolvedValue([
      { id: 'bug-1', status: 'open', severity: 'critical', priority: 1, estimateHours: null, aiEstimateHours: null },
    ]);
    prisma.workActivity.findMany.mockResolvedValue([]);
    prisma.taskWorklog.findMany.mockResolvedValue([]);
    prisma.xiaobaoRiskSummary.upsert.mockImplementation(({ create }) => create);

    const payload = await service.refreshSummary('version-1', { now });

    expect(xiaobaoAi.evaluateSummary).toHaveBeenCalledWith(payload, { now });
  });
});
