<?php
/**
 * Based on the code from the following packages:
 * Class Google\Site_Kit\Core\Storage\Data_Encryption
 *
 * @package   Google\Site_Kit
 * @copyright 2019 Google LLC
 * @license   https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link      https://sitekit.withgoogle.com
 */

namespace SRFM_Pro\Inc\Pro\Native_Integrations;

use SRFM\Inc\Helper as SRFM_Helper;
use SRFM_Pro\Inc\Helper;

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;

/**
 * Class responsible for encrypting and decrypting data.
 *
 * @since 1.13.0
 * @access private
 * @ignore
 */
class Encryption {
	/**
	 * Key to use for encryption.
	 *
	 * @since 1.13.0
	 * @var string
	 */
	private $key;

	/**
	 * Salt to use for encryption.
	 *
	 * @since 1.13.0
	 * @var string
	 */
	private $salt;

	/**
	 * Constructor.
	 *
	 * @since 1.13.0
	 */
	final public function __construct() {
		$this->key  = $this->get_default_key();
		$this->salt = $this->get_default_salt();
	}

	/**
	 * Static Facade Accessor
	 *
	 * @param string $method Method to call.
	 * @param mixed  $params Method params.
	 *
	 * @return mixed
	 */
	public static function __callStatic( $method, $params ) {
		/**
		 * Ignore line
		 *
		 * @phpstan-ignore-next-line
		 */
		return call_user_func_array( [ new static(), $method ], $params );
	}

	/**
	 * Encrypts a value.
	 *
	 * If a user-based key is set, that key is used. Otherwise the default key is used.
	 *
	 * @since 1.13.0
	 *
	 * @param string $value Value to encrypt.
	 * @return string|bool Encrypted value, or false on failure.
	 */
	protected function encrypt( $value ) {
		if ( ! extension_loaded( 'openssl' ) ) {
			return $value;
		}

		// Convert arrays or objects to JSON before encryption.
		if ( is_array( $value ) || is_object( $value ) ) {
			$value = wp_json_encode( $value );
			if ( false === $value ) {
				return false;
			}
		}

		$method = 'aes-256-ctr';
		$ivlen  = openssl_cipher_iv_length( $method );
		$iv     = openssl_random_pseudo_bytes( SRFM_Helper::get_integer_value( $ivlen ) );

		$raw_value = openssl_encrypt( $value . $this->salt, $method, $this->key, 0, $iv );
		if ( ! $raw_value ) {
			return false;
		}

		return base64_encode( $iv . $raw_value ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
	}

	/**
	 * Decrypts a value.
	 *
	 * If a user-based key is set, that key is used. Otherwise the default key is used.
	 *
	 * @since 1.13.0
	 *
	 * @param string $raw_value Value to decrypt.
	 * @return string|bool|mixed Decrypted value, or false on failure.
	 */
	protected function decrypt( $raw_value ) {
		if ( ! extension_loaded( 'openssl' ) ) {
			return $raw_value;
		}

		$raw_value = SRFM_Helper::get_string_value( base64_decode( $raw_value, true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode

		$method = 'aes-256-ctr';
		$ivlen  = SRFM_Helper::get_integer_value( openssl_cipher_iv_length( $method ) );
		$iv     = substr( $raw_value, 0, $ivlen );

		$raw_value = substr( $raw_value, $ivlen );

		$value = openssl_decrypt( $raw_value, $method, $this->key, 0, $iv );
		if ( ! $value || substr( $value, - strlen( $this->salt ) ) !== $this->salt ) {
			return false;
		}

		$decrypted = substr( $value, 0, - strlen( $this->salt ) );

		// Try to decode JSON if the result looks like JSON.
		if ( Helper::is_json( $decrypted ) ) {
			$json_decoded = json_decode( $decrypted, true );
			if ( null !== $json_decoded ) {
				return $json_decoded;
			}
		}

		return $decrypted;
	}

	/**
	 * Gets the default encryption key to use.
	 *
	 * @since 1.13.0
	 *
	 * @return string Default (not user-based) encryption key.
	 */
	protected function get_default_key() {

		if ( defined( 'SRFM_ENCRYPTION_KEY' ) && '' !== SRFM_ENCRYPTION_KEY ) {
			return SRFM_ENCRYPTION_KEY;
		}

		if ( defined( 'LOGGED_IN_KEY' ) && '' !== LOGGED_IN_KEY ) {
			return LOGGED_IN_KEY;
		}

		// If this is reached, you're either not on a live site or have a serious security issue.
		return 'this-is-fallback-key-for-encryption';
	}

	/**
	 * Gets the default encryption salt to use.
	 *
	 * @since 1.13.0
	 *
	 * @return string Encryption salt.
	 */
	private function get_default_salt() {
		if ( defined( 'SRFM_ENCRYPTION_SALT' ) && '' !== SRFM_ENCRYPTION_SALT ) {
			return SRFM_ENCRYPTION_SALT;
		}

		if ( defined( 'LOGGED_IN_SALT' ) && '' !== LOGGED_IN_SALT ) {
			return LOGGED_IN_SALT;
		}

		// If this is reached, you're either not on a live site or have a serious security issue.
		return 'this-is-fallback-salt-for-encryption';
	}
}
