<?php
/* Prevent direct access */
defined('ABSPATH') or die("You can't access this file directly.");

/**
 * Class WD_ASL_Instances
 *
 * This class handles the data transfer between code and instance data
 *
 * @class         WD_ASL_Instances
 * @version       1.0
 * @package       AjaxSearchLite/Classes/Core
 * @category      Class
 * @author        Ernest Marcinko
 */
class WD_ASL_Instances {

	/**
	 * Core singleton class
	 *
	 * @var WD_ASL_Instances self
	 */
	private static $_instance;

	/**
	 * This holds the search instances
	 *
	 * @var array()
	 */
	private $instances;

	/**
	 * This holds the search instances without data
	 *
	 * @var array()
	 */
	private $instancesNoData;

	/**
	 * When updating, this variable sets to true, telling that instances need re-parsing
	 *
	 * @var bool
	 */
	private $refresh = false;

	/**
	 * The search instances init script JSON data
	 *
	 * @var string[]
	 */
	private $script_data = array();

	/**
	 * Gets the search instance if exists
	 *
	 * @param int  $id
	 * @param bool $force_refresh
	 * @return bool|array
	 */
	public function get( $id = -1, $force_refresh = false ) {

		if ( $this->refresh || $force_refresh ) {
			$this->init();
			$this->refresh = false;
		}

		if ( $id > -1 ) {
			return isset($this->instances[ $id ]) ? $this->instances[ $id ] : array();
		}

		return $this->instances;
	}

	/**
	 * Gets the search instance if exists, without data
	 *
	 * @param int  $id
	 * @param bool $force_refresh
	 * @return bool|array
	 */
	public function getWithoutData( $id = -1, $force_refresh = false ) {

		if ( $this->refresh || $force_refresh ) {
			$this->init();
			$this->refresh = false;
		}

		if ( $id > -1 ) {
			return isset($this->instancesNoData[ $id ]) ? $this->instancesNoData[ $id ] : array();
		}

		return $this->instancesNoData;
	}

	/**
	 * Checks if the given search instance exists
	 *
	 * @param $id
	 * @return bool
	 */
	public function exists( $id ) {
		return isset($this->instances[ $id ]);
	}

	/**
	 * Update the search data
	 *
	 * @param $id
	 * @param $data
	 * @return false|int
	 */
	public function update( $id = 0, $data = array() ) {

		$this->refresh = true;
		$data          = apply_filters('asl_save_search_instance_options', $data);

		return update_option('asl_options', $data);
	}

	/**
	 * This method is intended to use on params AFTER parsed from the database
	 *
	 * @param $params
	 * @return mixed
	 */
	public function decode_params( $params ) {
		/**
		 * New method for future use.
		 * Detects if there is a _decode_ prefixed input for the current field.
		 * If so, then decodes and overrides the posted value.
		 */
		foreach ( $params as $k =>$v ) {
			if ( gettype($v) === 'string' && substr($v, 0, strlen('_decode_')) == '_decode_' ) {
				$real_v       = substr($v, strlen('_decode_'));
				$params[ $k ] = json_decode(base64_decode($real_v), true);
			}
		}
		return $params;
	}

	/**
	 * Records that a search instance has been printed on the current page request.
	 *
	 * The instance configuration itself is no longer persisted: the front-end runtime
	 * reads it straight from the .asl_init_data div's data-settings attribute (see
	 * asl.shortcode.script.php and src/client/plugin/wrapper/ASL.ts). This only tracks
	 * which instance IDs rendered. See wpdreams/ajax-search-lite#127.
	 *
	 * @param int|string $id   The search instance ID.
	 * @param array      $data Kept for signature/BC; no longer stored.
	 */
	// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter -- $data kept for BC; config is read client-side from the div, not persisted.
	public function add_script_data( $id, array $data ) {
		$this->script_data[ $id ] = true;
	}

	// ------------------------------------------------------------
	// ---------------- PRIVATE --------------------
	// ------------------------------------------------------------

	/**
	 * Just calls init
	 */
	private function __construct() {
		$this->init();
	}

	/**
	 * Fetches the search instances from the DB and stores them internally for future use
	 */
	private function init() {
		// Reset both variables, so in case of deleting no remains are left
		$this->instances       = array();
		$this->instancesNoData = array();

		$instances = array( 0 => get_option( 'asl_options', array() ) );

		foreach ( $instances as $k => $instance ) {
			$this->instancesNoData[ $k ] = array(
				'id' => $k,
			);

			$this->instances[ $k ]['id'] = $k;

			if ( is_array($instance) ) {
				$this->instances[ $k ]['raw_data'] = $this->decode_params($instance);
				$this->instances[ $k ]['data']     = array_merge(
					wd_asl()->options['asl_defaults'],
					$this->instances[ $k ]['raw_data']
				);
			} else {
				$this->instances[ $k ]['raw_data'] = wd_asl()->options['asl_defaults'];
				$this->instances[ $k ]['data']     = wd_asl()->options['asl_defaults'];
			}
		}
	}

	// ------------------------------------------------------------
	// ---------------- SINGLETON SPECIFIC --------------------
	// ------------------------------------------------------------

	/**
	 * Get the instance of self
	 *
	 * @return self
	 */
	public static function getInstance() {
		if ( !( self::$_instance instanceof self ) ) {
			self::$_instance = new self();
		}

		return self::$_instance;
	}
}
