/**
 * Integration test: waiting-list next-in-line notification.
 *
 * `notifyWaitlistNextInLine` surfaces the #1 client on a berth's waiting list
 * to staff who hold `berths.manage_waiting_list` when the berth frees up.
 * `createNotification` is mocked so we assert the fan-out decision (who, what
 * payload) without exercising the full notification insert / preferences /
 * queue path (covered by notification-lifecycle.test.ts).
 */

import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';

vi.mock('@/lib/services/notifications.service', () => ({
  createNotification: vi.fn(async () => null),
}));

import { db } from '@/lib/db';
import { user, roles, userPortRoles, type RolePermissions } from '@/lib/db/schema/users';
import { berthWaitingList } from '@/lib/db/schema/berths';
import { createNotification } from '@/lib/services/notifications.service';

let notifyWaitlistNextInLine: typeof import('@/lib/services/next-in-line-notify.service').notifyWaitlistNextInLine;
let makePort: typeof import('../helpers/factories').makePort;
let makeBerth: typeof import('../helpers/factories').makeBerth;
let makeClient: typeof import('../helpers/factories').makeClient;

beforeAll(async () => {
  notifyWaitlistNextInLine = (await import('@/lib/services/next-in-line-notify.service'))
    .notifyWaitlistNextInLine;
  const f = await import('../helpers/factories');
  makePort = f.makePort;
  makeBerth = f.makeBerth;
  makeClient = f.makeClient;
});

beforeEach(() => {
  vi.mocked(createNotification).mockClear();
});

async function seedManager(portId: string, perms: RolePermissions): Promise<string> {
  const suffix = crypto.randomUUID().slice(0, 8);
  const [u] = await db
    .insert(user)
    .values({
      id: crypto.randomUUID(),
      name: `WL User ${suffix}`,
      email: `wl-${suffix}@test.local`,
      emailVerified: true,
    })
    .returning();
  const [role] = await db
    .insert(roles)
    .values({ name: `WL Role ${suffix}`, permissions: perms })
    .returning();
  await db.insert(userPortRoles).values({ userId: u!.id, portId, roleId: role!.id });
  return u!.id;
}

describe('notifyWaitlistNextInLine', () => {
  it('notifies managers about the #1 client when a berth frees up', async () => {
    const port = await makePort();
    const berth = await makeBerth({ portId: port.id, overrides: { mooringNumber: 'WL1' } });
    const first = await makeClient({ portId: port.id, overrides: { fullName: 'Ada First' } });
    const second = await makeClient({ portId: port.id, overrides: { fullName: 'Bob Second' } });

    await db.insert(berthWaitingList).values([
      { berthId: berth.id, clientId: first.id, position: 1, priority: 'high' },
      { berthId: berth.id, clientId: second.id, position: 2, priority: 'normal' },
    ]);

    await seedManager(port.id, { berths: { manage_waiting_list: true } } as RolePermissions);

    await notifyWaitlistNextInLine({
      portId: port.id,
      berthId: berth.id,
      mooringNumber: 'WL1',
    });

    expect(createNotification).toHaveBeenCalledTimes(1);
    const payload = vi.mocked(createNotification).mock.calls[0]![0];
    expect(payload.type).toBe('berth_waiting_list_update');
    expect(payload.title).toContain('WL1');
    // #1 by position (Ada), with the high-priority note.
    expect(payload.description).toContain('Ada First');
    expect(payload.description).toContain('high priority');
    expect(payload.dedupeKey).toBe(`berth-waitlist-available:${berth.id}`);
    expect(payload.entityId).toBe(berth.id);
  });

  it('does not notify users who lack manage_waiting_list', async () => {
    const port = await makePort();
    const berth = await makeBerth({ portId: port.id, overrides: { mooringNumber: 'WL2' } });
    const c = await makeClient({ portId: port.id, overrides: { fullName: 'Carol Only' } });
    await db.insert(berthWaitingList).values({ berthId: berth.id, clientId: c.id, position: 1 });

    await seedManager(port.id, { berths: { manage_waiting_list: false } } as RolePermissions);

    await notifyWaitlistNextInLine({ portId: port.id, berthId: berth.id, mooringNumber: 'WL2' });

    expect(createNotification).not.toHaveBeenCalled();
  });

  it('no-ops when the waiting list is empty', async () => {
    const port = await makePort();
    const berth = await makeBerth({ portId: port.id, overrides: { mooringNumber: 'WL3' } });
    await seedManager(port.id, { berths: { manage_waiting_list: true } } as RolePermissions);

    await notifyWaitlistNextInLine({ portId: port.id, berthId: berth.id, mooringNumber: 'WL3' });

    expect(createNotification).not.toHaveBeenCalled();
  });
});
