/**
 * plan-actions.test — the pure provisioning planner: per-role decisions,
 * email derivation, role-id resolution, password rotation.
 */
import { describe, it, expect } from 'vitest';
import { emailForRole, planRoleActions, resolveRoleIds, rotatedPassword } from '../plan-actions.js';
import type { UatUsersFile } from '../../lib/users-file.js';

describe('emailForRole', () => {
  it('slugs the role into the local part', () => {
    expect(emailForRole('admin', 'uat.local')).toBe('uat.admin@uat.local');
    expect(emailForRole('Super Admin', 'uat.local')).toBe('uat.super-admin@uat.local');
    expect(emailForRole('***', 'uat.local')).toBe('uat.role@uat.local');
  });
});

describe('planRoleActions', () => {
  const existing: UatUsersFile = {
    version: '1',
    application: 'app',
    apiUrl: 'http://x',
    tenant: { name: 'UAT', slug: 'uat' },
    createdAt: 'now',
    users: [{ role: 'admin', email: 'uat.admin@uat.local', password: 'StoredPw!1' }],
  };
  it('verifies stored entries (keeping their password) and creates the rest', () => {
    const actions = planRoleActions({
      roles: ['admin', 'viewer'],
      existing,
      emailDomain: 'uat.local',
      newPasswords: { viewer: 'NewPw!2', admin: 'unused' },
    });
    expect(actions).toEqual([
      { role: 'admin', email: 'uat.admin@uat.local', action: 'verify', password: 'StoredPw!1' },
      { role: 'viewer', email: 'uat.viewer@uat.local', action: 'create', password: 'NewPw!2' },
    ]);
  });
  it('creates everything when no file exists, and throws on a missing generated password', () => {
    const actions = planRoleActions({
      roles: ['viewer'],
      existing: null,
      emailDomain: 'uat.local',
      newPasswords: { viewer: 'Pw!3' },
    });
    expect(actions[0].action).toBe('create');
    expect(() =>
      planRoleActions({ roles: ['ghost'], existing: null, emailDomain: 'uat.local', newPasswords: {} }),
    ).toThrow(/missing generated password/);
  });
});

describe('resolveRoleIds', () => {
  it('matches case-insensitively and reports the unknowns', () => {
    const { resolved, unknown } = resolveRoleIds(
      ['Admin', 'viewer', 'ghost'],
      [
        { id: 'r1', name: 'admin' },
        { id: 'r2', name: 'Viewer' },
      ],
    );
    expect(resolved).toEqual({ Admin: 'r1', viewer: 'r2' });
    expect(unknown).toEqual(['ghost']);
  });
});

describe('rotatedPassword', () => {
  it('derives a different password that keeps complexity classes', () => {
    const rotated = rotatedPassword('Uat!abc123');
    expect(rotated).not.toBe('Uat!abc123');
    expect(rotated).toMatch(/[A-Z]/);
    expect(rotated).toMatch(/[a-z]/);
    expect(rotated).toMatch(/\d/);
    expect(rotated).toMatch(/[^A-Za-z0-9]/);
  });
});
