<?php

class CryptoHelper
{
    private const DEFAULT_SECRET_KEY = '1234567890abcder';
    private const CIPHER = 'AES-128-CBC';

    /**
     * @param string $str1
     * @param bool $isBinary whether to encrypt as binary or not. Default is: false
     * @return string Encrypted data
     */
    public static function encrypt($str1, $isBinary = false)
    {
        $str = self::pkcs5_pad((string) $str1);
        $str = $isBinary ? $str : self::convertEncoding($str, 'ISO-8859-1', 'UTF-8');
        $key = self::secretKey();
        $encrypted = openssl_encrypt($str, self::CIPHER, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $key);

        if ($encrypted === false) {
            throw new RuntimeException('No fue posible cifrar la informacion.');
        }

        return $isBinary ? $encrypted : base64_encode($encrypted);
    }

    /**
     * @param string $code
     * @param bool $isBinary whether to decrypt as binary or not. Default is: false
     * @return string Decrypted data
     */
    public static function decrypt($code, $isBinary = false)
    {
        $code = $isBinary ? $code : base64_decode((string) $code, true);
        if ($code === false) {
            throw new InvalidArgumentException('El valor cifrado no tiene un formato valido.');
        }

        $key = self::secretKey();
        $decrypted = openssl_decrypt($code, self::CIPHER, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $key);
        if ($decrypted === false) {
            throw new RuntimeException('No fue posible descifrar la informacion.');
        }

        $decrypted = trim(self::pkcs5_unpad($decrypted));
        return $isBinary ? $decrypted : self::convertEncoding($decrypted, 'UTF-8', 'ISO-8859-1');
    }

    private static function pkcs5_pad($text)
    {
        $size = openssl_cipher_iv_length(self::CIPHER);
        if ($size === false || $size <= 0) {
            throw new RuntimeException('El cifrado configurado no esta disponible.');
        }

        $pad = $size - (strlen($text) % $size);
        return $text . str_repeat(chr($pad), $pad);
    }

    private static function pkcs5_unpad($text)
    {
        if ($text === '') {
            return '';
        }

        $pad = ord($text[strlen($text) - 1]);
        if ($pad < 1 || $pad > 16 || $pad > strlen($text)) {
            throw new RuntimeException('El relleno del valor cifrado no es valido.');
        }

        if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
            throw new RuntimeException('El valor cifrado esta corrupto o usa otra clave.');
        }

        return substr($text, 0, -1 * $pad);
    }

    private static function convertEncoding($value, $toEncoding, $fromEncoding)
    {
        if (function_exists('mb_convert_encoding')) {
            return mb_convert_encoding($value, $toEncoding, $fromEncoding);
        }

        if (function_exists('iconv')) {
            $converted = @iconv($fromEncoding, $toEncoding . '//TRANSLIT', $value);
            if ($converted !== false) {
                return $converted;
            }
        }

        return $value;
    }

    private static function secretKey()
    {
        $key = getenv('APP_LEGACY_KEY');
        $key = ($key === false || $key === '') ? self::DEFAULT_SECRET_KEY : $key;
        if (strlen($key) !== 16) {
            throw new RuntimeException('APP_LEGACY_KEY debe contener exactamente 16 bytes.');
        }
        return $key;
    }
}