import { describe, it, expect, vi } from 'vitest';
import { createProjectsRepo } from './projects.js';

/**
 * Build a minimal Drizzle mock. Each call to db.select() returns a fresh
 * chain that resolves `where()` to the provided rows for that call.
 *
 * `calls` is an ordered list: the first item is returned for the first
 * db.select() call, the second for the second, and so on.
 */
function makeDb(calls: unknown[][]) {
  let callIndex = 0;
  const selectSpy = vi.fn(() => {
    const rows = calls[callIndex++] ?? [];
    const chain = {
      where: vi.fn().mockResolvedValue(rows),
    } as { where: ReturnType<typeof vi.fn>; from?: ReturnType<typeof vi.fn> };
    // from() returns the chain so .where() can be chained, but also resolves
    // directly (as a thenable) for queries with no .where() call.
    chain.from = vi.fn(() => Object.assign(Promise.resolve(rows), chain));
    return chain;
  });
  return { select: selectSpy };
}

describe('createProjectsRepo — findAllForUser', () => {
  it('filters by userId when user has no team memberships', async () => {
    // First select: teamMembers query → empty
    // Second select: projects query → one owned project
    const db = makeDb([
      [], // teamMembers rows
      [{ id: 'p1', ownerId: 'user-1', teamId: null, ownerType: 'user' }],
    ]);
    const repo = createProjectsRepo(db as never);

    const result = await repo.findAllForUser('user-1');

    expect(db.select).toHaveBeenCalledTimes(2);
    expect(result).toHaveLength(1);
    expect(result[0]?.id).toBe('p1');
  });

  it('includes team projects when user is a team member', async () => {
    // First select: teamMembers → user belongs to one team
    // Second select: projects query → two projects (own + team)
    const db = makeDb([
      [{ teamId: 'team-1' }],
      [
        { id: 'p1', ownerId: 'user-1', teamId: null, ownerType: 'user' },
        { id: 'p2', ownerId: null, teamId: 'team-1', ownerType: 'team' },
      ],
    ]);
    const repo = createProjectsRepo(db as never);

    const result = await repo.findAllForUser('user-1');

    expect(db.select).toHaveBeenCalledTimes(2);
    expect(result).toHaveLength(2);
  });

  it('returns empty array when user has no projects and no teams', async () => {
    const db = makeDb([[], []]);
    const repo = createProjectsRepo(db as never);

    const result = await repo.findAllForUser('user-no-projects');
    expect(result).toHaveLength(0);
  });
});

describe('createProjectsRepo — findAll', () => {
  it('returns all rows without any user filter', async () => {
    const rows = [
      { id: 'p1', ownerId: 'user-1', teamId: null, ownerType: 'user' },
      { id: 'p2', ownerId: 'user-2', teamId: null, ownerType: 'user' },
    ];
    const db = makeDb([rows]);
    const repo = createProjectsRepo(db as never);

    const result = await repo.findAll();
    expect(result).toHaveLength(2);
  });
});
