import { FilesService } from './files.service';

/**
 * Replacing a stored document must never destroy the previous one: the customer
 * uploaded it, and a staff correction has to stay auditable against it. The old
 * row is soft-deleted (so every normal read still returns exactly the current
 * version) and stamped with who replaced it and why.
 */
describe('FilesService — document versions', () => {
  const file = {
    originalname: 'bill-of-lading.pdf',
    size: 1234,
    mimetype: 'application/pdf',
    buffer: Buffer.from('x'),
  } as Express.Multer.File;

  let filesRepository: {
    deleteByCode: jest.Mock;
    create: jest.Mock;
    findVersionHistory: jest.Mock;
  };
  let service: FilesService;

  beforeEach(() => {
    filesRepository = {
      deleteByCode: jest.fn().mockResolvedValue(undefined),
      create: jest.fn(async (row) => ({ id: 'file-new', ...row })),
      findVersionHistory: jest.fn().mockResolvedValue([]),
    };
    service = new FilesService(
      filesRepository as never,
      {
        uploadFile: jest.fn().mockResolvedValue('https://minio/bucket/new.pdf'),
        getObjectNameFromUrl: (u: string) => u,
        getSignedUrl: jest.fn(),
      } as never,
    );
  });

  it('stamps the retired version with who replaced it and why', async () => {
    await service.upsertByCode(
      { resourceId: 'ctr-1', resource: 'contracts', code: 'bill_of_lading', file },
      { userId: 'gl-user-1', reason: 'Customer sent page 2 only' },
    );

    expect(filesRepository.deleteByCode).toHaveBeenCalledWith(
      'ctr-1',
      'contracts',
      'bill_of_lading',
      { userId: 'gl-user-1', reason: 'Customer sent page 2 only' },
    );
  });

  it('still replaces silently when no replacer is given (system overwrites)', async () => {
    await service.upsertByCode({
      resourceId: 'ctr-1',
      resource: 'contracts',
      code: 'contract_pdf',
      file,
    });

    expect(filesRepository.deleteByCode).toHaveBeenCalledWith(
      'ctr-1',
      'contracts',
      'contract_pdf',
      undefined,
    );
  });

  it('marks the live row current and the soft-deleted ones superseded', async () => {
    filesRepository.findVersionHistory.mockResolvedValue([
      {
        id: 'v2',
        name: 'corrected.pdf',
        url: 'u2',
        size: 2,
        mimeType: 'application/pdf',
        createdAt: new Date('2026-07-20T10:00:00Z'),
        deletedAt: null,
        replacedByUserId: null,
        replaceReason: null,
      },
      {
        id: 'v1',
        name: 'original.pdf',
        url: 'u1',
        size: 1,
        mimeType: 'application/pdf',
        createdAt: new Date('2026-07-18T10:00:00Z'),
        deletedAt: new Date('2026-07-20T10:00:00Z'),
        replacedByUserId: 'gl-user-1',
        replaceReason: 'Wrong page order',
      },
    ]);

    const versions = await service.versionHistory(
      'ctr-1',
      'contracts',
      'bill_of_lading',
    );

    expect(versions[0]).toMatchObject({ id: 'v2', isCurrent: true, replacedAt: null });
    expect(versions[1]).toMatchObject({
      id: 'v1',
      isCurrent: false,
      replacedByUserId: 'gl-user-1',
      replaceReason: 'Wrong page order',
    });
    // The customer's original is still readable — that is the whole point.
    expect(versions[1].url).toBe('u1');
  });
});
