import { describe, expect, it } from 'vitest';
import { AppError } from '../errors.js';
import { requireAnyRole, requireOwnerOrAdmin, requireRole, type CurrentUser } from '../auth.js';

const customer: CurrentUser = { id: 'user-a', email: 'a@example.com', role: 'customer' };
const admin: CurrentUser = { id: 'user-admin', email: 'admin@example.com', role: 'admin' };
const posCashier: CurrentUser = { id: 'user-cashier', email: 'cashier@example.com', role: 'pos_cashier' };
const posManager: CurrentUser = { id: 'user-manager', email: 'manager@example.com', role: 'pos_manager' };

function codeOf(fn: () => void): string | undefined {
  try {
    fn();
  } catch (error) {
    return error instanceof AppError ? error.code : undefined;
  }
  return undefined;
}

describe('requireRole', () => {
  it('allows a matching role', () => {
    expect(() => requireRole(admin, 'admin')).not.toThrow();
    expect(() => requireRole(customer, 'customer')).not.toThrow();
  });

  it('throws 403 FORBIDDEN for a missing role', () => {
    expect(codeOf(() => requireRole(customer, 'admin'))).toBe('FORBIDDEN');
  });
});

describe('requireAnyRole', () => {
  it('allows a user holding any of the allowed roles', () => {
    expect(() => requireAnyRole(admin, ['pos_cashier', 'pos_manager', 'admin'])).not.toThrow();
    expect(() => requireAnyRole(posCashier, ['pos_cashier', 'pos_manager'])).not.toThrow();
    expect(() => requireAnyRole(posManager, ['pos_cashier', 'pos_manager'])).not.toThrow();
  });

  it('throws 403 FORBIDDEN when no role matches', () => {
    expect(codeOf(() => requireAnyRole(customer, ['pos_cashier', 'pos_manager']))).toBe('FORBIDDEN');
    expect(codeOf(() => requireAnyRole(posCashier, ['pos_manager']))).toBe('FORBIDDEN');
  });

  it('throws 403 FORBIDDEN for empty role list', () => {
    expect(codeOf(() => requireAnyRole(admin, []))).toBe('FORBIDDEN');
  });
});

describe('requireOwnerOrAdmin', () => {
  it('allows the owner regardless of role', () => {
    expect(() => requireOwnerOrAdmin(customer, 'user-a')).not.toThrow();
  });

  it('allows an admin on any user', () => {
    expect(() => requireOwnerOrAdmin(admin, 'user-b')).not.toThrow();
  });

  it('throws 403 FORBIDDEN for a non-owner customer', () => {
    expect(codeOf(() => requireOwnerOrAdmin(customer, 'user-b'))).toBe('FORBIDDEN');
  });
});
