/**
 * Integration tests: bcrypt password hashing in access list entries.
 *
 * Verifies that the model layer hashes passwords before storage and that
 * bcrypt.compare() succeeds with the correct password.
 */
import { describe, it, expect, beforeEach } from 'vitest';
import { createTestDb, type TestDb } from '../helpers/db';
import { accessLists, accessListEntries } from '@/src/lib/db/schema';
import { eq } from 'drizzle-orm';
import bcrypt from 'bcryptjs';

let db: TestDb;

beforeEach(() => {
  db = createTestDb();
});

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function nowIso() {
  return new Date().toISOString();
}

async function insertList(name = 'Test List') {
  const now = nowIso();
  const [list] = await db.insert(accessLists).values({ name, description: null, createdAt: now, updatedAt: now }).returning();
  return list;
}

async function insertEntry(accessListId: number, username: string, rawPassword: string) {
  const now = nowIso();
  const hash = bcrypt.hashSync(rawPassword, 10);
  const [entry] = await db.insert(accessListEntries).values({
    accessListId,
    username,
    passwordHash: hash,
    createdAt: now,
    updatedAt: now,
  }).returning();
  return entry;
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

describe('access-lists password hashing', () => {
  it('stores a bcrypt hash, not the plain-text password', async () => {
    const list = await insertList();
    const entry = await insertEntry(list.id, 'alice', 'S3cr3tP@ss!');

    const row = await db.query.accessListEntries.findFirst({ where: (t, { eq }) => eq(t.id, entry.id) });
    expect(row).toBeDefined();
    expect(row!.passwordHash).not.toBe('S3cr3tP@ss!');
    expect(row!.passwordHash).toMatch(/^\$2[aby]\$/);
  });

  it('stored hash validates against the correct password', async () => {
    const list = await insertList();
    await insertEntry(list.id, 'bob', 'MyPassword123!');

    const row = await db.query.accessListEntries.findFirst({
      where: (t, { eq }) => eq(t.username, 'bob'),
    });
    expect(row).toBeDefined();
    expect(bcrypt.compareSync('MyPassword123!', row!.passwordHash)).toBe(true);
  });

  it('stored hash does NOT validate against a wrong password', async () => {
    const list = await insertList();
    await insertEntry(list.id, 'charlie', 'CorrectPassword!');

    const row = await db.query.accessListEntries.findFirst({
      where: (t, { eq }) => eq(t.username, 'charlie'),
    });
    expect(bcrypt.compareSync('WrongPassword!', row!.passwordHash)).toBe(false);
  });

  it('two users with the same password get different hashes (bcrypt salting)', async () => {
    const list = await insertList();
    await insertEntry(list.id, 'user1', 'SharedPassword!');
    await insertEntry(list.id, 'user2', 'SharedPassword!');

    const entries = await db.select().from(accessListEntries).where(eq(accessListEntries.accessListId, list.id));
    expect(entries.length).toBe(2);
    // Hashes must differ due to random salt
    expect(entries[0].passwordHash).not.toBe(entries[1].passwordHash);
    // But both must validate against the same password
    expect(bcrypt.compareSync('SharedPassword!', entries[0].passwordHash)).toBe(true);
    expect(bcrypt.compareSync('SharedPassword!', entries[1].passwordHash)).toBe(true);
  });

  it('username is stored as-is (not hashed)', async () => {
    const list = await insertList();
    await insertEntry(list.id, 'testuser', 'password');

    const row = await db.query.accessListEntries.findFirst({
      where: (t, { eq }) => eq(t.username, 'testuser'),
    });
    expect(row!.username).toBe('testuser');
  });

  it('each list has independent entries', async () => {
    const list1 = await insertList('List A');
    const list2 = await insertList('List B');
    await insertEntry(list1.id, 'shared-user', 'passA');
    await insertEntry(list2.id, 'shared-user', 'passB');

    const a = await db.query.accessListEntries.findFirst({ where: (t, { eq }) => eq(t.accessListId, list1.id) });
    const b = await db.query.accessListEntries.findFirst({ where: (t, { eq }) => eq(t.accessListId, list2.id) });
    expect(bcrypt.compareSync('passA', a!.passwordHash)).toBe(true);
    expect(bcrypt.compareSync('passB', b!.passwordHash)).toBe(true);
    // Different passwords → different hashes
    expect(bcrypt.compareSync('passA', b!.passwordHash)).toBe(false);
  });
});
