import { afterEach, describe, expect, it } from 'vitest';
import { AuthService } from '../../src/services/auth-service.js';
import { getAuthorization, PERMISSIONS } from '../../src/services/rbac.js';
import { UserService } from '../../src/services/user-service.js';
import { verifyPassword } from '../../src/services/passwords.js';
import { createTestContext } from '../helpers.js';

const contexts: Awaited<ReturnType<typeof createTestContext>>[] = [];
afterEach(async () => { while (contexts.length) await contexts.pop()!.db.close(); });

describe('امنیت رمز و RBAC', () => {
  it('مرز مجوزهای سه نقش را اعمال می‌کند', async () => {
    const context = await createTestContext(); contexts.push(context);
    const admin = await getAuthorization(context.db, context.users.admin!.id);
    const supply = await getAuthorization(context.db, context.users.supply!.id);
    const warehouse = await getAuthorization(context.db, context.users.warehouse!.id);
    expect(admin.permissions.has(PERMISSIONS.USERS_MANAGE)).toBe(true);
    expect(supply.permissions.has(PERMISSIONS.DELIVERIES_APPROVE)).toBe(true);
    expect(supply.permissions.has(PERMISSIONS.USERS_MANAGE)).toBe(false);
    expect(warehouse.permissions).toEqual(new Set([PERMISSIONS.DELIVERIES_WAREHOUSE_VIEW, PERMISSIONS.DELIVERIES_WAREHOUSE_ACT]));
  });

  it('reset رمز یک‌بارنمایش، hash Argon2id، اجبار تغییر و ابطال نشست را انجام می‌دهد', async () => {
    const context = await createTestContext(); contexts.push(context);
    const auth = new AuthService(context.db);
    const before = await context.db.query<{ session_version: number }>('SELECT session_version FROM users WHERE id = $1', [context.users.supply!.id]);
    const temporary = await auth.resetPassword(context.users.admin!.id, context.users.supply!.id, { actorId: context.users.admin!.id });
    const stored = await context.db.query<{ password_hash: string; force_password_change: boolean; session_version: number }>('SELECT password_hash, force_password_change, session_version FROM users WHERE id = $1', [context.users.supply!.id]);
    expect(stored.rows[0]!.password_hash.startsWith('$argon2id$')).toBe(true);
    expect(stored.rows[0]!.password_hash).not.toContain(temporary);
    expect(await verifyPassword(stored.rows[0]!.password_hash, temporary)).toBe(true);
    expect(stored.rows[0]!.force_password_change).toBe(true);
    expect(stored.rows[0]!.session_version).toBe(before.rows[0]!.session_version + 1);
    const loggedIn = await auth.authenticate('supply', temporary, {});
    expect(loggedIn?.forcePasswordChange).toBe(true);
    const nextVersion = await auth.changeOwnPassword(context.users.supply!.id, temporary, 'A-very-strong-password-123!', {});
    expect(nextVersion).toBe(stored.rows[0]!.session_version + 1);
    const auditText = JSON.stringify((await context.db.query('SELECT * FROM audit_events')).rows);
    expect(auditText).not.toContain(temporary);
  });

  it('آخرین مدیر کل فعال قابل غیرفعال‌سازی نیست', async () => {
    const context = await createTestContext(); contexts.push(context);
    const service = new UserService(context.db);
    await expect(service.setActive(context.users.admin!.id, context.users.admin!.id, false, {})).rejects.toMatchObject({ code: 'LAST_SUPER_ADMIN' });
  });
});

