/**
 * USER_JOURNEYS.md §11 — admin reads/writes config via the API. Asserts
 * the validation rules baked into [backend/src/handlers/admin.rs:patch_config].
 */
import { test, expect } from '../../fixtures/test';

test.describe('Admin — config API', () => {
  test('PATCH /admin/config persists numeric values', async ({ api, adminToken }) => {
    await api.patchConfig(adminToken, { max_image_size_mb: '25' });
    const cfg = await api.getConfig(adminToken);
    expect(cfg.max_image_size_mb).toBe('25');
    // Restore the default so other specs see the seeded value.
    await api.patchConfig(adminToken, { max_image_size_mb: '20' });
  });

  test('non-numeric value for a numeric key is rejected', async ({ adminToken }) => {
    const res = await fetch(
      (process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101') + '/api/v1/admin/config',
      {
        method: 'PATCH',
        headers: { Authorization: `Bearer ${adminToken}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({ max_image_size_mb: 'not-a-number' }),
      }
    );
    expect(res.status).toBe(400);
  });

  test('unknown config key is rejected (whitelist enforced)', async ({ adminToken }) => {
    const res = await fetch(
      (process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101') + '/api/v1/admin/config',
      {
        method: 'PATCH',
        headers: { Authorization: `Bearer ${adminToken}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({ totally_fake_key: '1' }),
      }
    );
    expect(res.status).toBe(400);
  });

  test('toggle keys accept true/false but not arbitrary strings', async ({ api, adminToken }) => {
    await api.patchConfig(adminToken, { upload_rate_enabled: 'true' });
    let cfg = await api.getConfig(adminToken);
    expect(cfg.upload_rate_enabled).toBe('true');

    await api.patchConfig(adminToken, { upload_rate_enabled: 'false' });
    cfg = await api.getConfig(adminToken);
    expect(cfg.upload_rate_enabled).toBe('false');

    const res = await fetch(
      (process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101') + '/api/v1/admin/config',
      {
        method: 'PATCH',
        headers: { Authorization: `Bearer ${adminToken}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({ upload_rate_enabled: 'maybe' }),
      }
    );
    expect(res.status).toBe(400);
  });

  test('privacy_note round-trips verbatim, preserving whitespace + newlines', async ({
    api,
    adminToken,
  }) => {
    const note =
      '   Datenschutz\n  • Wir verwenden keine Cookies.\n  • Alles bleibt im Browser.\n\n— Dein Host';
    await api.patchConfig(adminToken, { privacy_note: note });
    const cfg = await api.getConfig(adminToken);
    expect(cfg.privacy_note).toBe(note);
    await api.patchConfig(adminToken, { privacy_note: '' });
  });
  test('quota_tolerance = 0 is rejected, with a pointer to the real off-switch', async ({
    api,
    adminToken,
  }) => {
    // Zero is inside the documented 0–1 range and catastrophic: the per-user limit is
    // `free_disk * tolerance / active_uploaders`, so 0 refuses EVERY upload — mid-event, with
    // "Du hast dein Upload-Limit für dieses Event erreicht", which names the wrong cause
    // entirely. `storage_quota_enabled` is what an admin reaching for an off-switch wants.
    const res = await fetch(
      (process.env.E2E_FRONTEND_URL ?? 'http://localhost:3101') + '/api/v1/admin/config',
      {
        method: 'PATCH',
        headers: { Authorization: `Bearer ${adminToken}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({ quota_tolerance: '0' }),
      }
    );
    expect(res.status).toBe(400);
    expect(
      (await res.text()).toLowerCase(),
      'the error must name the switch the admin actually wanted'
    ).toContain('speicher-quote');

    // The value is untouched — validation fully precedes any write.
    expect((await api.getConfig(adminToken)).quota_tolerance).toBe('0.75');
  });

  test('a very small quota_tolerance is still accepted', async ({ api, adminToken }) => {
    // The mirror. Rejecting 0 must not become a floor: small tolerances are how a large disk is
    // throttled to a sensible per-guest ceiling, and how the quota specs steer it (~1e-5 on a
    // 174 GB volume). A floor of 0.01 would forbid real configurations to prevent one typo.
    await api.patchConfig(adminToken, { quota_tolerance: '0.00001' });
    expect((await api.getConfig(adminToken)).quota_tolerance).toBe('0.00001');
    await api.patchConfig(adminToken, { quota_tolerance: '0.75' });
  });
});

test.describe('Admin — stats', () => {
  test('GET /admin/stats returns matching counts after seeding users', async ({
    api,
    adminToken,
    guest,
  }) => {
    await guest('Stat1');
    await guest('Stat2');
    await guest('Stat3');
    const stats = await api.getStats(adminToken);
    // Deterministic after the per-test truncate: 3 seeded guests + the Admin account
    // (recreated by the adminToken fixture's login) = exactly 4. An exact assertion
    // catches undercount/overcount regressions a `>= 3` lower bound would miss.
    expect(stats.user_count).toBe(4);
    expect(typeof stats.disk_total_bytes).toBe('number');
  });
});
