/**
 * End-to-end auth flow test: create token → extract auth → require role.
 * Exercises the full JWT lifecycle without network calls.
 */
import { describe, expect, it, beforeAll, afterAll } from 'vitest';
import {
  createJwtUtils,
  extractAuth,
  requireRole,
  hashPassword,
  verifyPassword,
} from '../index.js';

const SECRET = 'e2e-test-jwt-secret-at-least-32-chars!!';

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

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

  it('full flow: login credentials → JWT → authenticated request → role check', async () => {
    // 1. Simulate password verification (login step)
    const storedHash = await hashPassword('SecretPass123!');
    const passwordValid = await verifyPassword('SecretPass123!', storedHash);
    expect(passwordValid).toBe(true);

    // 2. Issue access token (platform-service would do this)
    const jwt = createJwtUtils({ issuer: 'lysnrai' });
    const accessToken = await jwt.createAccessToken({
      sub: 'user-admin-001',
      email: 'admin@lysnrai.com',
      role: 'super_admin',
      productId: 'lysnrai',
    });
    expect(typeof accessToken).toBe('string');

    // 3. Simulate authenticated request (any service receives this)
    const req = { headers: { authorization: `Bearer ${accessToken}` } };
    const auth = await extractAuth(req);
    expect(auth.sub).toBe('user-admin-001');
    expect(auth.email).toBe('admin@lysnrai.com');
    expect(auth.role).toBe('super_admin');
    expect(auth.productId).toBe('lysnrai');

    // 4. Role-gated endpoint check
    const adminAuth = await requireRole(req, 'super_admin', 'admin');
    expect(adminAuth.sub).toBe('user-admin-001');

    // 5. Role rejection for wrong role
    await expect(requireRole(req, 'viewer')).rejects.toMatchObject({
      statusCode: 403,
    });
  });

  it('refresh token cannot be used for authenticated requests', async () => {
    const jwt = createJwtUtils({ issuer: 'lysnrai' });
    const refreshToken = await jwt.createRefreshToken({
      sub: 'user-001',
      productId: 'lysnrai',
    });

    const req = { headers: { authorization: `Bearer ${refreshToken}` } };
    await expect(extractAuth(req)).rejects.toMatchObject({
      statusCode: 401,
      message: 'Invalid or expired token',
    });
  });

  it('cross-issuer tokens are rejected by verifyToken but pass extractAuth (no issuer check)', async () => {
    // extractAuth only checks type=access via jwtVerify without issuer
    // But verifyToken checks issuer — this is the cross-service security model
    const jwtA = createJwtUtils({ issuer: 'lysnrai' });
    const jwtB = createJwtUtils({ issuer: 'mindlyst' });

    const tokenA = await jwtA.createAccessToken({
      sub: 'u1',
      email: 'a@b.com',
      role: 'user',
    });

    // verifyToken with wrong issuer rejects
    const resultB = await jwtB.verifyToken(tokenA);
    expect(resultB).toBeNull();

    // verifyToken with correct issuer passes
    const resultA = await jwtA.verifyToken(tokenA);
    expect(resultA).not.toBeNull();
    expect(resultA!.sub).toBe('u1');
  });

  it('wrong password fails login flow before token issuance', async () => {
    const storedHash = await hashPassword('CorrectPassword');
    const passwordValid = await verifyPassword('WrongPassword', storedHash);
    expect(passwordValid).toBe(false);
    // No token should be issued — the flow stops here
  });
});
