<?php

declare(strict_types=1);

namespace App\Services;

final class SecretCipherService
{
    private const PREFIX = 'enc:v1:';
    private const CIPHER = 'AES-256-CBC';
    private const IV_LENGTH = 16;
    private const MAC_LENGTH = 32;

    public static function isEncrypted(string $value): bool
    {
        return str_starts_with($value, self::PREFIX);
    }

    public static function encrypt(string $plainText): string
    {
        $plainText = trim($plainText);
        if ($plainText === '') {
            return '';
        }

        if (self::isEncrypted($plainText)) {
            return $plainText;
        }

        if (!function_exists('openssl_encrypt')) {
            return $plainText;
        }

        $key = self::encryptionKey();
        if ($key === '') {
            return $plainText;
        }

        try {
            $iv = random_bytes(self::IV_LENGTH);
        } catch (\Throwable $e) {
            return $plainText;
        }
        $cipherRaw = openssl_encrypt($plainText, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
        if (!is_string($cipherRaw) || $cipherRaw === '') {
            return $plainText;
        }

        $mac = hash_hmac('sha256', $iv . $cipherRaw, $key, true);
        $payload = base64_encode($iv . $mac . $cipherRaw);

        return self::PREFIX . $payload;
    }

    public static function decrypt(string $value): string
    {
        $value = trim($value);
        if ($value === '') {
            return '';
        }

        if (!self::isEncrypted($value)) {
            return $value;
        }

        if (!function_exists('openssl_decrypt')) {
            return '';
        }

        $key = self::encryptionKey();
        if ($key === '') {
            return '';
        }

        $payload = substr($value, strlen(self::PREFIX));
        $decoded = base64_decode($payload, true);
        if (!is_string($decoded) || strlen($decoded) <= (self::IV_LENGTH + self::MAC_LENGTH)) {
            return '';
        }

        $iv = substr($decoded, 0, self::IV_LENGTH);
        $mac = substr($decoded, self::IV_LENGTH, self::MAC_LENGTH);
        $cipherRaw = substr($decoded, self::IV_LENGTH + self::MAC_LENGTH);

        $computedMac = hash_hmac('sha256', $iv . $cipherRaw, $key, true);
        if (!hash_equals($mac, $computedMac)) {
            return '';
        }

        $plain = openssl_decrypt($cipherRaw, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
        if (!is_string($plain)) {
            return '';
        }

        return trim($plain);
    }

    private static function encryptionKey(): string
    {
        static $cachedKey = null;
        if (is_string($cachedKey)) {
            return $cachedKey;
        }

        $appKey = trim((string) config('app.key', ''));
        if ($appKey !== '') {
            if (str_starts_with($appKey, 'base64:')) {
                $decoded = base64_decode(substr($appKey, 7), true);
                if (is_string($decoded) && $decoded !== '') {
                    $cachedKey = hash('sha256', $decoded, true);
                    return $cachedKey;
                }
            }

            $cachedKey = hash('sha256', $appKey, true);
            return $cachedKey;
        }

        $fallbackSeed = implode('|', [
            (string) config('database.password', ''),
            (string) config('database.username', ''),
            (string) config('database.database', ''),
            (string) config('app.name', ''),
        ]);

        $cachedKey = hash('sha256', $fallbackSeed, true);
        return $cachedKey;
    }
}
