/**
 * US-1 — bootstrap the first admin via `flask create-admin`.
 *
 * The `make create-admin` target wraps `docker exec mimic flask create-admin …`.
 * These tests exercise the CLI directly through `docker exec` (or whatever
 * runtime the harness exposes via the wrapper), and a follow-up API login to
 * confirm the row was created with role=admin and an argon2 hash that verifies.
 *
 * NOTE: the bootstrap admin (`root` / `rootpass8`) is already created out-of-band
 * before the Playwright suite starts. Test usernames here are scoped to `us1-*`
 * and cleaned up via API in afterAll.
 */
import { test, expect } from '@playwright/test';
import { execSync } from 'node:child_process';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { readFileSync } from 'node:fs';
import { adminToken, deleteUserByUsername, login, makeClient } from '../fixtures/api';

const __dirname = dirname(fileURLToPath(import.meta.url));

const RUNTIME = process.env.MIMIC_CONTAINER_CMD ?? 'docker';
const CONTAINER = process.env.MIMIC_CONTAINER ?? 'mimic';

function runCreateAdmin(user: string, pass: string): {
  stdout: string;
  stderr: string;
  status: number;
} {
  try {
    const stdout = execSync(`${RUNTIME} exec ${CONTAINER} flask create-admin ${user} ${pass}`, {
      stdio: ['ignore', 'pipe', 'pipe'],
      encoding: 'utf8',
    });
    return { stdout, stderr: '', status: 0 };
  } catch (e) {
    const err = e as { status?: number; stdout?: Buffer | string; stderr?: Buffer | string };
    return {
      stdout: typeof err.stdout === 'string' ? err.stdout : err.stdout?.toString() ?? '',
      stderr: typeof err.stderr === 'string' ? err.stderr : err.stderr?.toString() ?? '',
      status: err.status ?? -1,
    };
  }
}

test.describe('US-1 — bootstrap first admin', () => {
  const probeUser = 'us1-probe-admin';
  const dupUser = 'us1-dup-admin';

  test.afterAll(async () => {
    try {
      const token = await adminToken();
      for (const u of [probeUser, dupUser]) {
        await deleteUserByUsername(token, u);
      }
    } catch {
      /* best-effort cleanup */
    }
  });

  test('AC-1.1 — create-admin creates a user with role=admin and an argon2 hash that authenticates', async () => {
    const probePass = 'probepass123';
    const result = runCreateAdmin(probeUser, probePass);
    expect(result.status, `CLI failed: ${result.stderr || result.stdout}`).toBe(0);
    expect(result.stdout).toMatch(/created/i);

    // Roundtrip: the resulting credentials must log in as role=admin.
    const { user } = await login(probeUser, probePass);
    expect(user.username).toBe(probeUser);
    expect(user.role).toBe('admin');
  });

  test('AC-1.2 — fails cleanly when username already exists', async () => {
    // Seed once, then call again.
    runCreateAdmin(dupUser, 'firstpass8');
    const second = runCreateAdmin(dupUser, 'secondpass8');
    expect(second.status).not.toBe(0);
    const combined = (second.stderr + second.stdout).toLowerCase();
    expect(combined).toMatch(/exists|already|error/);
  });

  test('AC-1.3 — refuses passwords shorter than 8 characters', async () => {
    const result = runCreateAdmin('us1-shortpass-user', 'short7');
    expect(result.status).not.toBe(0);
    const combined = (result.stderr + result.stdout).toLowerCase();
    expect(combined).toMatch(/8|length|password/);

    // Make sure the short-password attempt did NOT create a row.
    const probe = await makeClient().post('/auth/login', {
      username: 'us1-shortpass-user',
      password: 'short7',
    });
    expect(probe.status).toBe(401);
  });

  test('AC-1.4 — make create-admin wraps `docker exec mimic flask create-admin` (the bootstrap admin proves it ran via that path)', async () => {
    // The harness step `make create-admin USER=root PASS=rootpass8` is what
    // got the suite to the point where adminToken() works. If that call had
    // been broken, this assertion would have already failed when seeding.
    const token = await adminToken();
    expect(token).toBeTruthy();

    // Defence-in-depth: assert the Makefile target literally invokes
    // `docker exec … flask create-admin`. This is a contract check.
    const makefilePath = resolve(__dirname, '../..', 'Makefile');
    const content = readFileSync(makefilePath, 'utf8');
    expect(content).toMatch(/docker exec .+ flask create-admin/);
  });
});
