<?php
/**
 * EarnActionsOption.php.
 *
 * @package RewardsWP
 */

namespace Awesomemotive\Rewardswp\Settings\Options\Points;

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

use Awesomemotive\Rewardswp\Abstracts\AbstractOption;
use Awesomemotive\Rewardswp\Interfaces\OptionInterface;
use function Awesomemotive\Rewardswp\Helpers\get_option_value;
use function Awesomemotive\Rewardswp\Helpers\get_points_currency_singular;
use function Awesomemotive\Rewardswp\Helpers\get_points_currency_plural;
use function Awesomemotive\Rewardswp\Helpers\get_site_currency;
use function Awesomemotive\Rewardswp\Helpers\format_currency;
use function Awesomemotive\Rewardswp\Helpers\get_default_points_base_amount;

/**
 * Class EarnActionsOption
 *
 * @since 1.1.0
 */
/**
 * Class EarnActionsOption.
 */
class EarnActionsOption extends AbstractOption implements OptionInterface {

	/**
	 * Get the setting key used in the option array.
	 *
	 * @since 1.1.0
	 *
	 * @return string
	 */
	public function get_name(): string {
		return 'earn_actions';
	}

	/**
	 * Get the setting group.
	 *
	 * @since 1.1.0
	 *
	 * @return string
	 */
	public function get_group(): string {
		return 'points';
	}

	/**
	 * Get the action type definitions.
	 *
	 * @since 1.1.0
	 *
	 * @return array[]
	 */
	public function get_action_definitions(): array {
		return [
			'purchase'       => [
				'id'                => 'purchase',
				'points_per_dollar' => 5,
				'base_amount'       => null, // null means use currency-appropriate default.
				'title'             => __( 'Make a purchase', 'rewardswp' ),
				'custom_title'      => '',
				'description'       => sprintf(
					/* translators: %s: base spend amount with currency symbol. */
					esc_html__( 'Reward customers with points for every %s they spend on completed orders', 'rewardswp' ),
					format_currency( get_default_points_base_amount() )
				),
				'icon'              => 'earn-purchase',
				'feature_slug'      => 'earn_purchase',
			],
			'signup'         => [
				'id'           => 'signup',
				'points'       => 100,
				'title'        => __( 'Create an account', 'rewardswp' ),
				'custom_title' => '',
				'description'  => __( 'Award points to new members when they register an account on your site', 'rewardswp' ),
				'icon'         => 'earn-signup',
				'feature_slug' => 'earn_signup',
			],
			'product_review' => [
				'id'                  => 'product_review',
				'points'              => 100,
				'verified_buyer_only' => true,
				'title'               => __( 'Write a product review', 'rewardswp' ),
				'custom_title'        => __( 'Leave a review', 'rewardswp' ),
				'description'         => __( 'Give members points when they submit product reviews that you approve', 'rewardswp' ),
				'icon'                => 'earn-comment',
				'feature_slug'        => 'earn_product_review',
			],
			'first_order'    => [
				'id'           => 'first_order',
				'points'       => 200,
				'title'        => __( 'First order bonus', 'rewardswp' ),
				'custom_title' => __( 'Make your first order', 'rewardswp' ),
				'description'  => __( "Award a one-time bonus after a member's first completed order.", 'rewardswp' ),
				'icon'         => 'earn-first-purchase-bonus',
				'feature_slug' => 'earn_first_order',
			],
			'birthday'       => [
				'id'           => 'birthday',
				'points'       => 150,
				'title'        => __( 'Birthday bonus', 'rewardswp' ),
				'custom_title' => __( 'Celebrate your birthday', 'rewardswp' ),
				'description'  => __( 'Collect member birthdays and automatically reward them with bonus points each year.', 'rewardswp' ),
				'icon'         => 'earn-birthday',
				'feature_slug' => 'earn_birthday',
			],
		];
	}

	/**
	 * Get the default value for the setting.
	 *
	 * If you set `enabled` as null you can hide an item from the initial list.
	 *
	 * @since 1.1.0
	 *
	 * @return array
	 */
	public function get_default_value(): array {
		$definitions = $this->get_action_definitions();
		$defaults    = [];

		foreach ( $definitions as $key => $definition ) {
			$defaults[ $key ] = array_merge(
				$definition,
				[ 'enabled' => false ] // Add the 'enabled' key here.
			);
		}

		return $defaults;
	}

	/**
	 * Get the label for the setting.
	 *
	 * @since 1.1.0
	 *
	 * @return string
	 */
	public function get_label(): string {
		return esc_html__( 'Earn Points Actions', 'rewardswp' );
	}

	/**
	 * Get the type of the setting (text, textarea, select, etc).
	 *
	 * @since 1.1.0
	 *
	 * @return string
	 */
	public function get_type(): string {
		return 'array';
	}

	/**
	 * Custom sanitization for earn points actions.
	 *
	 * @since 1.1.0
	 *
	 * @param mixed $value The value to be sanitized.
	 *
	 * @return array
	 */
	public function sanitize( $value ): array {
		if ( ! is_array( $value ) ) {
			return $this->get_default_value();
		}

		$default   = $this->get_default_value();
		$sanitized = [];

		// Process purchase action.
		$sanitized['purchase'] = $this->sanitize_purchase_action( $value['purchase'] ?? [], $default['purchase'] );

		// Process signup action.
		$sanitized['signup'] = $this->sanitize_signup_action( $value['signup'] ?? [], $default['signup'] );

		// Process product review action.
		$sanitized['product_review'] = $this->sanitize_product_review_action( $value['product_review'] ?? [], $default['product_review'] );

		// Process first order action.
		$sanitized['first_order'] = $this->sanitize_first_order_action( $value['first_order'] ?? [], $default['first_order'] );

		// Process birthday action.
		$sanitized['birthday'] = $this->sanitize_birthday_action( $value['birthday'] ?? [], $default['birthday'] );

		return $sanitized;
	}

	/**
	 * Sanitize the purchase action.
	 *
	 * @since 1.1.0
	 *
	 * @param array $purchase The purchase action to sanitize.
	 * @param array $default  The default purchase action.
	 *
	 * @return array
	 */
	private function sanitize_purchase_action( array $purchase, array $default ): array {
		if ( empty( $purchase ) ) {
			return $default;
		}

		// Handle points_per_dollar: if empty string or not set, default to 0.
		$points_per_dollar = 0;
		if ( isset( $purchase['points_per_dollar'] ) && $purchase['points_per_dollar'] !== '' ) {
			$points_per_dollar = max( 0, (int) $purchase['points_per_dollar'] );
		}

		// Handle base_amount: if empty or not set, use null to indicate currency default should be used.
		$base_amount = null;
		if ( isset( $purchase['base_amount'] ) && $purchase['base_amount'] !== '' && $purchase['base_amount'] !== null ) {
			$base_amount = max( 1, (int) $purchase['base_amount'] );
		}

		return [
			'id'                => 'purchase',
			'enabled'           => isset( $purchase['enabled'] ) ? (bool) $purchase['enabled'] : $default['enabled'],
			'configured'        => isset( $purchase['configured'] ) ? (bool) $purchase['configured'] : false,
			'title'             => isset( $purchase['title'] ) ? sanitize_text_field( $purchase['title'] ) : $default['title'],
			'description'       => isset( $purchase['description'] ) ? sanitize_text_field( $purchase['description'] ) : $default['description'],
			'icon'              => isset( $purchase['icon'] ) ? sanitize_text_field( $purchase['icon'] ) : $default['icon'],
			'points_per_dollar' => $points_per_dollar,
			'base_amount'       => $base_amount,
			'feature_slug'      => isset( $purchase['feature_slug'] ) ? sanitize_text_field( $purchase['feature_slug'] ) : $default['feature_slug'],
			'order'             => isset( $purchase['order'] ) ? (int) $purchase['order'] : 0,
		];
	}

	/**
	 * Sanitize the signup action.
	 *
	 * @since 1.1.0
	 *
	 * @param array $signup  The signup action to sanitize.
	 * @param array $default The default signup action.
	 *
	 * @return array
	 */
	private function sanitize_signup_action( array $signup, array $default ): array {
		if ( empty( $signup ) ) {
			return $default;
		}

		// Handle points: if empty string or not set, default to 0.
		$points = 0;
		if ( isset( $signup['points'] ) && $signup['points'] !== '' ) {
			$points = max( 0, (int) $signup['points'] );
		}

		// Get current stored value to detect enabled state transition.
		$old_settings = $this->get_value();
		$old_enabled  = $old_settings['signup']['enabled'] ?? false;
		$new_enabled  = isset( $signup['enabled'] ) ? (bool) $signup['enabled'] : $default['enabled'];

		// Preserve existing enabled_at or set new one on enable transition.
		$enabled_at = $signup['enabled_at'] ?? $old_settings['signup']['enabled_at'] ?? null;

		// Update timestamp only when transitioning from disabled to enabled.
		if ( $new_enabled && ! $old_enabled ) {
			$enabled_at = current_time( 'mysql' );
		}

		return [
			'id'                => 'signup',
			'enabled'           => $new_enabled,
			'configured'        => isset( $signup['configured'] ) ? (bool) $signup['configured'] : false,
			'title'             => isset( $signup['title'] ) ? sanitize_text_field( $signup['title'] ) : $default['title'],
			'description'       => isset( $signup['description'] ) ? sanitize_text_field( $signup['description'] ) : $default['description'],
			'icon'              => isset( $signup['icon'] ) ? sanitize_text_field( $signup['icon'] ) : $default['icon'],
			'points'            => $points,
			'feature_slug'      => isset( $signup['feature_slug'] ) ? sanitize_text_field( $signup['feature_slug'] ) : $default['feature_slug'],
			'enabled_at'        => $enabled_at,
			'order'             => isset( $signup['order'] ) ? (int) $signup['order'] : 0,
		];
	}

	/**
	 * Sanitize the review action.
	 *
	 * @since 1.1.0
	 *
	 * @param array $review  The review action to sanitize.
	 * @param array $default The default review action.
	 *
	 * @return array
	 */
	private function sanitize_product_review_action( array $review, array $default ): array {
		if ( empty( $review ) ) {
			return $default;
		}

		// Handle points: if empty string or not set, default to 0.
		$points = 0;
		if ( isset( $review['points'] ) && $review['points'] !== '' ) {
			$points = max( 0, (int) $review['points'] );
		}

		return [
			'id'                  => 'product_review',
			'enabled'             => isset( $review['enabled'] ) ? (bool) $review['enabled'] : $default['enabled'],
			'configured'          => isset( $review['configured'] ) ? (bool) $review['configured'] : false,
			'title'               => isset( $review['title'] ) ? sanitize_text_field( $review['title'] ) : $default['title'],
			'description'         => isset( $review['description'] ) ? sanitize_text_field( $review['description'] ) : $default['description'],
			'icon'                => isset( $review['icon'] ) ? sanitize_text_field( $review['icon'] ) : $default['icon'],
			'points'              => $points,
			'verified_buyer_only' => isset( $review['verified_buyer_only'] ) ? (bool) $review['verified_buyer_only'] : $default['verified_buyer_only'],
			'feature_slug'        => isset( $review['feature_slug'] ) ? sanitize_text_field( $review['feature_slug'] ) : $default['feature_slug'],
			'order'               => isset( $review['order'] ) ? (int) $review['order'] : 0,
		];
	}

	/**
	 * Sanitize the first order action.
	 *
	 * @since 1.1.0
	 *
	 * @param array $first_order The first order action to sanitize.
	 * @param array $default     The default first order action.
	 *
	 * @return array
	 */
	private function sanitize_first_order_action( array $first_order, array $default ): array {
		if ( empty( $first_order ) ) {
			return array_merge( $default, [ 'configured' => false ] );
		}

		// Handle points: if empty string or not set, default to 0.
		$points = 0;
		if ( isset( $first_order['points'] ) && $first_order['points'] !== '' ) {
			$points = max( 0, (int) $first_order['points'] );
		}

		$sanitized = [
			'id'                => 'first_order',
			'enabled'           => isset( $first_order['enabled'] ) ? (bool) $first_order['enabled'] : $default['enabled'],
			'configured'        => isset( $first_order['configured'] ) ? (bool) $first_order['configured'] : false,
			'points'            => $points,
			'title'             => isset( $first_order['title'] ) ? sanitize_text_field( $first_order['title'] ) : $default['title'],
			'description'       => isset( $first_order['description'] ) ? sanitize_text_field( $first_order['description'] ) : $default['description'],
			'icon'              => isset( $first_order['icon'] ) ? sanitize_text_field( $first_order['icon'] ) : $default['icon'],
			'feature_slug'      => isset( $first_order['feature_slug'] ) ? sanitize_text_field( $first_order['feature_slug'] ) : $default['feature_slug'],
			'order'             => isset( $first_order['order'] ) ? (int) $first_order['order'] : 0,
		];

		return $sanitized;
	}

	/**
	 * Sanitize the birthday action.
	 *
	 * @since 1.3.0
	 *
	 * @param array $birthday The birthday action to sanitize.
	 * @param array $default  The default birthday action.
	 *
	 * @return array
	 */
	private function sanitize_birthday_action( array $birthday, array $default ): array {
		if ( empty( $birthday ) ) {
			return array_merge( $default, [ 'configured' => false ] );
		}

		// Handle points: if empty string or not set, default to 0.
		$points = 0;
		if ( isset( $birthday['points'] ) && $birthday['points'] !== '' ) {
			$points = max( 0, (int) $birthday['points'] );
		}

		return [
			'id'           => 'birthday',
			'enabled'      => isset( $birthday['enabled'] ) ? (bool) $birthday['enabled'] : $default['enabled'],
			'configured'   => isset( $birthday['configured'] ) ? (bool) $birthday['configured'] : false,
			'points'       => $points,
			'title'        => isset( $birthday['title'] ) ? sanitize_text_field( $birthday['title'] ) : $default['title'],
			'description'  => isset( $birthday['description'] ) ? sanitize_text_field( $birthday['description'] ) : $default['description'],
			'icon'         => isset( $birthday['icon'] ) ? sanitize_text_field( $birthday['icon'] ) : $default['icon'],
			'feature_slug' => isset( $birthday['feature_slug'] ) ? sanitize_text_field( $birthday['feature_slug'] ) : $default['feature_slug'],
			'order'        => isset( $birthday['order'] ) ? (int) $birthday['order'] : 0,
		];
	}

	/**
	 * Get the formatted description for an earn action.
	 *
	 * @since 1.1.0
	 * @param array $action The action data.
	 * @return string
	 */
	public function get_formatted_description( array $action, ?string $currency_singular = null, ?string $currency_plural = null ): string {
		$currency_singular = $currency_singular ?? get_points_currency_singular();
		$currency_plural   = $currency_plural ?? get_points_currency_plural();
		$description       = '';

		if ( ! isset( $action['id'] ) ) {
			return '';
		}

		switch ( $action['id'] ) {
			case 'purchase':
				if ( ! empty( $action['points_per_dollar'] ) ) {
					$points      = (int) $action['points_per_dollar'];
					$base_amount = $action['base_amount'] ?? get_default_points_base_amount();
					$description = sprintf(
						/* translators: %1$d: points, %2$s: points label, %3$s: base spend amount with currency symbol. */
						esc_html__( '%1$d %2$s for every %3$s spent', 'rewardswp' ),
						$points,
						$points === 1 ? $currency_singular : $currency_plural,
						format_currency( $base_amount )
					);
				}
				break;
			case 'signup':
				if ( ! empty( $action['points'] ) ) {
					$points      = (int) $action['points'];
					$description = sprintf(
						/* translators: %1$d points, %2$s currency label */
						'%1$d %2$s',
						$points,
						$points === 1 ? $currency_singular : $currency_plural
					);
				}
				break;
			case 'product_review':
				if ( ! empty( $action['points'] ) ) {
					$points      = (int) $action['points'];
					$description = sprintf(
						/* translators: %1$d points, %2$s currency label */
						'%1$d %2$s',
						$points,
						$points === 1 ? $currency_singular : $currency_plural
					);
				}
				break;

			case 'first_order':
				if ( ! empty( $action['points'] ) ) {
					$points      = (int) $action['points'];
					$description = sprintf(
						/* translators: %1$d points, %2$s currency label */
						'%1$d %2$s',
						$points,
						$points === 1 ? $currency_singular : $currency_plural
					);
				}
				break;

			case 'birthday':
				if ( ! empty( $action['points'] ) ) {
					$points      = (int) $action['points'];
					$description = sprintf(
						/* translators: %1$d points, %2$s currency label */
						'%1$d %2$s',
						$points,
						$points === 1 ? $currency_singular : $currency_plural
					);
				}
				break;
		}

		return $description;
	}

	/**
	 * Get the current value for the option, ensuring all defined actions are included.
	 *
	 * @since 1.1.0
	 *
	 * @return array
	 */
	public function get_value(): array {
		$saved_value = parent::get_value();
		$defaults    = $this->get_default_value();
		$definitions = $this->get_action_definitions();

		// Merge saved values with defaults to ensure all actions are present.
		$merged = $defaults;

		foreach ( $saved_value as $key => $action ) {
			if ( isset( $defaults[ $key ] ) ) {
				// Merge saved action data with default structure.
				$merged[ $key ] = array_merge( $defaults[ $key ], $action );

				// Force from the definition each load so these stay translatable; a stored value freezes the save-time locale.
				foreach ( [ 'title', 'custom_title', 'description', 'icon' ] as $def_key ) {
					if ( isset( $definitions[ $key ][ $def_key ] ) ) {
						$merged[ $key ][ $def_key ] = $definitions[ $key ][ $def_key ];
					}
				}
			}
		}

		return $merged;
	}

	/**
	 * Get all enabled earn points actions.
	 *
	 * @since 1.1.0
	 *
	 * @return array
	 */
	public function get_enabled_actions(): array {
		$actions = $this->get_value();
		$enabled = [];

		foreach ( $actions as $key => $action ) {
			if ( ! isset( $action['enabled'] ) || ! $action['enabled'] ) {
				continue;
			}

			$enabled[ $key ] = $action;
		}

		// Sort by order field (ascending).
		uasort(
			$enabled,
			function ( $a, $b ) {
				$order_a = $a['order'] ?? 0;
				$order_b = $b['order'] ?? 0;
				return $order_a - $order_b;
			}
		);

		return $enabled;
	}
}
