import { describe, expect, it, beforeAll, afterAll } from 'vitest';
import { createJwtUtils, extractAuth, requireRole } from '../index.js';

const SECRET = 'test-jwt-secret-at-least-32-chars-long!!';
let validAccessToken: string;
let refreshToken: string;

describe('extractAuth', () => {
  beforeAll(async () => {
    process.env.JWT_SECRET = SECRET;
    const jwt = createJwtUtils({ issuer: 'test-issuer' });
    validAccessToken = await jwt.createAccessToken({
      sub: 'user-1',
      email: 'test@example.com',
      role: 'admin',
      productId: 'lysnrai',
    });
    refreshToken = await jwt.createRefreshToken({ sub: 'user-1' });
  });

  afterAll(() => {
    delete process.env.JWT_SECRET;
  });

  it('extracts auth from valid Bearer token', async () => {
    const req = { headers: { authorization: `Bearer ${validAccessToken}` } };
    const payload = await extractAuth(req);
    expect(payload.sub).toBe('user-1');
    expect(payload.email).toBe('test@example.com');
    expect(payload.role).toBe('admin');
    expect(payload.productId).toBe('lysnrai');
    expect(payload.type).toBe('access');
  });

  it('throws 401 when no authorization header', async () => {
    const req = { headers: {} };
    await expect(extractAuth(req)).rejects.toMatchObject({
      statusCode: 401,
      message: 'Unauthorized',
    });
  });

  it('throws 401 when authorization header is not Bearer', async () => {
    const req = { headers: { authorization: 'Basic abc123' } };
    await expect(extractAuth(req)).rejects.toMatchObject({
      statusCode: 401,
      message: 'Unauthorized',
    });
  });

  it('throws 401 for invalid token', async () => {
    const req = { headers: { authorization: 'Bearer garbage.not.valid' } };
    await expect(extractAuth(req)).rejects.toMatchObject({
      statusCode: 401,
      message: 'Invalid or expired token',
    });
  });

  it('throws 401 for refresh token (requires access type)', async () => {
    const req = { headers: { authorization: `Bearer ${refreshToken}` } };
    await expect(extractAuth(req)).rejects.toMatchObject({
      statusCode: 401,
      message: 'Invalid or expired token',
    });
  });

  it('throws 401 for empty Bearer value', async () => {
    const req = { headers: { authorization: 'Bearer ' } };
    await expect(extractAuth(req)).rejects.toMatchObject({
      statusCode: 401,
    });
  });
});

describe('requireRole', () => {
  beforeAll(() => {
    process.env.JWT_SECRET = SECRET;
  });

  afterAll(() => {
    delete process.env.JWT_SECRET;
  });

  it('passes when role matches', async () => {
    const req = { headers: { authorization: `Bearer ${validAccessToken}` } };
    const payload = await requireRole(req, 'admin');
    expect(payload.sub).toBe('user-1');
    expect(payload.role).toBe('admin');
  });

  it('passes when role is in allowed list', async () => {
    const req = { headers: { authorization: `Bearer ${validAccessToken}` } };
    const payload = await requireRole(req, 'viewer', 'admin', 'super_admin');
    expect(payload.role).toBe('admin');
  });

  it('throws 403 when role does not match', async () => {
    const req = { headers: { authorization: `Bearer ${validAccessToken}` } };
    await expect(requireRole(req, 'super_admin')).rejects.toMatchObject({
      statusCode: 403,
      message: 'Insufficient permissions',
    });
  });

  it('passes with no roles specified (any authenticated user)', async () => {
    const req = { headers: { authorization: `Bearer ${validAccessToken}` } };
    const payload = await requireRole(req);
    expect(payload.sub).toBe('user-1');
  });

  it('throws 401 when no auth header (before checking role)', async () => {
    const req = { headers: {} };
    await expect(requireRole(req, 'admin')).rejects.toMatchObject({
      statusCode: 401,
    });
  });
});
