<?php
namespace WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Services;

use WPUltimateSecurity\UltimateSecurity\Support\Helpers\CryptoHelper;

/**
 * Encryption Service for 2FA Secrets
 *
 * Handles encryption and decryption of sensitive 2FA data like TOTP/HOTP secrets.
 * Uses CryptoHelper for cryptographic operations.
 *
 * RECOMMENDED: Define ULTIMATE_SECURITY_ENCRYPTION_KEY in wp-config.php:
 * define('ULTIMATE_SECURITY_ENCRYPTION_KEY', 'your-64-character-hex-string');
 *
 * Generate a key with: bin2hex(random_bytes(32))
 */
class EncryptionService
{
	/**
	 * Track if we're using the secure wp-config constant
	 *
	 * @var bool|null
	 */
	private static $using_config_key = null;

	/**
	 * Get or generate encryption key
	 *
	 * Priority:
	 * 1. ULTIMATE_SECURITY_ENCRYPTION_KEY constant (most secure - recommended)
	 * 2. Stored key in database (legacy/fallback)
	 * 3. Derived from WordPress salts (if no stored key)
	 *
	 * @return string|false Binary encryption key (32 bytes)
	 */
	public static function get_encryption_key()
	{
		// Priority 1: Check for wp-config.php constant (most secure)
		if (defined('ULTIMATE_SECURITY_ENCRYPTION_KEY')) {
			$config_key = constant('ULTIMATE_SECURITY_ENCRYPTION_KEY');
			if (!empty($config_key)) {
				// Support both hex-encoded (64 chars) and raw base64 (44 chars) formats
				if (strlen($config_key) === 64 && ctype_xdigit($config_key)) {
					// Hex-encoded key
					$key = hex2bin($config_key);
					if ($key !== false && strlen($key) === 32) {
						self::$using_config_key = true;
						return $key;
					}
				} elseif (strlen($config_key) >= 32) {
					// Raw or base64 key - derive 32 bytes
					$key = CryptoHelper::hash($config_key, 'sha256', true);
					self::$using_config_key = true;
					return $key;
				}
			}
		}

		self::$using_config_key = false;

		// Priority 2: Try stored key in database (legacy)
		$stored = get_option('ultimate_security_encryption_key', '');
		if (!empty($stored)) {
			$key = base64_decode($stored, true);
			if ($key !== false && strlen($key) === 32) {
				return $key;
			}
			// Bad stored value: fall through to regenerate
		}

		// Priority 3: Derive from WordPress salts if available
		if (defined('AUTH_KEY') && defined('SECURE_AUTH_KEY')) {
			// produce raw 32 bytes
			$key = CryptoHelper::hash(AUTH_KEY . SECURE_AUTH_KEY, 'sha256', true);
		} else {
			// fallback: create a random 256-bit key using CryptoHelper
			$key = CryptoHelper::generateToken(32, 'raw');
		}

		// Store base64 encoded key in DB (autoload = false to reduce exposure)
		update_option('ultimate_security_encryption_key', base64_encode($key), false);

		return $key;
	}

	/**
	 * Check if using the secure wp-config constant for encryption
	 *
	 * @return bool True if using ULTIMATE_SECURITY_ENCRYPTION_KEY constant
	 */
	public static function is_using_config_key()
	{
		if (self::$using_config_key === null) {
			// Trigger key loading to set the flag
			self::get_encryption_key();
		}
		return self::$using_config_key === true;
	}

	/**
	 * Get security recommendation for encryption key storage
	 *
	 * @return array|null Recommendation array or null if already secure
	 */
	public static function get_key_storage_recommendation()
	{
		if (self::is_using_config_key()) {
			return null; // Already secure
		}

		// Generate a sample key for the recommendation
		$sample_key = bin2hex(random_bytes(32));

		return [
			'severity' => 'warning',
			'message' => __('For better security, define the encryption key in wp-config.php instead of storing it in the database.', 'ultimate-security'),
			'code' => sprintf(
				"define('ULTIMATE_SECURITY_ENCRYPTION_KEY', '%s');",
				$sample_key
			),
			'instructions' => __('Add this line to your wp-config.php file, before the line that says "That\'s all, stop editing!"', 'ultimate-security')
		];
	}

	/**
	 * Encrypt data using CryptoHelper (AES-256-GCM)
	 *
	 * Note: This method now uses AES-256-GCM via CryptoHelper for better security.
	 * Existing data encrypted with CBC will need migration or a fallback.
	 *
	 * @param string $data Data to encrypt
	 * @return string|false Base64 encoded encrypted data
	 */
	public static function encrypt($data)
	{
		return CryptoHelper::encrypt($data);
	}

	/**
	 * Decrypt data using CryptoHelper
	 *
	 * @param string $encryptedBase64 Base64 encoded encrypted data
	 * @return string|false Decrypted plaintext
	 */
	public static function decrypt($encryptedBase64)
	{
		return CryptoHelper::decrypt($encryptedBase64);
	}

	/**
	 * Enable encryption for existing database keys
	 *
	 * Migrates existing unencrypted TOTP/HOTP secrets to encrypted format
	 *
	 * @return bool Success status
	 */
	public static function enable_encryption_for_existing_keys()
	{
		global $wpdb;
		$meta_key_totp = 'ultimate_security_2fa_totp_secret';
		$meta_key_hotp = 'ultimate_security_2fa_hotp_secret';

		// Query the wp_usermeta table to find all users with the specific meta key.
		$users_with_meta = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT user_id, meta_key, meta_value FROM {$wpdb->usermeta} WHERE meta_key = %s OR meta_key = %s",
				$meta_key_totp,
				$meta_key_hotp
			)
		);

		// Loop through the results and update the meta values.
		if (!empty($users_with_meta)) {
			foreach ($users_with_meta as $user_meta) {
				$user_id = $user_meta->user_id;
				$meta_key = $user_meta->meta_key;
				$old_meta_value = $user_meta->meta_value;

				// Modify the old meta value (example: append some text).
				$new_meta_value = self::encrypt($old_meta_value);
				// Update the meta value in the database.
				update_user_meta($user_id, $meta_key, $new_meta_value);
			}
		}

		update_option('ultimate-security-enable-encryption-database-keys', 1);

		return true;
	}

	/**
	 * Check if encryption is enabled
	 *
	 * @return bool
	 */
	public static function is_encryption_enabled()
	{
		return (bool) get_option('ultimate-security-enable-encryption-database-keys', 0);
	}
}
