'use strict';

// Self-service password-reset tokens. Deliberately the same shape as lib/emailVerify.js:
// the emailed token is random, stored ONLY as a SHA-256 hash (single-use, same discipline
// as recovery codes and api tokens), with the plaintext living just in the email link. One
// pending token per user on the users row, so re-requesting overwrites the previous one.
//
// TTL is much shorter than email verification's 24h: this token changes a credential, so
// the window in which a leaked link is useful should be small.

const crypto = require('crypto');
const bcrypt = require('bcryptjs');
const { db } = require('../db/database');
const { hashToken } = require('../middleware/apiToken');

const TTL_SEC = 60 * 60;        // 1 hour
const MIN_PASSWORD_LENGTH = 8;  // same minimum as registration and PUT /api/auth/me

// Mint a token for a user, store its hash + expiry, return the PLAINTEXT (emailed once).
function issue(userId) {
  const token = crypto.randomBytes(32).toString('hex');
  const expires = Math.floor(Date.now() / 1000) + TTL_SEC;
  db.prepare('UPDATE users SET password_reset_hash = ?, password_reset_expires = ? WHERE id = ?')
    .run(hashToken(token), expires, userId);
  return token;
}

// Consume a token and set the new password. Returns the user id on success, else null
// (unknown / expired / already used). Single-use: the hash is cleared in the same statement
// that sets the password, and that UPDATE is conditioned on the hash still being present,
// so two concurrent redemptions cannot both win.
//
// Clears must_change_password too — the user has just chosen a password, which is exactly
// what that flag was demanding.
function consume(token, newPassword) {
  if (!token || typeof token !== 'string') return null;
  if (!newPassword || newPassword.length < MIN_PASSWORD_LENGTH) return null;
  const hash = hashToken(token);
  const row = db.prepare('SELECT id, password_reset_expires FROM users WHERE password_reset_hash = ?').get(hash);
  if (!row) return null;
  if (!row.password_reset_expires || row.password_reset_expires < Math.floor(Date.now() / 1000)) return null;
  const res = db.prepare(`UPDATE users SET password_hash = ?, password_reset_hash = NULL,
      password_reset_expires = NULL, must_change_password = 0, updated_at = strftime('%s','now')
    WHERE id = ? AND password_reset_hash = ?`).run(bcrypt.hashSync(newPassword, 10), row.id, hash);
  return res.changes === 1 ? row.id : null;
}

module.exports = { issue, consume, TTL_SEC, MIN_PASSWORD_LENGTH };
