<?php

namespace Limb_Chatbot\Includes\Traits;

use Limb_Chatbot\Includes\Data_Objects\Setting;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;

/**
 * Trait Encryptable
 *
 * Provides static methods for masking, encrypting, and decrypting string data using AES-256-CBC.
 *
 * @since 1.0.0
 */
trait Encryptable {

	/**
	 * Mask a string by showing a few visible characters at the start and end,
	 * replacing the middle part with asterisks.
	 *
	 * If the input string is too short, shows only first and last character masked with asterisks or
	 * just a masked string if length is 2 or less.
	 *
	 * @param string $data The input string to mask.
	 * @param int $mask_length The number of asterisks to replace the middle part with. Default 10.
	 * @param int $visible_start Number of visible characters at the start of the string. Default 3.
	 * @param int $visible_end Number of visible characters at the end of the string. Default 3.
	 *
	 * @return string The masked string with visible edges and middle masked with asterisks.
	 *
	 * @since 1.0.0
	 */
	public static function mask( string $data, int $mask_length = 10, int $visible_start = 3, int $visible_end = 3 ): string {
		$length = strlen( $data );
		// If too short, return just the masked string with visible edges
		if ( $length <= ( $visible_start + $visible_end ) ) {
			if ( $length <= 2 ) {
				return str_repeat( '*', $mask_length ); // Too short to show anything
			}

			// Show first and last character, mask middle
			return substr( $data, 0, 1 ) . str_repeat( '*', $mask_length ) . substr( $data, - 1 );
		}
		$start = substr( $data, 0, $visible_start );
		$end   = substr( $data, - $visible_end );

		return $start . str_repeat( '*', $mask_length ) . $end;
	}

	/**
	 * Encrypt the given data using AES-256-CBC cipher.
	 *
	 * Uses an encryption key retrieved from settings, with the first 16 bytes used as IV.
	 *
	 * @param string $data The plaintext data to encrypt.
	 *
	 * @return string The base64-encoded encrypted string.
	 *
	 * @throws Exception If the encryption key is missing.
	 *
	 * @since 1.0.0
	 */
	public static function encrypt( $data ): string {
		$key = self::get_encryption_key();

		return openssl_encrypt( $data, 'AES-256-CBC', $key, 0, substr( $key, 0, 16 ) );
	}

	/**
	 * Retrieve the encryption key from settings.
	 *
	 * @throws Exception If the encryption key is missing or empty.
	 *
	 * @return string The encryption key string.
	 *
	 * @since 1.0.0
	 */
	private static function get_encryption_key(): string {
		$key = Setting::find( Setting::SETTING_PREFIX . 'settings.encryption_key' )->get_value();
		if ( ! $key ) {
			throw new Exception( Error_Codes::MISSING_VALUE, __( 'Encryption key is missing.', 'limb-chatbot' ) );
		}

		return $key;
	}

	/**
	 * Decrypt the given encrypted data using AES-256-CBC cipher.
	 *
	 * Uses an encryption key retrieved from settings, with the first 16 bytes used as IV.
	 *
	 * @param string $encryptedData The base64-encoded encrypted string.
	 *
	 * @return string The decrypted plaintext string.
	 *
	 * @throws Exception If the encryption key is missing.
	 *
	 * @since 1.0.0
	 */
	public static function decrypt( $encryptedData ): string {
		$key = self::get_encryption_key();

		return openssl_decrypt( $encryptedData, 'AES-256-CBC', $key, 0, substr( $key, 0, 16 ) );
	}
}