import { describe, it, expect } from 'vitest';
import { createPending, activate, endTenancy } from '@/lib/services/berth-tenancies.service';
import { makeBerth, makeClient, makePort, makeYacht, makeAuditMeta } from '../helpers/factories';
import { ConflictError } from '@/lib/errors';

describe('tenancy exclusivity', () => {
  it('two concurrent activates on same berth: one succeeds, one throws ConflictError', async () => {
    const port = await makePort();
    const berth = await makeBerth({ portId: port.id });
    const clientA = await makeClient({ portId: port.id });
    const clientB = await makeClient({ portId: port.id });
    const yachtA = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: clientA.id,
    });
    const yachtB = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: clientB.id,
    });

    const resA = await createPending(
      port.id,
      {
        berthId: berth.id,
        clientId: clientA.id,
        yachtId: yachtA.id,
        startDate: new Date(),
      },
      makeAuditMeta({ portId: port.id }),
    );
    const resB = await createPending(
      port.id,
      {
        berthId: berth.id,
        clientId: clientB.id,
        yachtId: yachtB.id,
        startDate: new Date(),
      },
      makeAuditMeta({ portId: port.id }),
    );

    const results = await Promise.allSettled([
      activate(resA.id, port.id, {}, makeAuditMeta({ portId: port.id })),
      activate(resB.id, port.id, {}, makeAuditMeta({ portId: port.id })),
    ]);
    const successes = results.filter((r) => r.status === 'fulfilled');
    const failures = results.filter((r) => r.status === 'rejected');
    expect(successes).toHaveLength(1);
    expect(failures).toHaveLength(1);
    expect((failures[0] as PromiseRejectedResult).reason).toBeInstanceOf(ConflictError);
  });

  it('activating a second tenancy after first is ended succeeds', async () => {
    const port = await makePort();
    const berth = await makeBerth({ portId: port.id });
    const clientA = await makeClient({ portId: port.id });
    const clientB = await makeClient({ portId: port.id });
    const yachtA = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: clientA.id,
    });
    const yachtB = await makeYacht({
      portId: port.id,
      ownerType: 'client',
      ownerId: clientB.id,
    });

    const resA = await createPending(
      port.id,
      { berthId: berth.id, clientId: clientA.id, yachtId: yachtA.id, startDate: new Date() },
      makeAuditMeta({ portId: port.id }),
    );
    await activate(resA.id, port.id, {}, makeAuditMeta({ portId: port.id }));
    await endTenancy(resA.id, port.id, { endDate: new Date() }, makeAuditMeta({ portId: port.id }));

    const resB = await createPending(
      port.id,
      { berthId: berth.id, clientId: clientB.id, yachtId: yachtB.id, startDate: new Date() },
      makeAuditMeta({ portId: port.id }),
    );
    await expect(
      activate(resB.id, port.id, {}, makeAuditMeta({ portId: port.id })),
    ).resolves.toBeDefined();
  });
});
