/**
 * listAccess — getAccessibleListIds access-scope query tests (D-04).
 *
 * DB-backed tests. Uses the test DB harness from Plan 01 (test/setup.ts).
 * Tests 5-8 verify that getAccessibleListIds returns exactly the right set
 * of list IDs for a given user: owned + shared, no more, no less.
 *
 * Critical: Test 7 is the D-04 negative assertion — a private non-shared list
 * owned by another user MUST NOT appear in the result.
 *
 * Run: pnpm --filter @familysync/api exec vitest run tests/lib/listAccess.test.ts
 */

import { describe, it, expect } from 'vitest';
import { randomUUID } from 'node:crypto';
import { db } from '../../src/db/client.js';
import { users, lists, listShares } from '../../src/db/schema.js';
import { getAccessibleListIds } from '../../src/lib/listAccess.js';

// Seed helpers — insert minimal rows and return their IDs.
// oidc_sub uses a UUID suffix so rows never collide across test runs
// even when the users table is not truncated between runs.
async function seedUser(label: string): Promise<number> {
  const [result] = await db
    .insert(users)
    .values({
      oidcIss: 'https://auth.test',
      oidcSub: `sub-${label}-${randomUUID()}`,
      displayName: `User ${label}`,
      color: '#000000',
    })
    .$returningId();
  return result.id;
}

async function seedList(ownerId: number, isShared = false): Promise<number> {
  const [result] = await db
    .insert(lists)
    .values({
      ownerId,
      name: `List ${Math.random()}`,
      isShared,
    })
    .$returningId();
  return result.id;
}

async function shareList(listId: number, userId: number): Promise<void> {
  await db.insert(listShares).values({ listId, userId });
}

describe('listAccess — getAccessibleListIds (D-04)', () => {
  it('returns ids of lists the user owns (Test 5)', async () => {
    const userId = await seedUser('owner-5');
    const listId1 = await seedList(userId);
    const listId2 = await seedList(userId);

    const ids = await getAccessibleListIds(userId);

    expect(ids).toContain(listId1);
    expect(ids).toContain(listId2);
  });

  it('returns ids of lists shared to the user via list_shares (Test 6)', async () => {
    const ownerUserId = await seedUser('owner-6');
    const sharedUserId = await seedUser('shared-6');
    const sharedListId = await seedList(ownerUserId, true);
    await shareList(sharedListId, sharedUserId);

    const ids = await getAccessibleListIds(sharedUserId);

    expect(ids).toContain(sharedListId);
  });

  it("does NOT return another user's private non-shared list id (Test 7 — D-04 negative)", async () => {
    const ownerUserId = await seedUser('owner-7');
    const otherUserId = await seedUser('other-7');
    const privateListId = await seedList(ownerUserId, false);
    // Deliberately NOT sharing privateListId with otherUserId

    const ids = await getAccessibleListIds(otherUserId);

    expect(ids).not.toContain(privateListId);
  });

  it('result has no duplicates when a list is both owned and shared to the owner (Test 8)', async () => {
    const userId = await seedUser('owner-8');
    const listId = await seedList(userId, true);
    // Erroneously share the list back to the owner (degenerate case)
    await shareList(listId, userId);

    const ids = await getAccessibleListIds(userId);

    const occurrences = ids.filter((id) => id === listId);
    expect(occurrences).toHaveLength(1);
  });
});
