<?php

namespace App\Core;

/**
 * Advanced OWASP Security Helper
 * Handles AES-256-GCM encryption/decryption, HMAC Blind Indexing,
 * Bcrypt password hashing, and JWT validation.
 */
class Security
{
    /**
     * Get the encryption key from environment (must be 32 bytes for AES-256)
     */
    private static function getEncryptionKey(): string
    {
        $key = getenv('ENCRYPTION_KEY');
        if (!$key || strlen($key) < 16) {
            throw new \RuntimeException("ENCRYPTION_KEY environment variable is empty or too short. Cryptographic operations aborted.");
        }
        return substr(hash('sha256', $key, true), 0, 32);
    }

    /**
     * Get HMAC Salt for Blind Indexing
     */
    private static function getHmacSalt(): string
    {
        $salt = getenv('HMAC_SALT');
        if (!$salt) {
            throw new \RuntimeException("HMAC_SALT environment variable is empty. Cryptographic operations aborted.");
        }
        return $salt;
    }

    /**
     * Get JWT Secret
     */
    private static function getJwtSecret(): string
    {
        $secret = getenv('JWT_SECRET');
        if (!$secret) {
            throw new \RuntimeException("JWT_SECRET environment variable is empty. Cryptographic operations aborted.");
        }
        return $secret;
    }

    /**
     * Encrypt text using AES-256-GCM
     */
    public static function encrypt(string $plainText): string
    {
        if ($plainText === '') {
            return '';
        }
        
        $key = self::getEncryptionKey();
        $iv = openssl_random_pseudo_bytes(12); // GCM standard IV is 12 bytes
        $tag = '';
        
        $ciphertext = openssl_encrypt(
            $plainText,
            'aes-256-gcm',
            $key,
            OPENSSL_RAW_DATA,
            $iv,
            $tag
        );

        if ($ciphertext === false) {
            return '';
        }

        // Return combined iv + tag + ciphertext base64 encoded
        return base64_encode($iv . $tag . $ciphertext);
    }

    /**
     * Decrypt text using AES-256-GCM
     */
    public static function decrypt(string $encryptedText): string
    {
        if ($encryptedText === '') {
            return '';
        }

        $key = self::getEncryptionKey();
        $data = base64_decode($encryptedText);
        
        // AES-256-GCM tag is 16 bytes, IV is 12 bytes
        if (strlen($data) < 28) {
            return '';
        }

        $iv = substr($data, 0, 12);
        $tag = substr($data, 12, 16);
        $ciphertext = substr($data, 28);

        $decrypted = openssl_decrypt(
            $ciphertext,
            'aes-256-gcm',
            $key,
            OPENSSL_RAW_DATA,
            $iv,
            $tag
        );

        return $decrypted !== false ? $decrypted : '';
    }

    /**
     * Generate Blind Index (HMAC-SHA256) for secure database queries
     */
    public static function blindIndex(string $value): string
    {
        if ($value === '') {
            return '';
        }
        $normalized = strtolower(trim($value));
        return hash_hmac('sha256', $normalized, self::getHmacSalt());
    }

    /**
     * Hash password using bcrypt
     */
    public static function hashPassword(string $password): string
    {
        return password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
    }

    /**
     * Verify password
     */
    public static function verifyPassword(string $password, string $hash): bool
    {
        return password_verify($password, $hash);
    }

    /**
     * Generate JWT Token with HMAC-SHA256 signature
     * Includes user_id, company_id, role, iss, aud, and jti.
     */
    public static function generateJWT(array $payload, int $expirySeconds = 86400): string
    {
        $header = self::base64UrlEncode(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
        
        // Standard OWASP Claims
        $payload['iat'] = time();
        $payload['exp'] = time() + $expirySeconds;
        $payload['iss'] = getenv('APP_URL'); // Issuer
        $payload['aud'] = 'nabeh_dashboard'; // Audience
        $payload['jti'] = bin2hex(random_bytes(16)); // JWT ID to prevent Replay Attacks
        
        $payloadEncoded = self::base64UrlEncode(json_encode($payload));
        
        $secret = self::getJwtSecret();
        $signature = hash_hmac('sha256', "$header.$payloadEncoded", $secret, true);
        $signatureEncoded = self::base64UrlEncode($signature);
        
        return "$header.$payloadEncoded.$signatureEncoded";
    }

    /**
     * Verify JWT Token and return payload if valid, false otherwise
     */
    public static function verifyJWT(string $token)
    {
        $parts = explode('.', $token);
        if (count($parts) !== 3) {
            return false;
        }

        list($headerEncoded, $payloadEncoded, $signatureEncoded) = $parts;
        
        $secret = self::getJwtSecret();
        if (!$secret) {
            return false;
        }

        $signature = self::base64UrlDecode($signatureEncoded);
        $expectedSignature = hash_hmac('sha256', "$headerEncoded.$payloadEncoded", $secret, true);
        
        if (!hash_equals($signature, $expectedSignature)) {
            return false;
        }
        
        $payload = json_decode(self::base64UrlDecode($payloadEncoded), true);
        if (!$payload || !isset($payload['exp']) || time() >= $payload['exp']) {
            return false;
        }

        // Validate Issuer
        $expectedIssuer = getenv('APP_URL');
        if (isset($payload['iss']) && $payload['iss'] !== $expectedIssuer) {
            return false;
        }
        
        return $payload;
    }
    private static function base64UrlEncode(string $data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }

    private static function base64UrlDecode(string $data): string
    {
        return base64_decode(strtr($data, '-_', '+/') . str_repeat('=', (4 - strlen($data) % 4) % 4));
    }
}
