<?php

/**
 * Normalizes offer post meta into the flat rules shape consumed by the pricing engine, templates, and AJAX handlers.
 *
 * @link       https://nudgesells.com
 * @since      1.0.0
 *
 * @package    NudgeSells
 * @subpackage NudgeSells/includes
 */

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

/**
 * NudgeSells_Rules_Repository
 *
 * Reads and validates a offer's saved configuration into the consistent rules array used throughout the plugin.
 *
 * @since      1.0.0
 * @package    NudgeSells
 * @subpackage NudgeSells/includes
 * @author     NudgeSells <support@nudgesells.com>
 */
class NudgeSells_Rules_Repository
{

	/**
	 * Get rules.
	 *
	 * @param mixed $product_id
	 * @param int $variation_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_rules($product_id, $variation_id = 0)
	{
		$offer = NudgeSells_Offer_Repository::get_offer_for_product($product_id, $variation_id);

		if (! $offer) {
			return array(
				'enabled'         => false,
				'offer_id'     => 0,
				'display_style'   => 'list',
				'addons'          => array(),
				'addons_selection_type' => 'checkbox',
				'bundle_discount' => self::default_bundle(),
				'tiers'           => array(),
				'tiers_repeat_scale' => false,
				'tier_final_message' => '',
				'upgrades'        => array(),
				'upgrades_original_label'    => __('Buy as shown', 'nudgesells'),
				'upgrades_original_sublabel' => '',
				'condition_end'   => '',
			) + self::default_section_header('addons') + self::default_section_header('upgrades');
		}

		return self::apply_fallback_offers(self::get_rules_for_offer($offer->ID), $product_id);
	}

	/**
	 * Apply fallback offers.
	 *
	 * @param array $rules
	 * @param mixed $product_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	private static function apply_fallback_offers(array $rules, $product_id)
	{
		if (empty($rules['enabled'])) {
			return $rules;
		}

		$fallback_cross_sells = empty($rules['addons']) && 'yes' === NudgeSells_Admin_Settings::get('fallback_cross_sells', 'no');

		$fallback_up_sells = NudgeSells_Pro_Loader::is_active()
			&& empty($rules['upgrades'])
			&& 'yes' === NudgeSells_Admin_Settings::get('fallback_up_sells', 'no');

		if (! $fallback_cross_sells && ! $fallback_up_sells) {
			return $rules;
		}

		$product = wc_get_product($product_id);
		if (! $product) {
			return $rules;
		}

		if ($fallback_cross_sells) {
			$rules['addons'] = self::build_fallback_rules($product->get_cross_sell_ids(), $product_id, false);
		}
		if ($fallback_up_sells) {
			$rules['upgrades'] = self::build_fallback_rules($product->get_upsell_ids(), $product_id, true);
		}

		return $rules;
	}

	/**
	 * Build fallback rules.
	 *
	 * @param array $ids
	 * @param mixed $exclude_id
	 * @param mixed $is_upgrade
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	private static function build_fallback_rules(array $ids, $exclude_id, $is_upgrade)
	{
		$fallback_rules = array();
		foreach ($ids as $id) {
			$id = absint($id);
			if (! $id || $id === (int) $exclude_id) {
				continue;
			}
			$rule = array(
				'product_id'     => $id,
				'discount_type'  => 'percent',
				'discount_value' => 0,
				'label'          => '',
			);
			if ($is_upgrade) {
				$rule['sublabel'] = '';
			} else {
				$rule['subline'] = '';
			}
			$fallback_rules[] = $rule;
		}
		return $fallback_rules;
	}

	/**
	 * Get rules for offer.
	 *
	 * @param mixed $offer_id
	 *
	 * @return array
	 * @since 1.0.0
	 */
	public static function get_rules_for_offer($offer_id)
	{
		return array(
			'enabled'         => 'publish' === get_post_status($offer_id),
			'offer_id'     => $offer_id,
			'display_style'   => self::get_display_style($offer_id),
			'addons'          => self::get_addons($offer_id),
			'addons_selection_type' => in_array(get_post_meta($offer_id, '_nudgesells_addons_selection_type', true), array('checkbox', 'qty'), true) ? get_post_meta($offer_id, '_nudgesells_addons_selection_type', true) : 'checkbox',
			'bundle_discount' => self::get_bundle_discount($offer_id),
			'tiers'           => self::get_tiers($offer_id),
			'tiers_repeat_scale' => ! empty(get_post_meta($offer_id, '_nudgesells_tiers_repeat_scale', true)),
			'tier_final_message' => sanitize_text_field(get_post_meta($offer_id, '_nudgesells_tier_final_message', true)),
			'upgrades'        => self::get_upgrades($offer_id),
			'upgrades_original_label'    => self::get_upgrades_original_label($offer_id),
			'upgrades_original_sublabel' => self::get_upgrades_original_sublabel($offer_id),
			'condition_end'   => sanitize_text_field(get_post_meta($offer_id, '_nudgesells_condition_end', true)),
		)
			+ self::get_section_header($offer_id, 'addons')
			+ self::get_section_header($offer_id, 'upgrades');
	}

	/**
	 * Get section header.
	 *
	 * @param mixed $offer_id
	 * @param mixed $prefix
	 *
	 * @return array
	 * @since 1.0.0
	 */
	public static function get_section_header($offer_id, $prefix)
	{
		$title_default = 'upgrades' === $prefix
			? __('Prefer a better deal?', 'nudgesells')
			: __('Frequently added together', 'nudgesells');

		$title = get_post_meta($offer_id, "_nudgesells_{$prefix}_title", true);
		$icon_id = absint(get_post_meta($offer_id, "_nudgesells_{$prefix}_icon_id", true));

		return array(
			"{$prefix}_title"         => '' !== trim((string) $title) ? $title : $title_default,
			"{$prefix}_subline"       => sanitize_text_field(get_post_meta($offer_id, "_nudgesells_{$prefix}_subline", true)),
			"{$prefix}_icon_id"       => $icon_id,
			"{$prefix}_icon_url"      => $icon_id ? wp_get_attachment_image_url($icon_id, 'thumbnail') : '',
			"{$prefix}_readmore_text" => sanitize_text_field(get_post_meta($offer_id, "_nudgesells_{$prefix}_readmore_text", true)),
			"{$prefix}_readmore_url"  => esc_url_raw(get_post_meta($offer_id, "_nudgesells_{$prefix}_readmore_url", true)),
		);
	}

	/**
	 * Default section header.
	 *
	 * @param mixed $prefix
	 *
	 * @return array
	 * @since 1.0.0
	 */
	private static function default_section_header($prefix)
	{
		$title_default = 'upgrades' === $prefix
			? __('Prefer a better deal?', 'nudgesells')
			: __('Frequently added together', 'nudgesells');

		return array(
			"{$prefix}_title"         => $title_default,
			"{$prefix}_subline"       => '',
			"{$prefix}_icon_id"       => 0,
			"{$prefix}_icon_url"      => '',
			"{$prefix}_readmore_text" => '',
			"{$prefix}_readmore_url"  => '',
		);
	}

	/**
	 * Get upgrades original label.
	 *
	 * @param mixed $offer_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_upgrades_original_label($offer_id)
	{
		$label = get_post_meta($offer_id, '_nudgesells_upgrades_original_label', true);
		return '' !== trim((string) $label) ? $label : __('Buy as shown', 'nudgesells');
	}

	/**
	 * Get upgrades original sublabel.
	 *
	 * @param mixed $offer_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_upgrades_original_sublabel($offer_id)
	{
		return sanitize_text_field(get_post_meta($offer_id, '_nudgesells_upgrades_original_sublabel', true));
	}

	/**
	 * Get addons title.
	 *
	 * @param mixed $offer_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_addons_title($offer_id)
	{
		$title = get_post_meta($offer_id, '_nudgesells_addons_title', true);
		return '' !== trim((string) $title) ? $title : __('Frequently added together', 'nudgesells');
	}

	/**
	 * Get upgrades title.
	 *
	 * @param mixed $offer_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_upgrades_title($offer_id)
	{
		$title = get_post_meta($offer_id, '_nudgesells_upgrades_title', true);
		return '' !== trim((string) $title) ? $title : __('Prefer a better deal?', 'nudgesells');
	}

	/**
	 * Get display style.
	 *
	 * @param mixed $offer_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_display_style($offer_id)
	{
		$raw = self::get_raw_display_style($offer_id);
		if ('inherit' === $raw) {
			$default = NudgeSells_Admin_Settings::get('default_display', 'list');
			return in_array($default, array('list', 'card'), true) ? $default : 'list';
		}
		return $raw;
	}

	/**
	 * Get raw display style.
	 *
	 * @param mixed $offer_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_raw_display_style($offer_id)
	{
		$style = get_post_meta($offer_id, '_nudgesells_display_style', true);
		return in_array($style, array('list', 'card', 'inherit'), true) ? $style : 'inherit';
	}

	/**
	 * Get addons.
	 *
	 * @param mixed $offer_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_addons($offer_id)
	{
		$addons = get_post_meta($offer_id, '_nudgesells_addons', true);
		if (! is_array($addons)) {
			return array();
		}
		return array_values(array_filter(array_map(array(__CLASS__, 'normalize_addon'), $addons)));
	}

	/**
	 * Get bundle discount.
	 *
	 * @param mixed $offer_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_bundle_discount($offer_id)
	{
		$bundle = get_post_meta($offer_id, '_nudgesells_bundle_discount', true);
		if (! is_array($bundle)) {
			return self::default_bundle();
		}
		return array(
			'enabled' => ! empty($bundle['enabled']),
			'type'    => in_array($bundle['type'] ?? 'percent', array('percent', 'fixed', 'fixed_price', 'free_product'), true) ? $bundle['type'] : 'percent',
			'value'   => floatval($bundle['value'] ?? 0),
			'label'   => sanitize_text_field($bundle['label'] ?? __('Bundle discount', 'nudgesells')),
			'progress_label' => sanitize_text_field($bundle['progress_label'] ?? ''),
			'free_product_id' => absint($bundle['free_product_id'] ?? 0),
		);
	}

	/**
	 * Get tiers.
	 *
	 * @param mixed $offer_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_tiers($offer_id)
	{
		$tiers = get_post_meta($offer_id, '_nudgesells_tiers', true);
		if (! is_array($tiers)) {
			return array();
		}
		$tiers = array_values(array_filter(array_map(array(__CLASS__, 'normalize_tier'), $tiers)));
		usort($tiers, function ($a, $b) {
			return $a['min_qty'] <=> $b['min_qty'];
		});
		return $tiers;
	}

	/**
	 * Get upgrades.
	 *
	 * @param mixed $offer_id
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function get_upgrades($offer_id)
	{
		$upgrades = get_post_meta($offer_id, '_nudgesells_upgrades', true);
		if (! is_array($upgrades)) {
			return array();
		}
		return array_values(array_filter(array_map(array(__CLASS__, 'normalize_upgrade'), $upgrades)));
	}

	/**
	 * Default bundle.
	 *
	 * @return array
	 * @since 1.0.0
	 */
	private static function default_bundle()
	{
		return array(
			'enabled' => false,
			'type'    => 'percent',
			'value'   => 0,
			'label'   => __('Bundle discount', 'nudgesells'),
			'progress_label' => '',
			'free_product_id' => 0,
		);
	}

	/**
	 * Normalize addon.
	 *
	 * @param mixed $addon
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function normalize_addon($addon)
	{
		if (empty($addon['product_id'])) {
			return null;
		}
		return array(
			'product_id'      => absint($addon['product_id']),
			'discount_type'   => in_array($addon['discount_type'] ?? 'percent', array('percent', 'fixed', 'fixed_price'), true) ? $addon['discount_type'] : 'percent',
			'discount_value'  => floatval($addon['discount_value'] ?? 0),
			'label'           => sanitize_text_field($addon['label'] ?? ''),
			'subline'         => sanitize_text_field($addon['subline'] ?? ''),
			'allow_qty'       => absint($addon['allow_qty'] ?? 0),
		);
	}

	/**
	 * Normalize tier.
	 *
	 * @param mixed $tier
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function normalize_tier($tier)
	{
		if (! isset($tier['min_qty']) || '' === $tier['min_qty']) {
			return null;
		}

		if (isset($tier['reward_target']) || isset($tier['reward_shape'])) {
			$reward_target = $tier['reward_target'] ?? 'all_selected';
			$reward_shape  = $tier['reward_shape'] ?? 'percent';
		} else {
			$legacy_type = $tier['discount_type'] ?? 'percent';
			if ('free_gift' === $legacy_type) {
				$reward_target = 'specific_product';
				$reward_shape  = 'free_all';
			} else {
				$reward_target = 'all_selected';
				$reward_shape  = in_array($legacy_type, array('percent', 'fixed', 'fixed_price'), true) ? $legacy_type : 'percent';
			}
		}

		$reward_target = in_array($reward_target, array('all_selected', 'specific_product'), true) ? $reward_target : 'all_selected';
		$reward_shape  = in_array($reward_shape, array('percent', 'fixed', 'fixed_price', 'free_units', 'free_all'), true) ? $reward_shape : 'percent';

		return array(
			'min_qty'         => absint($tier['min_qty']),
			'reward_target'   => $reward_target,
			'reward_shape'    => $reward_shape,
			'discount_value'  => floatval($tier['discount_value'] ?? 0),
			'free_qty'        => absint($tier['free_qty'] ?? 0),
			'gift_product_id' => absint($tier['gift_product_id'] ?? 0),
			'label'           => sanitize_text_field($tier['label'] ?? ''),
		);
	}

	/**
	 * Normalize upgrade.
	 *
	 * @param mixed $upgrade
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public static function normalize_upgrade($upgrade)
	{
		if (empty($upgrade['product_id'])) {
			return null;
		}
		return array(
			'product_id'     => absint($upgrade['product_id']),
			'discount_type'  => in_array($upgrade['discount_type'] ?? 'percent', array('percent', 'fixed', 'fixed_price'), true) ? $upgrade['discount_type'] : 'percent',
			'discount_value' => floatval($upgrade['discount_value'] ?? 0),
			'label'          => sanitize_text_field($upgrade['label'] ?? ''),
			'sublabel'       => sanitize_text_field($upgrade['sublabel'] ?? ''),
		);
	}

	/**
	 * Sanitize rules payload.
	 *
	 * Whitelists and type-casts a raw decoded-JSON array (as submitted
	 * by the offer edit screen) into the exact shape the rest of the
	 * plugin expects, before it's stored or used anywhere. Deliberately
	 * reuses normalize_addon() / normalize_tier() / normalize_upgrade()
	 * rather than re-implementing field-by-field validation here a
	 * second time -- that schema is defined in exactly one place, so it
	 * can't quietly drift out of sync with the rest of the class.
	 *
	 * @param array $data Raw array from json_decode() of the submitted rules JSON.
	 *
	 * @return array
	 * @since 1.0.0
	 */
	public static function sanitize_rules_payload(array $data)
	{
		$addons = array_map(array(__CLASS__, 'normalize_addon'), is_array($data['addons'] ?? null) ? $data['addons'] : array());
		$addons = array_values(array_filter($addons));

		$tiers = array_map(array(__CLASS__, 'normalize_tier'), is_array($data['tiers'] ?? null) ? $data['tiers'] : array());
		$tiers = array_values(array_filter($tiers));

		$upgrades = array_map(array(__CLASS__, 'normalize_upgrade'), is_array($data['upgrades'] ?? null) ? $data['upgrades'] : array());
		$upgrades = array_values(array_filter($upgrades));

		$bundle_raw = is_array($data['bundle_discount'] ?? null) ? $data['bundle_discount'] : array();

		return array(
			'addons'                => $addons,
			'addons_selection_type' => in_array($data['addons_selection_type'] ?? '', array('checkbox', 'qty'), true) ? $data['addons_selection_type'] : 'checkbox',
			'bundle_discount'       => array(
				'enabled'         => ! empty($bundle_raw['enabled']),
				'type'            => in_array($bundle_raw['type'] ?? '', array('percent', 'fixed', 'fixed_price', 'free_product'), true) ? $bundle_raw['type'] : 'percent',
				'value'           => floatval($bundle_raw['value'] ?? 0),
				'label'           => sanitize_text_field($bundle_raw['label'] ?? ''),
				'progress_label'  => sanitize_text_field($bundle_raw['progress_label'] ?? ''),
				'free_product_id' => absint($bundle_raw['free_product_id'] ?? 0),
			),
			'tiers'                 => $tiers,
			'tiers_repeat_scale'    => ! empty($data['tiers_repeat_scale']),
			'upgrades'              => $upgrades,
		);
	}

	/**
	 * Save offer rules.
	 *
	 * @param mixed $offer_id
	 * @param array $data
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public static function save_offer_rules($offer_id, array $data)
	{
		$addons = array_map(array(__CLASS__, 'normalize_addon'), $data['addons'] ?? array());
		update_post_meta($offer_id, '_nudgesells_addons', array_values(array_filter($addons)));
		update_post_meta($offer_id, '_nudgesells_addons_selection_type', in_array($data['addons_selection_type'] ?? '', array('checkbox', 'qty'), true) ? $data['addons_selection_type'] : 'checkbox');

		update_post_meta($offer_id, '_nudgesells_bundle_discount', array(
			'enabled' => ! empty($data['bundle_discount']['enabled']),
			'type'    => in_array($data['bundle_discount']['type'] ?? '', array('percent', 'fixed', 'fixed_price', 'free_product'), true) ? $data['bundle_discount']['type'] : 'percent',
			'value'   => floatval($data['bundle_discount']['value'] ?? 0),
			'label'   => sanitize_text_field($data['bundle_discount']['label'] ?? ''),
			'progress_label' => sanitize_text_field($data['bundle_discount']['progress_label'] ?? ''),
			'free_product_id' => absint($data['bundle_discount']['free_product_id'] ?? 0),
		));

		$tiers = array_map(array(__CLASS__, 'normalize_tier'), $data['tiers'] ?? array());
		update_post_meta($offer_id, '_nudgesells_tiers', array_values(array_filter($tiers)));
		update_post_meta($offer_id, '_nudgesells_tiers_repeat_scale', ! empty($data['tiers_repeat_scale']) ? 'yes' : '');

		$upgrades = array_map(array(__CLASS__, 'normalize_upgrade'), $data['upgrades'] ?? array());
		update_post_meta($offer_id, '_nudgesells_upgrades', array_values(array_filter($upgrades)));
	}
}
