<?php
/**
 * Copyright © Simplyin. All rights reserved.
 */
declare(strict_types=1);

namespace Simplyin\Payment\Helper;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;
use Magento\Framework\App\DeploymentConfig;

/**
 * Encryption helper for SimplyIN API
 *
 * Provides encryption functions for secure order status updates.
 * Key derivation now uses HKDF with a site-specific salt derived from the
 * Magento deployment crypt key, so the derived key is not email-only.
 */
class Encryption
{
    private const CIPHER = 'aes-256-cbc';

    /**
     * Config path for optional per-store encryption salt override
     */
    private const XML_PATH_ENCRYPTION_SALT = 'payment/simplyin_payment/encryption_salt';

    /**
     * @var DeploymentConfig
     */
    private DeploymentConfig $deploymentConfig;

    /**
     * @var ScopeConfigInterface
     */
    private ScopeConfigInterface $scopeConfig;

    /**
     * Constructor
     *
     * @param DeploymentConfig $deploymentConfig
     * @param ScopeConfigInterface $scopeConfig
     */
    public function __construct(
        DeploymentConfig $deploymentConfig,
        ScopeConfigInterface $scopeConfig
    ) {
        $this->deploymentConfig = $deploymentConfig;
        $this->scopeConfig = $scopeConfig;
    }

    /**
     * Encrypt plaintext using AES-256-CBC
     *
     * @param string $plaintext
     * @param string $secretKey Binary secret key
     * @return string|false Base64 encoded encrypted data
     */
    public function encrypt(string $plaintext, string $secretKey)
    {
        $ivLength = openssl_cipher_iv_length(self::CIPHER);
        $iv = openssl_random_pseudo_bytes($ivLength);

        $ciphertextRaw = openssl_encrypt(
            $plaintext,
            self::CIPHER,
            $secretKey,
            OPENSSL_RAW_DATA,
            $iv
        );

        if ($ciphertextRaw === false) {
            return false;
        }

        return base64_encode($iv . $ciphertextRaw);
    }

    /**
     * Decrypt ciphertext using AES-256-CBC
     *
     * @param string $ciphertext Base64 encoded encrypted data
     * @param string $secretKey Binary secret key
     * @return string|false Decrypted plaintext
     */
    public function decrypt(string $ciphertext, string $secretKey)
    {
        // phpcs:ignore Magento2.Functions.DiscouragedFunction.Discouraged -- required to decode AES ciphertext envelope (base64-wrapped iv + raw ciphertext)
        $data = base64_decode($ciphertext, true);
        if ($data === false) {
            return false;
        }

        $ivLength = openssl_cipher_iv_length(self::CIPHER);
        if (strlen($data) <= $ivLength) {
            return false;
        }
        $iv = substr($data, 0, $ivLength);
        $ciphertextRaw = substr($data, $ivLength);

        $plaintext = openssl_decrypt(
            $ciphertextRaw,
            self::CIPHER,
            $secretKey,
            OPENSSL_RAW_DATA,
            $iv
        );

        return $plaintext;
    }

    /**
     * Hash email for SimplyIN API
     *
     * Format: SHA-256 hash of "--{email}--"
     *
     * @param string $email
     * @return string
     */
    public function hashEmail(string $email): string
    {
        return hash('sha256', '--' . strtolower(trim($email)) . '--');
    }

    /**
     * Get secret key from email — hardened with site-specific salt via HKDF
     *
     * The key is derived using HKDF (RFC 5869) combining:
     * - IKM (Input Key Material): email-based seed (backward-compat with SimplyIN API format)
     * - Salt: Magento deployment crypt key (unique per installation)
     * - Info: context string "simplyin-order-encryption"
     *
     * This ensures the derived key is not reconstructible from email alone without
     * access to the Magento installation's crypt key.
     *
     * @param string $email
     * @return string Binary secret key (32 bytes)
     */
    public function getSecretKey(string $email): string
    {
        $normalizedEmail = strtolower(trim($email));

        // Original email-based seed (required for SimplyIN API compatibility)
        $emailSeed = hash('sha256', '__' . $normalizedEmail . '__', true);

        // Site-specific salt from Magento crypt key
        $salt = $this->getSiteSalt();

        // Use HKDF to derive a strong key combining email + site salt
        // This makes the key non-derivable without Magento's crypt key
        $derivedKey = hash_hkdf(
            'sha256',
            $emailSeed,
            32,
            'simplyin-order-encryption',
            $salt
        );

        return $derivedKey;
    }

    /**
     * Get secret key using legacy method (email-only derivation)
     *
     * Retained for backward compatibility with existing encrypted data
     * in the SimplyIN API that was encrypted before the HKDF upgrade.
     *
     * @param string $email
     * @return string Binary secret key
     * @deprecated Retained only to decrypt SimplyIN API data encrypted before the
     *             HKDF upgrade (email-only derivation, no installation salt); must not
     *             be used for new encryptions.
     * @see \Simplyin\Payment\Helper\Encryption::getSecretKey()
     */
    public function getSecretKeyLegacy(string $email): string
    {
        return hash('sha256', '__' . strtolower(trim($email)) . '__', true);
    }

    /**
     * Get site-specific salt for key derivation
     *
     * Priority:
     * 1. Admin-configured encryption_salt (allows rotation)
     * 2. Magento deployment crypt key (always available)
     *
     * @return string Binary salt
     */
    private function getSiteSalt(): string
    {
        // Check for admin-configured salt (allows rotation without changing crypt key)
        $configuredSalt = (string) $this->scopeConfig->getValue(
            self::XML_PATH_ENCRYPTION_SALT,
            ScopeInterface::SCOPE_STORE
        );

        if (!empty($configuredSalt)) {
            return hash('sha256', $configuredSalt, true);
        }

        // Fallback to Magento's deployment crypt key
        $cryptKey = (string) $this->deploymentConfig->get('crypt/key', '');

        return hash('sha256', 'simplyin-salt-' . $cryptKey, true);
    }
}
