<?php
/**
 * Base option abstraction for RewardsWP settings.
 *
 * @package RewardsWP
 */

namespace Awesomemotive\Rewardswp\Abstracts;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use Awesomemotive\Rewardswp\Interfaces\IntegrationInterface;
use Awesomemotive\Rewardswp\Services\OptionsService;

use function Awesomemotive\Rewardswp\Helpers\is_list;

/**
 * Base class for plugin options with common helpers.
 */
abstract class AbstractOption {
	/**
	 * Options service instance.
	 *
	 * @since 1.0.0
	 *
	 * @var OptionsService
	 */
	protected OptionsService $options_service;

	/**
	 * Current integration instance.
	 *
	 * @since 1.0.0
	 *
	 * @var IntegrationInterface
	 */
	protected IntegrationInterface $integration;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 *
	 * @param OptionsService       $options_service Options service instance.
	 * @param IntegrationInterface $integration     Current integration instance.
	 */
	public function __construct( OptionsService $options_service, IntegrationInterface $integration ) {
		$this->options_service = $options_service;
		$this->integration     = $integration;
	}

	/**
	 * Get the default value for the option.
	 *
	 * @since 1.0.0
	 *
	 * @return mixed
	 */
	abstract public function get_default_value();

	/**
	 * Get the label for the option.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	abstract public function get_label(): string;

	/**
	 * Get the option name used in the option array.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	abstract public function get_name(): string;

	/**
	 * Get the type of the option (text, textarea, select, etc.).
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	abstract public function get_type(): string;

	/**
	 * Get the optional option group.
	 *
	 * An option can be grouped up to one level, use snake_case values for this.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public function get_group(): string {
		return '';
	}

	/**
	 * A method to sanitize the option before saving.
	 *
	 * @since 1.0.0
	 *
	 * @param mixed $value The value to be sanitized.
	 *
	 * @return mixed The sanitized value.
	 */
	public function sanitize( $value ) {
		return sanitize_text_field( $value );
	}

	/**
	 * Get the current value for the option.
	 *
	 * @since 1.0.0
	 *
	 * @return mixed
	 */
	public function get_value() {
		$group = $this->get_group();

		if ( '' === $group ) {
			$value = $this->options_service->get_option( $this->get_name() );
		} else {
			$value = $this->options_service->get_option( $this->get_name(), $group );
		}

		$default = $this->get_default_value();

		if ( is_null( $value ) ) {
			return $default;
		}

		// Merge stored array with defaults so newly added keys are always present.
		// Only merge associative arrays — skip numeric lists (e.g. spend/earn actions)
		// where the stored shape differs from the default shape.
		if ( is_array( $default ) && is_array( $value ) && ! is_list( $value ) ) {
			return wp_parse_args( $value, $default );
		}

		return $value;
	}

	/**
	 * Get the available options for the option.
	 * Should be overridden by classes that have multiple options (e.g., select, checkbox, etc.).
	 *
	 * @since 1.0.0
	 *
	 * Example usage:
	 *
	 * return [
	 *     [
	 *         'value' => 'icon_with_text',
	 *         'label' => esc_html__( 'Icon with text', 'rewardswp' ),
	 *     ],
	 *     [
	 *         'value' => 'text_only',
	 *         'label' => esc_html__( 'Text only', 'rewardswp' ),
	 *     ],
	 *     [
	 *         'value' => 'icon_only',
	 *         'label' => esc_html__( 'Icon only', 'rewardswp' ),
	 *     ],
	 * ];
	 *
	 * @return array Array of options, where each option includes 'value' and 'label'.
	 */
	public function get_options(): array {
		return [];
	}

	/**
	 * Update the option with a new value.
	 *
	 * @since 1.0.0
	 *
	 * @param mixed $new_value
	 *
	 * @return void
	 */
	public function update( $new_value ): void {
		$this->options_service->update_option(
			$this->get_name(),
			$this->sanitize( $new_value ),
			$this->get_group()
		);
	}
}
