// Local auth provider — file-based users (bcrypt) + self-signed JWTs of the SAME
// claim shape as Supabase/GoTrue (sub / email / app_metadata.role / exp), so the
// rest of the engine (auth.js verifyToken, roleOf) treats local and supabase
// tokens identically. Lets a core instance run DB-less (no Supabase/Postgres):
// Node + Hugo + nginx. Activated by config.auth === 'local'.
//
// Users live in a JSON file (USERS_FILE, default <SITE_DIR>/cms-users.json):
//   [{ id, email, password: <bcrypt hash>, role, created_at, last_sign_in_at }]
import path from 'node:path';
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { randomUUID } from 'node:crypto';
import { sign } from 'hono/jwt';

const SITE_DIR = process.env.SITE_DIR || '/site';
const USERS_FILE = process.env.USERS_FILE || path.join(SITE_DIR, 'cms-users.json');
const JWT_SECRET = process.env.JWT_SECRET || '';
const ROLES = ['user', 'editor', 'admin'];

// bcryptjs is pure-JS (no native build) and lazily imported — a supabase-auth
// instance never loads it.
async function bcrypt() { return (await import('bcryptjs')).default; }

export async function loadUsers() {
  try {
    const j = JSON.parse(await readFile(USERS_FILE, 'utf8'));
    return Array.isArray(j) ? j : (j.users || []);
  } catch { return []; }
}
async function persist(users) {
  await mkdir(path.dirname(USERS_FILE), { recursive: true });
  await writeFile(USERS_FILE, JSON.stringify(users, null, 2), 'utf8');
}

export async function hashPassword(pw) { return (await bcrypt()).hash(pw, 10); }
export async function verifyPassword(pw, hash) {
  try { return await (await bcrypt()).compare(pw, hash); } catch { return false; }
}

// Mint a GoTrue-shaped HS256 access token (verified by auth.js with JWT_SECRET).
export async function mintToken(user, ttl = 3600) {
  if (!JWT_SECRET) throw new Error('JWT_SECRET fehlt — für auth: local nötig');
  const now = Math.floor(Date.now() / 1000);
  return sign({
    sub: user.id, email: user.email, role: 'authenticated',
    app_metadata: { role: user.role || 'user' }, iat: now, exp: now + ttl,
  }, JWT_SECRET, 'HS256');
}

export async function login(email, password) {
  const users = await loadUsers();
  const u = users.find((x) => (x.email || '').toLowerCase() === (email || '').toLowerCase());
  if (!u || !u.password) return null;
  if (!(await verifyPassword(password, u.password))) return null;
  u.last_sign_in_at = new Date().toISOString();
  await persist(users);
  return {
    access_token: await mintToken(u),
    token_type: 'bearer',
    user: { id: u.id, email: u.email, role: u.role || 'user' },
  };
}

// ── Admin CRUD (mirrors routes/users.js shapes) ──────────────────────────────
export async function listUsers(adminEmails = []) {
  const users = await loadUsers();
  return users.map((u) => {
    const fixedAdmin = adminEmails.includes((u.email || '').toLowerCase());
    const role = u.role || 'user';
    return {
      id: u.id, email: u.email, created_at: u.created_at || null,
      last_sign_in_at: u.last_sign_in_at || null,
      role: fixedAdmin ? 'admin' : role, isAdmin: fixedAdmin || role === 'admin', fixedAdmin,
    };
  });
}
export async function countByRole(adminEmails = []) {
  const list = await listUsers(adminEmails);
  const out = { total: list.length, admin: 0, editor: 0, user: 0 };
  for (const u of list) out[u.role] = (out[u.role] || 0) + 1;
  return out;
}
export async function createUser({ email, password, role }) {
  if (!email || !password) throw new Error('E-Mail und Passwort nötig');
  if (role && !ROLES.includes(role)) throw new Error('Unbekannte Rolle');
  const users = await loadUsers();
  if (users.some((u) => (u.email || '').toLowerCase() === email.toLowerCase())) {
    throw new Error('E-Mail existiert bereits');
  }
  const u = {
    id: randomUUID(), email, password: await hashPassword(password),
    role: role || 'user', created_at: new Date().toISOString(), last_sign_in_at: null,
  };
  users.push(u); await persist(users);
  return u.id;
}
export async function updateUser(id, { password, role }) {
  const users = await loadUsers();
  const u = users.find((x) => x.id === id);
  if (!u) throw new Error('Nutzer:in nicht gefunden');
  if (password) u.password = await hashPassword(password);
  if (role) { if (!ROLES.includes(role)) throw new Error('Unbekannte Rolle'); u.role = role; }
  await persist(users);
}
export async function deleteUser(id) {
  const users = await loadUsers();
  const next = users.filter((u) => u.id !== id);
  if (next.length === users.length) throw new Error('Nutzer:in nicht gefunden');
  await persist(next);
}
