<?php
/**
 * A class for getting and setting various plugin options.
 *
 * @since 3.0.0
 */

declare(strict_types=1);
namespace FeData\FastEvents\Storage;

defined( 'ABSPATH' ) || exit;

use Exception;
use FeData\FastEvents\Utilities\JsonUtil;
use FeData\FastEvents\Utilities\SettingsDefaults;
use stdClass;

/**
 * A class for getting and setting various plugin options.
 */
class Options {

	private const FE_PREFIX             = 'fast_events_';
	private const SAAS_ERROR            = 'fast_events_saas_error';
	private const ACTION_SCHEDULER      = 'fast_events_action_scheduler_initialized';
	private const FAST_EVENTS_EMAIL_MSG = 'fast_events_email_msg_';
	private const MANAGEMENT            = 'fast_events_management_options';
	private const SETTINGS              = 'fast_events_options';
	private const VERSION               = 'fast_events_version';
	private const DEMO_DATA_INSTALLED   = 'fast_events_demo_data_installed';
	private const DEMO_DATA_POST_IDS    = 'fast_events_demo_data_post_ids';


	/**
	 * Raw options of this plugin
	 *
	 * @since 1.0
	 * @var stdClass
	 */
	private stdClass $options;

	/**
	 * Settings of the management interface
	 *
	 * @since 2.0
	 * @var array
	 */
	private array $management;

	/**
	 * Options constructor.
	 *
	 * @since 3.0.0
	 *
	 * @param Encryption $encryption Encryption class.
	 */
	public function __construct( private readonly Encryption $encryption ) {
		$raw_value = get_option( self::SETTINGS, array() );
		if ( is_string( $raw_value ) ) {
			$raw_value = $this->encryption->decrypt( $raw_value );
			$data      = json_decode( $raw_value );
			if ( json_last_error() !== JSON_ERROR_NONE ) { // Invalid JSON.
				$this->options = SettingsDefaults::default_settings(); // Reset settings.
			} else {
				$this->options = $data; // Valid JSON.
			}
		} else {
			$this->options = is_array( $raw_value ) ? (object) $raw_value : SettingsDefaults::default_settings();
		}

		$value            = get_option( self::MANAGEMENT, array() );
		$this->management = is_array( $value ) ? $value : array();
	}


	/**
	 * Singletons should not be cloneable.
	 *
	 * @since 2.5.0
	 */
	private function __clone(): void {}


	/**
	 * Prevent from being unserialized.
	 *
	 * @since 2.5.0
	 */
	public function __wakeup(): void {}


	/**
	 * Get the plugin settings
	 *
	 * @since 2.0
	 *
	 * @return stdClass
	 */
	public function get_plugin_settings(): stdClass {
		return $this->options;
	}


	/**
	 * Set the Fast Events settings
	 *
	 * @since 1.0
	 * @since 2.5.0 Use wp_json_encode instead of serialize.
	 *
	 * @param stdClass $options Array of plugin settings.
	 */
	public function set_plugin_settings( stdClass $options ): void {
		$this->options = $options;
		update_option( self::SETTINGS, $this->encryption->encrypt( wp_json_encode( $options ) ), true );
	}


	/**
	 * Get the plugin version
	 *
	 * @since 3.0
	 *
	 * @return string
	 */
	public function get_plugin_version(): string {
		return get_option( self::VERSION, '' );
	}


	/**
	 * Set the Fast Events settings
	 *
	 * @since 1.0
	 * @since 2.5.0 Use wp_json_encode instead of serialize.
	 *
	 * @param string $version Plugin version.
	 */
	public function set_plugin_version( string $version ): void {
		update_option( self::VERSION, $version, true );
	}

	/**
	 * Get the Management interface settings
	 *
	 * @since 2.0
	 *
	 * @return array
	 */
	public function get_management_settings(): array {
		return $this->management;
	}


	/**
	 * Set the Management settings
	 *
	 * @since 2.0
	 *
	 * @param array $management Array of plugin settings.
	 */
	public function set_management_settings( array $management ): void {
		$this->management = $management;
		update_option( self::MANAGEMENT, $management, true );
	}


	/**
	 * Delete the management settings.
	 *
	 * @since 3.0
	 */
	public function delete_management_settings(): void {
		delete_option( self::MANAGEMENT );
	}


	/**
	 * Is the demo data installed?
	 *
	 * @since 3.0
	 *
	 * @return bool
	 */
	public function is_demo_data_installed(): bool {
		$value = get_option( self::DEMO_DATA_INSTALLED, false );
		return true === $value || '1' === $value;
	}


	/**
	 * Demo data installed.
	 *
	 * @since 3.0
	 */
	public function demo_data_installed(): void {
		update_option( self::DEMO_DATA_INSTALLED, true, false );
	}


	/**
	 * Save the post-ids of the demo data.
	 *
	 * @since 3.0
	 *
	 * @param array $post_ids Array of post-ids.
	 */
	public function save_post_ids( array $post_ids ): void {
		update_option( self::DEMO_DATA_POST_IDS, $post_ids, false );
	}


	/**
	 * Is the Action scheduler initialized?
	 *
	 * @since 3.0
	 *
	 * @return bool
	 */
	public function is_as_initialized(): bool {
		$value = get_option( self::ACTION_SCHEDULER, false );
		return true === $value || '1' === $value;
	}


	/**
	 * Set the Action Scheduler to be initialized.
	 *
	 * @since 3.0
	 */
	public function set_as_initialized(): void {
		update_option( self::ACTION_SCHEDULER, true, true );
	}


	/**
	 * Delete the Action Scheduler initialisation status.
	 *
	 * @since 3.0
	 */
	public function delete_as_initialized(): void {
		delete_option( self::ACTION_SCHEDULER );
	}


	/**
	 * Get the saved message from the options table.
	 *
	 * @since 1.0
	 * @since 2.0.3 Use option table as cache
	 *
	 * @param int $msg_id Unique identifier.
	 * @return array
	 */
	public function get_message( int $msg_id ): array {
		return get_option( self::FAST_EVENTS_EMAIL_MSG . $msg_id, array() );
	}


	/**
	 * Delete a saved message from the options table.
	 *
	 * @since 2.0.3
	 *
	 * @param int $msg_id Unique identifier.
	 */
	public function delete_message( int $msg_id ): void {
		delete_option( self::FAST_EVENTS_EMAIL_MSG . $msg_id );
	}


	/**
	 * Save a mail-message (subject and body) in the options table.
	 *
	 * @since 1.0
	 * @since 2.0 Set the file mode.
	 * @since 2.0.3 Use options table as a cache.
	 * @since 2.3.0 Use fast_events_json_encode.
	 *
	 * @param stdClass $result Order detail.
	 *
	 * @return int
	 */
	public function save_message( stdClass $result ): int {
		try {
			$id = random_int( 100, PHP_INT_MAX );
		} catch ( Exception ) {
			$id = time();
		} finally {
			add_option(
				self::FAST_EVENTS_EMAIL_MSG . $id,
				array(
					'date'  => current_time( 'mysql' ),
					'email' => $result,
					'hash'  => wp_hash( JsonUtil::fast_events_json_encode( $result ) ),
				),
				'',
				false
			);
		}

		return $id;
	}


	/**
	 * Save the last SaaS error message.
	 *
	 * @since 3.0.0
	 *
	 * @param string $msg The error message.
	 */
	public function save_saas_error( string $msg ): void {
		add_option( self::SAAS_ERROR, $msg, '', false );
	}


	/**
	 * Get the last SaaS error message.
	 *
	 * @since 3.0.0
	 *
	 * @return bool|string
	 */
	public function get_saas_error(): bool|string {
		return get_option( self::SAAS_ERROR );
	}


	/**
	 * Delete the last SaaS error message.
	 *
	 * @since 3.0.0
	 */
	public function delete_saas_error(): void {
		delete_option( self::SAAS_ERROR );
	}

	/**
	 * Save the Payment Provider specific information
	 *
	 * @since 3.0.0
	 *
	 * @param string $provider_name The payment provider name.
	 * @param array  $data          The data to save.
	 */
	public function set_provider_data( string $provider_name, array $data ): void {
		update_option( self::FE_PREFIX . $provider_name, $this->encryption->encrypt( wp_json_encode( $data ) ), true );
	}

	/**
	 * Retrieve the Payment Provider specific information
	 *
	 * @since 3.0.0
	 *
	 * @param string $provider_name The payment provider name.
	 * @return array                The data of the payment provider.
	 */
	public function get_provider_data( string $provider_name ): array {
		$result = json_decode( $this->encryption->decrypt( get_option( self::FE_PREFIX . $provider_name, '' ) ), true );
		if ( json_last_error() !== JSON_ERROR_NONE ) { // Invalid JSON.
			return array();
		} else {
			return $result;
		}
	}

	/**
	 * Delete the Payment Provider specific information
	 *
	 * @since 3.0.0
	 *
	 * @param string $provider_name The payment provider name.
	 */
	public function delete_provider_data( string $provider_name ): void {
		delete_option( self::FE_PREFIX . $provider_name );
	}
}
