<?php

declare(strict_types=1);

namespace ServerManager\Core;

class Encryption
{
    private string $method;
    private string $masterKey;
    private const KEY_HASH_ALGO = 'sha256';

    public function __construct(array $config)
    {
        $this->method = $config['method'] ?? 'aes-256-cbc';

        if (!empty($config['master_key'])) {
            $this->masterKey = $config['master_key'];
        } elseif (!empty($config['master_key_file']) && file_exists($config['master_key_file'])) {
            $this->masterKey = trim(file_get_contents($config['master_key_file']));
        } else {
            $keyFile = $config['master_key_file'] ?? '/etc/servermanager/master.key';
            if (file_exists($keyFile)) {
                $this->masterKey = trim(file_get_contents($keyFile));
            } else {
                throw new \RuntimeException(
                    'Master encryption key not found. Generate one with: php bin/install.php --generate-key'
                );
            }
        }

        if (strlen($this->masterKey) < 32) {
            throw new \RuntimeException('Master encryption key must be at least 32 characters (256 bits)');
        }
    }

    public function encrypt(string $plaintext): string
    {
        $ivLength = openssl_cipher_iv_length($this->method);
        $iv = random_bytes($ivLength);
        $key = hash(self::KEY_HASH_ALGO, $this->masterKey, true);

        $encrypted = openssl_encrypt($plaintext, $this->method, $key, OPENSSL_RAW_DATA, $iv);

        if ($encrypted === false) {
            throw new \RuntimeException('Encryption failed');
        }

        return base64_encode($iv . $encrypted);
    }

    public function decrypt(string $ciphertext): string
    {
        $data = base64_decode($ciphertext);
        $ivLength = openssl_cipher_iv_length($this->method);
        $iv = substr($data, 0, $ivLength);
        $encrypted = substr($data, $ivLength);
        $key = hash(self::KEY_HASH_ALGO, $this->masterKey, true);

        $decrypted = openssl_decrypt($encrypted, $this->method, $key, OPENSSL_RAW_DATA, $iv);

        if ($decrypted === false) {
            throw new \RuntimeException('Decryption failed');
        }

        return $decrypted;
    }

    public function encryptArray(array $data, array $fields): array
    {
        foreach ($fields as $field) {
            if (!empty($data[$field])) {
                $data[$field] = $this->encrypt($data[$field]);
            }
        }
        return $data;
    }

    public function decryptArray(array $data, array $fields): array
    {
        foreach ($fields as $field) {
            if (!empty($data[$field])) {
                try {
                    $data[$field] = $this->decrypt($data[$field]);
                } catch (\RuntimeException $e) {
                    $data[$field] = null;
                }
            }
        }
        return $data;
    }
}
