import { test } from 'node:test';
import assert from 'node:assert/strict';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { rm, readFile } from 'node:fs/promises';

// Isolated users file + a known secret BEFORE importing the module (it reads env
// at import time). No Supabase needed — this is the DB-less path.
const USERS = join(tmpdir(), `cms-users-${process.pid}.json`);
process.env.USERS_FILE = USERS;
process.env.JWT_SECRET ||= 'test-secret-xyz';

const local = await import('../src/auth-local.js');
const { verify } = await import('hono/jwt');

test.after(async () => { await rm(USERS, { force: true }); });

test('create → list → login mints a GoTrue-shaped, verifiable token', async () => {
  const id = await local.createUser({ email: 'Karim@Example.com', password: 'hunter2', role: 'admin' });
  assert.ok(id);
  const list = await local.listUsers(['someone@else.com']);
  assert.equal(list.length, 1);
  assert.equal(list[0].email, 'Karim@Example.com');
  assert.equal(list[0].role, 'admin');

  // wrong password rejected, right password accepted (case-insensitive email)
  assert.equal(await local.login('karim@example.com', 'nope'), null);
  const res = await local.login('karim@example.com', 'hunter2');
  assert.ok(res?.access_token);
  assert.equal(res.user.email, 'Karim@Example.com');

  const claims = await verify(res.access_token, process.env.JWT_SECRET, 'HS256');
  assert.equal(claims.sub, id);
  assert.equal(claims.email, 'Karim@Example.com');
  assert.equal(claims.app_metadata.role, 'admin'); // same shape auth.js/roleOf read
  assert.ok(claims.exp > Math.floor(Date.now() / 1000));
});

test('passwords are bcrypt-hashed at rest (never plaintext)', async () => {
  const raw = JSON.parse(await readFile(USERS, 'utf8'));
  assert.match(raw[0].password, /^\$2[aby]\$/); // bcrypt hash prefix
  assert.notEqual(raw[0].password, 'hunter2');
});

test('countByRole reflects roles; fixed-admin email forced to admin', async () => {
  await local.createUser({ email: 'red@x.com', password: 'pw', role: 'editor' });
  const counts = await local.countByRole([]);
  assert.equal(counts.total, 2);
  assert.equal(counts.admin, 1);
  assert.equal(counts.editor, 1);
  // an env-admin email overrides the stored role in the listing
  const forced = await local.listUsers(['red@x.com']);
  assert.equal(forced.find((u) => u.email === 'red@x.com').role, 'admin');
});

test('update + delete', async () => {
  const id = await local.createUser({ email: 'tmp@x.com', password: 'pw' });
  await local.updateUser(id, { role: 'editor' });
  assert.equal((await local.listUsers([])).find((u) => u.id === id).role, 'editor');
  await local.deleteUser(id);
  assert.equal((await local.listUsers([])).some((u) => u.id === id), false);
  await assert.rejects(() => local.deleteUser('nope'), /nicht gefunden/);
});

test('duplicate email rejected', async () => {
  await assert.rejects(() => local.createUser({ email: 'red@x.com', password: 'pw' }), /existiert bereits/);
});
