const argon2 = require('argon2');
const jwt = require('jsonwebtoken');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');

// ─── Load RSA keys ────────────────────────────────────

function loadKeys() {
  const privatePath = process.env.JWT_PRIVATE_KEY_PATH || path.join(__dirname, 'private.pem');
  const publicPath = process.env.JWT_PUBLIC_KEY_PATH || path.join(__dirname, 'public.pem');

  return {
    privateKey: fs.readFileSync(privatePath, 'utf8'),
    publicKey: fs.readFileSync(publicPath, 'utf8')
  };
}

// ─── Argon2id (with client-side SHA-256 for HTTP transport) ──

/**
 * Hash a raw password for storage.
 * Step 1: SHA-256(raw) → 64-char hex
 * Step 2: Argon2id(sha256_hex) → stored in DB
 *
 * This way the client sends SHA-256(password) over HTTP,
 * and the server never sees or stores the raw password.
 */
async function hashPassword(password) {
  const sha256 = crypto.createHash('sha256').update(password).digest('hex');
  return argon2.hash(sha256, {
    type: argon2.argon2id,
    memoryCost: 65536,    // 64 MB
    timeCost: 3,          // 3 iterations
    parallelism: 4
  });
}

/**
 * Verify a client-submitted password hash against stored Argon2id hash.
 * @param {string} storedHash  — Argon2id(SHA-256(raw_password))
 * @param {string} receivedSha256 — SHA-256(raw_password) from client (64-char hex)
 */
async function verifyPassword(storedHash, receivedSha256) {
  return argon2.verify(storedHash, receivedSha256);
}

// ─── JWT ──────────────────────────────────────────────

function signAccessToken(user, deviceId) {
  const { privateKey } = loadKeys();
  return jwt.sign(
    {
      sub: user.id,
      username: user.username,
      role: user.role,
      device_id: deviceId
    },
    privateKey,
    {
      algorithm: 'RS256',
      expiresIn: '30m'
    }
  );
}

function verifyAccessToken(token) {
  const { publicKey } = loadKeys();
  return jwt.verify(token, publicKey, { algorithms: ['RS256'] });
}

// ─── Refresh Token ────────────────────────────────────

function generateRefreshToken() {
  return crypto.randomBytes(32).toString('hex');   // 256-bit → 64 hex chars
}

function hashToken(token) {
  return crypto.createHash('sha256').update(token).digest('hex');
}

// ─── HWID Matching ────────────────────────────────────

/**
 * Dynamic HWID matching.
 * Rule 1: CPU + Board both match → immediate pass (core hardware).
 * Rule 2: Weighted score ≥ 80%.
 *   CPU: 45%, Board: 45%, Disk: 8%, MAC: 2%
 */
function hwidMatchRate(device, incoming) {
  const cpuMatch   = device.cpu_hash   === incoming.cpu;
  const boardMatch = device.board_hash === incoming.board;
  const diskMatch  = device.disk_hash  === incoming.disk;
  const macMatch   = device.mac_hash   === incoming.mac;

  // Rule 1: CPU + Board
  if (cpuMatch && boardMatch) return { match: true, rate: 0.80 };

  // Rule 2: weighted
  let score = 0;
  if (cpuMatch)   score += 0.45;
  if (boardMatch) score += 0.45;
  if (diskMatch)  score += 0.08;
  if (macMatch)   score += 0.02;

  return { match: score >= 0.80, rate: score };
}

/**
 * Compute a combined HWID hash for quick lookup.
 */
function combinedHwidHash(components) {
  const combined = `${components.cpu}|${components.board}|${components.disk}|${components.mac}`;
  return crypto.createHash('sha256').update(combined).digest('hex');
}

// ─── Admin RBAC ───────────────────────────────────────

function requireAdmin(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ success: false, error: 'UNAUTHORIZED' });
  }

  try {
    const token = authHeader.slice(7);
    const payload = verifyAccessToken(token);
    if (payload.role !== 'admin') {
      return res.status(403).json({ success: false, error: 'FORBIDDEN', message: 'Admin role required' });
    }
    req.user = payload;
    next();
  } catch (err) {
    return res.status(401).json({ success: false, error: 'TOKEN_INVALID' });
  }
}

/**
 * Hash a pre-computed SHA-256 hex string (from client) with Argon2id.
 * Used for registration when the client sends SHA-256(password).
 */
async function hashPasswordFromHash(sha256hex) {
  return argon2.hash(sha256hex, {
    type: argon2.argon2id,
    memoryCost: 65536,
    timeCost: 3,
    parallelism: 4
  });
}

module.exports = {
  hashPassword, hashPasswordFromHash, verifyPassword,
  signAccessToken, verifyAccessToken,
  generateRefreshToken, hashToken,
  hwidMatchRate, combinedHwidHash,
  requireAdmin
};
