import { NotFoundException } from '@nestjs/common';
import { VersionService } from './version.service';

describe('VersionService domain writes', () => {
  const makeService = () => {
    const tx = {
      requirement: { updateMany: jest.fn() },
      versionPlan: { deleteMany: jest.fn() },
      devTask: { deleteMany: jest.fn() },
      testCase: { deleteMany: jest.fn() },
      bug: { deleteMany: jest.fn() },
      version: { delete: jest.fn() },
    };
    const prisma = {
      product: { findUnique: jest.fn() },
      project: { findFirst: jest.fn() },
      version: {
        create: jest.fn(),
        findFirst: jest.fn(),
        findMany: jest.fn(),
        update: jest.fn(),
      },
      $transaction: jest.fn(async (callback: (client: typeof tx) => Promise<unknown>) => callback(tx)),
    };

    return {
      prisma,
      service: new VersionService(prisma as any),
      tx,
    };
  };

  it('creates versions with root metadata in the relation table', async () => {
    const { prisma, service } = makeService();
    prisma.product.findUnique.mockResolvedValue({ id: 'product-1' });
    prisma.project.findFirst.mockResolvedValue({ id: 'project-1', productId: 'product-1' });
    prisma.version.create.mockResolvedValue({ id: 'version-1', productId: 'product-1' });

    await service.create('product-1', {
      projectId: 'project-1',
      name: 'CRM 1.0',
      status: 'developing',
      expectedReleaseDate: '2026-08-01T00:00:00.000Z',
      members: [{ role: 'frontend', name: '张三' }],
      priority: 'P1',
    });

    expect(prisma.version.create).toHaveBeenCalledWith({
      data: expect.objectContaining({
        productId: 'product-1',
        projectId: 'project-1',
        name: 'CRM 1.0',
        status: 'developing',
        expectedReleaseDate: new Date('2026-08-01T00:00:00.000Z'),
        members: [{ role: 'frontend', name: '张三' }],
        priority: 1,
      }),
    });
  });

  it('rejects versions for projects outside the product scope', async () => {
    const { prisma, service } = makeService();
    prisma.product.findUnique.mockResolvedValue({ id: 'product-1' });
    prisma.project.findFirst.mockResolvedValue(null);

    await expect(
      service.create('product-1', { projectId: 'project-404', name: 'CRM 1.0' }),
    ).rejects.toBeInstanceOf(NotFoundException);
    expect(prisma.version.create).not.toHaveBeenCalled();
  });

  it('lists versions by product and optional project scope', async () => {
    const { prisma, service } = makeService();
    prisma.version.findMany.mockResolvedValue([]);

    await service.findAll('product-1', 'project-1');

    expect(prisma.version.findMany).toHaveBeenCalledWith({
      where: { productId: 'product-1', projectId: 'project-1' },
      orderBy: { createdAt: 'desc' },
    });
  });

  it('updates versions only inside their product scope', async () => {
    const { prisma, service } = makeService();
    prisma.version.findFirst.mockResolvedValue({ id: 'version-1', productId: 'product-1' });
    prisma.version.update.mockResolvedValue({ id: 'version-1', status: 'paused' });

    await service.update('product-1', 'version-1', { status: 'paused', releaseDate: '2026-09-01T00:00:00.000Z' });

    expect(prisma.version.update).toHaveBeenCalledWith({
      where: { id: 'version-1' },
      data: {
        status: 'paused',
        releaseDate: new Date('2026-09-01T00:00:00.000Z'),
      },
    });
  });

  it('deletes version-scoped data and releases linked requirements before deleting a version', async () => {
    const { prisma, service, tx } = makeService();
    prisma.version.findFirst.mockResolvedValue({ id: 'version-1', productId: 'product-1' });
    tx.version.delete.mockResolvedValue({ id: 'version-1' });

    await service.remove('product-1', 'version-1');

    expect(tx.requirement.updateMany).toHaveBeenCalledWith({
      where: { productId: 'product-1', versionId: 'version-1' },
      data: { versionId: null },
    });
    expect(tx.versionPlan.deleteMany).toHaveBeenCalledWith({ where: { versionId: 'version-1' } });
    expect(tx.devTask.deleteMany).toHaveBeenCalledWith({ where: { versionId: 'version-1' } });
    expect(tx.testCase.deleteMany).toHaveBeenCalledWith({ where: { versionId: 'version-1' } });
    expect(tx.bug.deleteMany).toHaveBeenCalledWith({ where: { versionId: 'version-1' } });
    expect(tx.version.delete).toHaveBeenCalledWith({ where: { id: 'version-1' } });
  });
});
