import { describe, expect, it } from 'vitest';
import { buildVisibilityWhere, canUserSeeTask } from './visibility.js';

function makeUser(overrides: Partial<Express.User> = {}): Express.User {
  return {
    id: 'user-1', email: 'u@x.com', name: 'u', avatarUrl: null,
    role: 'user', status: 'active',
    orgIds: [], defaultVisibility: 'private', defaultVisibilityOrgId: null,
    ...overrides,
  };
}

describe('buildVisibilityWhere', () => {
  it('admin sees everything (1=1)', () => {
    const w = buildVisibilityWhere(makeUser({ role: 'admin' }), 'lt');
    expect(w.clause).toBe('1=1');
    expect(w.params).toEqual([]);
  });

  it('user with no orgs: owner or public only', () => {
    const w = buildVisibilityWhere(makeUser(), 'lt');
    expect(w.clause).toContain('lt.owner_id = ?');
    expect(w.clause).toContain("lt.visibility = 'public'");
    expect(w.clause).toContain('IN (NULL)'); // empty orgs → never matches
    expect(w.params).toEqual(['user-1']);
  });

  it('user with orgs: owner or public or same-org', () => {
    const w = buildVisibilityWhere(makeUser({ orgIds: ['10', '20'] }), 'lt');
    expect(w.clause).toMatch(/lt\.visibility_scope_org_id IN \(\?,\?\)/);
    expect(w.params).toEqual(['user-1', '10', '20']);
  });

  it('respects custom table alias', () => {
    const w = buildVisibilityWhere(makeUser(), 'j');
    expect(w.clause).toContain('j.owner_id');
    expect(w.clause).not.toContain('lt.');
  });
});

describe('canUserSeeTask', () => {
  const adminUser = makeUser({ role: 'admin' });
  const aliceNoOrg = makeUser({ id: 'alice' });
  const bobOrg10 = makeUser({ id: 'bob', orgIds: ['10'] });

  it('admin sees private tasks of others', () => {
    const t = { ownerId: 'someone-else', visibility: 'private' as const, visibilityScopeOrgId: null };
    expect(canUserSeeTask(adminUser, t)).toBe(true);
  });

  it('owner sees own private task', () => {
    const t = { ownerId: 'alice', visibility: 'private' as const, visibilityScopeOrgId: null };
    expect(canUserSeeTask(aliceNoOrg, t)).toBe(true);
  });

  it('non-owner cannot see another user\'s private task', () => {
    const t = { ownerId: 'someone-else', visibility: 'private' as const, visibilityScopeOrgId: null };
    expect(canUserSeeTask(aliceNoOrg, t)).toBe(false);
  });

  it('public task is visible to anyone', () => {
    const t = { ownerId: 'someone-else', visibility: 'public' as const, visibilityScopeOrgId: null };
    expect(canUserSeeTask(aliceNoOrg, t)).toBe(true);
  });

  it('org task: same org member can see', () => {
    const t = { ownerId: 'someone-else', visibility: 'org' as const, visibilityScopeOrgId: '10' };
    expect(canUserSeeTask(bobOrg10, t)).toBe(true);
  });

  it('org task: different org member cannot see', () => {
    const t = { ownerId: 'someone-else', visibility: 'org' as const, visibilityScopeOrgId: '99' };
    expect(canUserSeeTask(bobOrg10, t)).toBe(false);
  });

  it('org task with null scope: only owner can see', () => {
    const t = { ownerId: 'alice', visibility: 'org' as const, visibilityScopeOrgId: null };
    expect(canUserSeeTask(aliceNoOrg, t)).toBe(true);
    expect(canUserSeeTask(bobOrg10, t)).toBe(false);
  });

  it('owner with null ownerId: not matched (null !== null check skipped)', () => {
    const t = { ownerId: null, visibility: 'private' as const, visibilityScopeOrgId: null };
    expect(canUserSeeTask(aliceNoOrg, t)).toBe(false);
  });
});
