import { describe, it, expect, beforeEach } from 'vitest';
import type { AuditInput } from '../db/browser-session-repo.js';
import { assertProfileOwner } from './browser-session-auth.js';

interface FakeRepo {
  audit: (input: AuditInput) => void;
  rows: AuditInput[];
}

function makeFakeRepo(): FakeRepo {
  const rows: AuditInput[] = [];
  return {
    rows,
    audit(input: AuditInput) {
      rows.push(input);
    },
  };
}

describe('assertProfileOwner — fail-closed owner enforcement', () => {
  let fake: FakeRepo;

  beforeEach(() => {
    fake = makeFakeRepo();
  });

  it('passes when job.ownerId equals profile.ownerId', () => {
    expect(() =>
      assertProfileOwner(
        { id: 7, ownerId: 'user-a' },
        { id: 'job-1', ownerId: 'user-a' },
        fake,
      ),
    ).not.toThrow();
    expect(fake.rows.length).toBe(0);
  });

  it('throws and audits when job.ownerId is null (legacy / dev-mode jobs)', () => {
    expect(() =>
      assertProfileOwner(
        { id: 7, ownerId: 'user-a' },
        { id: 'job-1', ownerId: null },
        fake,
      ),
    ).toThrow('Browser session profile owner mismatch');
    expect(fake.rows).toHaveLength(1);
    expect(fake.rows[0]).toMatchObject({
      actorUserId: null,
      ownerId: 'user-a',
      profileId: 7,
      action: 'use',
      result: 'error',
      jobId: 'job-1',
    });
    expect(fake.rows[0]!.reason).toContain('job.owner=null');
    expect(fake.rows[0]!.reason).toContain('profile.owner=user-a');
  });

  it('throws and audits when job.ownerId is undefined', () => {
    expect(() =>
      assertProfileOwner(
        { id: 7, ownerId: 'user-a' },
        { id: 'job-2', ownerId: undefined },
        fake,
      ),
    ).toThrow('Browser session profile owner mismatch');
    expect(fake.rows).toHaveLength(1);
    expect(fake.rows[0]!.reason).toContain('job.owner=null');
  });

  it('throws and audits when job.ownerId is empty string', () => {
    expect(() =>
      assertProfileOwner(
        { id: 7, ownerId: 'user-a' },
        { id: 'job-3', ownerId: '' },
        fake,
      ),
    ).toThrow('Browser session profile owner mismatch');
    expect(fake.rows).toHaveLength(1);
  });

  it('throws and audits when job.ownerId differs from profile.ownerId', () => {
    expect(() =>
      assertProfileOwner(
        { id: 9, ownerId: 'user-a' },
        { id: 'job-4', ownerId: 'user-b' },
        fake,
      ),
    ).toThrow('Browser session profile owner mismatch');
    expect(fake.rows).toHaveLength(1);
    expect(fake.rows[0]!.reason).toContain('job.owner=user-b');
    expect(fake.rows[0]!.reason).toContain('profile.owner=user-a');
    expect(fake.rows[0]!.actorUserId).toBe('user-b');
  });
});
