const crypto = require('crypto');
const db     = require('./db/database');

// Sessions live for 7 days, after which the user must log in again.
const SESSION_TTL_MS = 1000 * 60 * 60 * 24 * 7;

// ── Password hashing (scrypt, no external deps) ───────────────────────────────
// Stored format: scrypt$<saltHex>$<hashHex>
function hashPassword(password) {
  const salt = crypto.randomBytes(16);
  const hash = crypto.scryptSync(password, salt, 64);
  return `scrypt$${salt.toString('hex')}$${hash.toString('hex')}`;
}

function verifyPassword(password, stored) {
  try {
    const [scheme, saltHex, hashHex] = String(stored).split('$');
    if (scheme !== 'scrypt') return false;
    const salt     = Buffer.from(saltHex, 'hex');
    const expected = Buffer.from(hashHex, 'hex');
    const actual   = crypto.scryptSync(password, salt, expected.length);
    return crypto.timingSafeEqual(expected, actual);
  } catch {
    return false;
  }
}

// ── Bootstrap admin from environment ──────────────────────────────────────────
// Creates the admin account if missing, or re-syncs its password every startup
// so rotating ADMIN_PASSWORD in the container rotates the live credential.
// Because of this, the bootstrap admin's password cannot be changed from the UI
// — it is owned by the Docker environment. Additional users created in the UI
// are unaffected.
function bootstrapAdmin() {
  const username = (process.env.ADMIN_USERNAME || 'admin').trim();
  const password = process.env.ADMIN_PASSWORD;

  if (!password) {
    console.warn('[AUTH] ADMIN_PASSWORD is not set — admin account was NOT bootstrapped. Set it in the container environment.');
    return;
  }

  const existing = db.prepare('SELECT * FROM users WHERE username = ?').get(username);
  if (!existing) {
    db.prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)')
      .run(username, hashPassword(password), 'admin');
    console.log(`[AUTH] Bootstrapped admin user '${username}'.`);
  } else {
    db.prepare('UPDATE users SET password_hash = ?, role = ? WHERE id = ?')
      .run(hashPassword(password), 'admin', existing.id);
    console.log(`[AUTH] Synced admin user '${username}' password from environment.`);
  }
}

// ── User management ───────────────────────────────────────────────────────────
function findUserByUsername(username) {
  return db.prepare('SELECT * FROM users WHERE username = ?').get(String(username || '').trim());
}

function listUsers() {
  return db.prepare('SELECT id, username, role, created_at FROM users ORDER BY username ASC').all();
}

function createUser({ username, password, role }) {
  const uname = String(username || '').trim();
  if (!uname)           throw Object.assign(new Error('username is required'), { status: 400 });
  if (!password)        throw Object.assign(new Error('password is required'), { status: 400 });
  if (password.length < 6) throw Object.assign(new Error('password must be at least 6 characters'), { status: 400 });

  const safeRole = role === 'admin' ? 'admin' : 'user';
  if (findUserByUsername(uname)) {
    throw Object.assign(new Error('A user with that username already exists'), { status: 409 });
  }

  const result = db.prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)')
    .run(uname, hashPassword(password), safeRole);
  return { id: result.lastInsertRowid, username: uname, role: safeRole };
}

function deleteUser(id) {
  return db.prepare('DELETE FROM users WHERE id = ?').run(id).changes > 0;
}

function setPassword(id, password) {
  if (!password || password.length < 6) {
    throw Object.assign(new Error('password must be at least 6 characters'), { status: 400 });
  }
  return db.prepare('UPDATE users SET password_hash = ? WHERE id = ?').run(hashPassword(password), id).changes > 0;
}

// ── Sessions ──────────────────────────────────────────────────────────────────
function createSession(user) {
  const token   = crypto.randomBytes(32).toString('hex');
  const expires = new Date(Date.now() + SESSION_TTL_MS).toISOString();
  db.prepare('INSERT INTO sessions (token, user_id, expires_at) VALUES (?, ?, ?)')
    .run(token, user.id, expires);
  return token;
}

function getSessionUser(token) {
  if (!token) return null;
  const row = db.prepare(`
    SELECT s.expires_at, u.id, u.username, u.role
    FROM sessions s JOIN users u ON u.id = s.user_id
    WHERE s.token = ?
  `).get(token);
  if (!row) return null;
  if (new Date(row.expires_at).getTime() < Date.now()) {
    db.prepare('DELETE FROM sessions WHERE token = ?').run(token);
    return null;
  }
  return { id: row.id, username: row.username, role: row.role };
}

function destroySession(token) {
  if (token) db.prepare('DELETE FROM sessions WHERE token = ?').run(token);
}

function login(username, password) {
  const user = findUserByUsername(username);
  if (!user || !verifyPassword(password, user.password_hash)) return null;
  const token = createSession(user);
  return { token, user: { id: user.id, username: user.username, role: user.role } };
}

// ── Express middleware ────────────────────────────────────────────────────────
function tokenFromReq(req) {
  const h = req.headers.authorization || '';
  return h.startsWith('Bearer ') ? h.slice(7) : null;
}

function requireAuth(req, res, next) {
  const user = getSessionUser(tokenFromReq(req));
  if (!user) return res.status(401).json({ error: 'Authentication required' });
  req.user      = user;
  req.authToken = tokenFromReq(req);
  next();
}

function requireAdmin(req, res, next) {
  if (!req.user || req.user.role !== 'admin') {
    return res.status(403).json({ error: 'Admin access required' });
  }
  next();
}

module.exports = {
  bootstrapAdmin,
  login,
  destroySession,
  listUsers,
  createUser,
  deleteUser,
  setPassword,
  requireAuth,
  requireAdmin,
};
