// src/services/jwtKeys.js
// === SECURITY HARDENING: JWT KEY ROTATION ===
// JWT key management with support for key rotation and multiple signing keys

/**
 * JWT Key Management
 * 
 * Supports:
 * - Multiple signing keys with key IDs (kid)
 * - Active key for signing new tokens
 * - Multiple verification keys for token rotation
 * - Key rotation without breaking existing tokens
 * 
 * Configuration:
 * - JWT_ACTIVE_KEY_ID: The key ID to use for signing new tokens (default: '1')
 * - JWT_KEYS_JSON: JSON object mapping key IDs to secrets
 *   Example: {"1": "secret1", "2": "secret2"}
 * - JWT_ISSUER: Issuer claim (iss) for tokens (default: 'farm-auth-service')
 * - JWT_AUDIENCE: Audience claim (aud) for tokens (default: 'mobile-app')
 * 
 * TODO: In production, load keys from a secrets manager (AWS Secrets Manager, 
 *       HashiCorp Vault, etc.) instead of environment variables
 */

const crypto = require('crypto');

// === SECURITY HARDENING: JWT KEY ROTATION ===
// Load keys from environment
const ACTIVE_KEY_ID = process.env.JWT_ACTIVE_KEY_ID || '1';
const JWT_ISSUER = process.env.JWT_ISSUER || 'farm-auth-service';
const JWT_AUDIENCE = process.env.JWT_AUDIENCE || 'mobile-app';

// Parse keys from environment
// Format: JWT_KEYS_JSON='{"1":"secret1","2":"secret2"}'
// OR use legacy single keys: JWT_ACCESS_SECRET, JWT_REFRESH_SECRET
let keys = {};

function loadKeys() {
  // Try new format first (JWT_KEYS_JSON)
  if (process.env.JWT_KEYS_JSON) {
    try {
      keys = JSON.parse(process.env.JWT_KEYS_JSON);
    } catch (err) {
      console.error('Failed to parse JWT_KEYS_JSON:', err);
      keys = {};
    }
  }

  // Fallback to legacy format for backward compatibility
  if (Object.keys(keys).length === 0) {
    if (process.env.JWT_ACCESS_SECRET) {
      keys['1'] = process.env.JWT_ACCESS_SECRET;
    }
    if (process.env.JWT_REFRESH_SECRET && !keys['1']) {
      keys['1'] = process.env.JWT_REFRESH_SECRET;
    }
  }

  // Ensure we have at least one key
  if (Object.keys(keys).length === 0) {
    throw new Error('No JWT keys configured. Set JWT_KEYS_JSON or JWT_ACCESS_SECRET');
  }

  // Ensure active key exists
  if (!keys[ACTIVE_KEY_ID]) {
    console.warn(`Active key ID ${ACTIVE_KEY_ID} not found, using first available key`);
    const firstKeyId = Object.keys(keys)[0];
    return { keys, activeKeyId: firstKeyId };
  }

  return { keys, activeKeyId: ACTIVE_KEY_ID };
}

const { keys: loadedKeys, activeKeyId } = loadKeys();

/**
 * Get the secret for a specific key ID
 */
function getKeySecret(keyId) {
  return loadedKeys[keyId] || null;
}

/**
 * Get the active key ID and secret for signing new tokens
 */
function getActiveKey() {
  return {
    keyId: activeKeyId,
    secret: loadedKeys[activeKeyId],
  };
}

/**
 * Get all keys (for verification during rotation)
 */
function getAllKeys() {
  return loadedKeys;
}

/**
 * Get issuer claim
 */
function getIssuer() {
  return JWT_ISSUER;
}

/**
 * Get audience claim
 */
function getAudience() {
  return JWT_AUDIENCE;
}

/**
 * Validate JWT claims (iss, aud, exp, iat, nbf)
 */
function validateTokenClaims(payload, options = {}) {
  const {
    allowExpired = false,
    clockSkewSeconds = 60, // Allow 60 seconds clock skew
  } = options;

  const now = Math.floor(Date.now() / 1000);
  const skew = clockSkewSeconds;

  // Validate issuer
  if (payload.iss && payload.iss !== JWT_ISSUER) {
    return { valid: false, reason: 'invalid_issuer' };
  }

  // Validate audience
  if (payload.aud && payload.aud !== JWT_AUDIENCE) {
    return { valid: false, reason: 'invalid_audience' };
  }

  // Validate expiration
  if (payload.exp) {
    if (!allowExpired && payload.exp < now - skew) {
      return { valid: false, reason: 'expired' };
    }
  }

  // Validate issued at (should not be in the future beyond clock skew)
  if (payload.iat) {
    if (payload.iat > now + skew) {
      return { valid: false, reason: 'issued_in_future' };
    }
  }

  // Validate not before (if present)
  if (payload.nbf) {
    if (payload.nbf > now + skew) {
      return { valid: false, reason: 'not_yet_valid' };
    }
  }

  return { valid: true };
}

/**
 * TODO: Load keys from secrets manager
 * Example implementation for AWS Secrets Manager:
 * 
 * async function loadKeysFromSecretsManager() {
 *   const AWS = require('aws-sdk');
 *   const secretsManager = new AWS.SecretsManager();
 *   
 *   const secret = await secretsManager.getSecretValue({
 *     SecretId: process.env.JWT_SECRETS_ARN
 *   }).promise();
 *   
 *   return JSON.parse(secret.SecretString);
 * }
 */

module.exports = {
  getKeySecret,
  getActiveKey,
  getAllKeys,
  getIssuer,
  getAudience,
  validateTokenClaims,
};

