<?php
/**
 * Encryption Service for BotFend Anti-Bot Firewall
 *
 * @package BotFend\Security
 */

namespace BotFend\Security;

defined('ABSPATH') || exit;

class Encryption {

    private static $instance = null;
    const METHOD = 'aes-256-ctr';
    const ENC_PREFIX = 'enc:';
    const BASE64_PREFIX = 'base64:';

    public static function get_instance(): self {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {}

    public function encrypt(string $value): string {
        if (empty($value)) return '';
        if ($this->is_encrypted($value)) return $value;
        $key = $this->get_encryption_key();
        if (!function_exists('openssl_encrypt')) {
            $this->debug_log('OpenSSL not available, using base64 for sensitive data');
            return self::BASE64_PREFIX . base64_encode($value);
        }
        $iv_length = openssl_cipher_iv_length(self::METHOD);
        if ($iv_length === false || $iv_length <= 0) {
            $this->debug_log('Cipher IV length calculation failed');
            return self::BASE64_PREFIX . base64_encode($value);
        }
        $iv = openssl_random_pseudo_bytes($iv_length);
        if ($iv === false) return self::BASE64_PREFIX . base64_encode($value);
        $encrypted = openssl_encrypt($value, self::METHOD, $key, OPENSSL_RAW_DATA, $iv);
        if ($encrypted === false) return self::BASE64_PREFIX . base64_encode($value);
        return self::ENC_PREFIX . base64_encode($iv . $encrypted);
    }

    public function decrypt(string $value): string {
        if (empty($value)) return '';
        if (strpos($value, self::BASE64_PREFIX) === 0) {
            $decoded = base64_decode(substr($value, strlen(self::BASE64_PREFIX)));
            return $decoded !== false ? $decoded : '';
        }
        if (strpos($value, self::ENC_PREFIX) !== 0) return $value;
        $encrypted_data = substr($value, strlen(self::ENC_PREFIX));
        $decoded = base64_decode($encrypted_data);
        if ($decoded === false) {
            $this->debug_log('Failed to decode encrypted data');
            return '';
        }
        if (!function_exists('openssl_decrypt')) {
            $this->debug_log('OpenSSL not available for decryption');
            return '';
        }
        $key = $this->get_encryption_key();
        $iv_length = openssl_cipher_iv_length(self::METHOD);
        if ($iv_length === false || $iv_length <= 0) {
            $this->debug_log('Cipher IV length calculation failed during decryption');
            return '';
        }
        if (strlen($decoded) <= $iv_length) {
            $this->debug_log('Encrypted data too short');
            return '';
        }
        $iv = substr($decoded, 0, $iv_length);
        $encrypted = substr($decoded, $iv_length);
        $decrypted = openssl_decrypt($encrypted, self::METHOD, $key, OPENSSL_RAW_DATA, $iv);
        if ($decrypted === false) {
            $this->debug_log('OpenSSL decryption failed');
            return '';
        }
        return $decrypted;
    }

    public function is_encrypted(string $value): bool {
        return strpos($value, self::ENC_PREFIX) === 0 || strpos($value, self::BASE64_PREFIX) === 0;
    }

    public function mask(string $value): string {
        $length = strlen($value);
        if ($length <= 8) return '••••••••';
        $first = substr($value, 0, 4);
        $last = substr($value, -4);
        $masked = str_repeat('•', min(8, $length - 8));
        return $first . $masked . $last;
    }

    private function get_encryption_key(): string {
        $raw_key = wp_salt('auth');
        if (defined('AUTH_KEY') && !empty(AUTH_KEY)) {
            $raw_key = AUTH_KEY;
        }
        return hash('sha256', $raw_key, true);
    }

    private function debug_log(string $message): void {
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log(sprintf('BotFend Encryption: %s', $message));
        }
    }
}