<?php
/**
 * The legacy class that manage the Exclude item rule
 *
 * @package YITH WooCommerce Dynamic Pricing & Discounts\Classes\PriceRules
 */

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

/**
 * Class that manage the exlusion rule
 */
class YWDPD_Exclude_Items extends YWDPD_Rule {

	/** Stores price rule data.
	 *
	 * @var array the common data
	 */
	protected $data = array(
		'key'                                => '',
		'name'                               => '',
		'active'                             => 'yes',
		'priority'                           => 1,
		'schedule_discount_mode'             => array(
			'schedule_type' => 'no_schedule',
		),
		'discount_mode'                      => 'exclude_items',
		'no_apply_with_other_rules'          => 'yes',
		'user_rules'                         => 'everyone',
		'user_rules_customers_list'          => array(),
		'user_rules_role_list'               => array(),
		'enable_user_rule_exclude'           => 'no',
		'user_rule_exclude'                  => 'specific_customers',
		'user_rules_customers_list_excluded' => array(),
		'user_rules_role_list_excluded'      => array(),
		'rule_for'                           => 'all_products',
		'rule_for_products_list'             => array(),
		'rule_for_categories_list'           => array(),
		'rule_for_tags_list'                 => array(),
		'active_exclude'                     => 'no',
		'exclude_rule_for'                   => 'specific_products',
		'exclude_rule_for_products_list'     => array(),
		'exclude_rule_for_categories_list'   => array(),
		'exclude_rule_for_tags_list'         => array(),
	);

	/**
	 * Get the rule if the ID is passed, otherwise the rule is new and empty.
	 *
	 * @param int|YWDPD_Exclude_Items|object $obj ID to load from the DB (optional) or already queried data.
	 * @throws Exception The exception.
	 */
	public function __construct( $obj = 0 ) {

		parent::__construct( $obj );

		$this->read();
	}

	/**
	 * Set if apply rule with other rules.
	 *
	 * @param string $no_apply_with_other_rules Set if apply rule with other rules.
	 *
	 * @since 3.0.0
	 */
	public function set_no_apply_with_other_rules( $no_apply_with_other_rules ) {
		$this->set_prop( 'no_apply_with_other_rules', $no_apply_with_other_rules );
	}

	/**
	 * Set the user exclusion list that can't get the rule discount.
	 *
	 * @param array $user_rules_customers_list_excluded The customer ids.
	 *
	 * @since 3.0.0
	 */
	public function set_user_rules_customers_list_excluded( $user_rules_customers_list_excluded ) {
		$this->set_prop( 'user_rules_customers_list_excluded', $user_rules_customers_list_excluded );
	}

	/**
	 * Set the user role exclude list that can't get the rule discount.
	 *
	 * @param array $user_rules_role_list_excluded The role slug list.
	 *
	 * @since 3.0.0
	 */
	public function set_user_rules_role_list_excluded( $user_rules_role_list_excluded ) {
		$this->set_prop( 'user_rules_role_list_excluded', $user_rules_role_list_excluded );
	}

	/**
	 * Set if exclude customers for this rule.
	 *
	 * @param string $enable_user_rule_exclude Can be yes or not.
	 *
	 * @since 3.0.0
	 */
	public function set_enable_user_rule_exclude( $enable_user_rule_exclude ) {
		$this->set_prop( 'enable_user_rule_exclude', $enable_user_rule_exclude );
	}

	/**
	 * Set the user exclusion type.
	 *
	 * @param string $user_rule_exclude The exclusion type.
	 *
	 * @since 3.0.0
	 */
	public function set_user_rule_exclude( $user_rule_exclude ) {
		$this->set_prop( 'user_rule_exclude', $user_rule_exclude );
	}

	/**
	 * Set the user list that can get the rule discount.
	 *
	 * @param array $user_rules_customers_list The customer ids.
	 *
	 * @since 3.0.0
	 */
	public function set_user_rules_customers_list( $user_rules_customers_list ) {
		$this->set_prop( 'user_rules_customers_list', $user_rules_customers_list );
	}

	/**
	 * Set the user role list that can't get the rule discount.
	 *
	 * @param array $user_rules_role_list The role slug list.
	 *
	 * @since 3.0.0
	 */
	public function set_user_rules_role_list( $user_rules_role_list ) {
		$this->set_prop( 'user_rules_role_list', $user_rules_role_list );
	}

	/**
	 * Set what are the user that  couldn't see the price rule.
	 *
	 * @param string $user_rules The type of the user.
	 *
	 * @since 3.0.0
	 */
	public function set_user_rules( $user_rules ) {
		$this->set_prop( 'user_rules', $user_rules );
	}

	/**
	 * Set the discount mode of this price rule.
	 *
	 * @param string $discount_mode The type of this price rule.
	 *
	 * @since 3.0.0
	 */
	public function set_discount_mode( $discount_mode ) {
		$this->set_prop( 'discount_mode', $discount_mode );
	}

	/**
	 * Set if the rule is valid for all products or not.
	 *
	 * @param string $rule_for Where the rule will be applied.
	 *
	 * @since 3.0.0
	 */
	public function set_rule_for( $rule_for ) {
		$this->set_prop( 'rule_for', $rule_for );
	}

	/**
	 * Set the product ids where apply the rule
	 *
	 * @param array $product_list The products ids.
	 *
	 * @since 3.0.0
	 */
	public function set_rule_for_product_list( $product_list ) {
		$this->set_prop( 'rule_for_products_list', $product_list );
	}

	/**
	 * Set the product category ids where apply the rule
	 *
	 * @param array $catgory_list The product category ids.
	 *
	 * @since 3.0.0
	 */
	public function set_rule_for_categories_list( $catgory_list ) {
		$this->set_prop( 'rule_for_categories_list', $catgory_list );
	}

	/**
	 * Set the product tag ids where apply the rule
	 *
	 * @param array $tag_list The tag ids.
	 *
	 * @since 3.0.0
	 */
	public function set_rule_for_tags_list( $tag_list ) {
		$this->set_prop( 'rule_for_tags_list', $tag_list );
	}

	/**
	 * Set if exclude product or not.
	 *
	 * @param string $exclude Yes or no.
	 *
	 * @since 3.0.0
	 */
	public function set_active_exclude( $exclude ) {
		$this->set_prop( 'active_exclude', $exclude );
	}

	/**
	 * Set what exclude from the rule.
	 *
	 * @param string $exclude_for The exclusion type.
	 *
	 * @since 3.0.0
	 */
	public function set_exclude_rule_for( $exclude_for ) {
		$this->set_prop( 'exclude_rule_for', $exclude_for );
	}

	/**
	 * Set the product ids to exclude.
	 *
	 * @param array $product_list the product ids list.
	 *
	 * @since 3.0.0
	 */
	public function set_exclude_rule_for_products_list( $product_list ) {
		$this->set_prop( 'exclude_rule_for_products_list', $product_list );
	}

	/**
	 * Set the product category ids to exclude.
	 *
	 * @param array $category_list the category ids list.
	 *
	 * @since 3.0.0
	 */
	public function set_exclude_rule_for_categories_list( $category_list ) {
		$this->set_prop( 'exclude_rule_for_categories_list', $category_list );
	}

	/**
	 * Set the product tag ids to exclude.
	 *
	 * @param array $tag_list the tag ids list.
	 *
	 * @since 3.0.0
	 */
	public function set_exclude_rule_for_tags_list( $tag_list ) {
		$this->set_prop( 'exclude_rule_for_tags_list', $tag_list );
	}

	// THE GETTERS.

	/**
	 * Get if apply rule with other rules.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_no_apply_with_other_rules( $context = 'view' ) {
		return $this->get_prop( 'no_apply_with_other_rules', $context );
	}

	/**
	 * Get the user exclusion list that can't get the rule discount.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_user_rules_customers_list_excluded( $context = 'view' ) {
		return $this->get_prop( 'user_rules_customers_list_excluded', $context );
	}

	/**
	 * Get the user role exclude list that can't get the rule discount.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_user_rules_role_list_excluded( $context = 'view' ) {
		return $this->get_prop( 'user_rules_role_list_excluded', $context );
	}

	/**
	 * Get if exclude customers for this rule.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_enable_user_rule_exclude( $context = 'view' ) {
		return $this->get_prop( 'enable_user_rule_exclude', $context );
	}

	/**
	 * Get the user exclusion type.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_user_rule_exclude( $context = 'view' ) {
		return $this->get_prop( 'user_rule_exclude', $context );
	}

	/**
	 * Get the user list that can get the rule discount.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_user_rules_customers_list( $context = 'view' ) {
		return $this->get_prop( 'user_rules_customers_list', $context );
	}

	/**
	 * Get the user role list that can get the rule discount.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_user_rules_role_list( $context = 'view' ) {
		return $this->get_prop( 'user_rules_role_list', $context );
	}

	/**
	 * Get what are the user that could see the price rule.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_user_rules( $context = 'view' ) {
		return $this->get_prop( 'user_rules', $context );
	}

	/**
	 * Get the discount mode of this price rule.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_discount_mode( $context = 'view' ) {
		return $this->get_prop( 'discount_mode', $context );
	}

	/**
	 * Return the rule type ( Price or Cart )
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_type() {
		return 'price_rule';
	}

	/**
	 * Get if the rule is valid for all products or not.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_rule_for( $context = 'view' ) {
		return $this->get_prop( 'rule_for', $context );
	}

	/**
	 * Get the product ids where apply the rule
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_rule_for_product_list( $context = 'view' ) {
		return $this->get_prop( 'rule_for_products_list', $context );
	}

	/**
	 * Get the product category ids where apply the rule
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_rule_for_categories_list( $context = 'view' ) {
		return $this->get_prop( 'rule_for_categories_list', $context );
	}

	/**
	 * Get the product tag ids where apply the rule
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_rule_for_tags_list( $context = 'view' ) {
		return $this->get_prop( 'rule_for_tags_list', $context );
	}

	/**
	 * Get if exclude product or not.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_active_exclude( $context = 'view' ) {
		return $this->get_prop( 'active_exclude', $context );
	}

	/**
	 * Get what exclude from the rule.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 * @since 3.0.0
	 */
	public function get_exclude_rule_for( $context = 'view' ) {
		return $this->get_prop( 'exclude_rule_for', $context );
	}

	/**
	 * Get the product ids to exclude.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @since 3.0.0
	 */
	public function get_exclude_rule_for_products_list( $context = 'view' ) {
		return $this->get_prop( 'exclude_rule_for_products_list', $context );
	}

	/**
	 * Get the product category ids to exclude.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_exclude_rule_for_categories_list( $context = 'view' ) {
		return $this->get_prop( 'exclude_rule_for_categories_list', $context );
	}

	/**
	 * Get the product tag ids to exclude.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return array
	 * @since 3.0.0
	 */
	public function get_exclude_rule_for_tags_list( $context = 'view' ) {
		return $this->get_prop( 'exclude_rule_for_tags_list', $context );
	}

	// Conditional methods.

	/**
	 * Check if the user is excluded.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_user_excluded( $context = 'view' ) {

		$is_exclude_user_active = yith_plugin_fw_is_true( $this->get_enable_user_rule_exclude( $context ) );
		$is_in_exclusion        = false;
		if ( $is_exclude_user_active ) {

			$user           = is_user_logged_in() ? wp_get_current_user() : false;
			$user_roles     = $user instanceof WP_User ? $user->roles : array( 'guest' );
			$exclusion_type = $this->get_user_rule_exclude();
			if ( 'specific_customers' === $exclusion_type && $user instanceof WP_User ) {
				$customer_ids_excluded = $this->get_user_rules_customers_list_excluded( $context );
				if ( in_array( intval( $user->ID ), array_map( 'intval', $customer_ids_excluded ), true ) ) {
					$is_in_exclusion = true;
				}
			} elseif ( 'specific_roles' === $exclusion_type ) {
				$customer_roles_excluded = $this->get_user_rules_role_list_excluded( $context );
				if ( count( array_intersect( $user_roles, $customer_roles_excluded ) ) > 0 ) {
					$is_in_exclusion = true;
				}
			}
		}
		if ( 'view' === $context ) {
			$is_in_exclusion = apply_filters( 'ywdpd_is_user_excluded', $is_in_exclusion, $this );

		}

		return $is_in_exclusion;
	}

	/**
	 * Check if the rule is valid for the current user.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_valid_for_user( $context = 'view' ) {
		$value = false;

		if ( ! $this->is_user_excluded( $context ) ) {

			$user_type  = $this->get_user_rules( $context );
			$user       = is_user_logged_in() ? wp_get_current_user() : false;
			$user_roles = $user instanceof WP_User ? $user->roles : array( 'guest' );

			if ( 'everyone' === $user_type ) {
				$value = true;
			} elseif ( 'customers_list' === $user_type && $user instanceof WP_User ) {
				$customer_ids = $this->get_user_rules_customers_list( $context );

				if ( in_array( $user->ID, $customer_ids, true ) ) {
					$value = true;
				}
			} elseif ( 'role_list' === $user_type ) {
				$customer_roles = $this->get_user_rules_role_list( $context );

				if ( count( array_intersect( $user_roles, $customer_roles ) ) > 0 ) {
					$value = true;
				}
			}
		}

		if ( 'view' === $context ) {
			$value = apply_filters( 'yit_ywdpd_validate_user', $value, $this->get_user_rules( $context ), $this );
			$value = apply_filters( 'ywdpd_is_valid_for_user', $value, $this );
		}

		return $value;
	}

	/**
	 * Check if this rule is Valid.
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_valid( $context = 'view' ) {

		return $this->is_enabled( $context ) && $this->is_valid_for_user( $context ) && $this->is_scheduled( $context );
	}

	/**
	 * Check if the rule is valid for apply discount.
	 *
	 * @param WC_Product $product The product object.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_valid_to_apply( $product ) {

		$type     = $this->get_rule_for();
		$is_valid = false;
		$list_ids = array();
		if ( $this->is_product_excluded_from_apply( $product ) ) {
			$is_valid = true;
		} else {
			if ( 'all_products' === $type ) {
				$is_valid = true;
			} elseif ( 'specific_products' === $type ) {
				$list_ids = $this->get_rule_for_product_list();
				$is_valid = $this->is_product_in_list( $product, $list_ids, 'product' );
			} elseif ( 'specific_categories' === $type ) {
				$list_ids = $this->get_rule_for_categories_list();
				$is_valid = $this->is_product_in_list( $product, $list_ids, 'product_cat' );
			} elseif ( 'specific_tag' === $type ) {
				$list_ids = $this->get_rule_for_tags_list();
				$is_valid = $this->is_product_in_list( $product, $list_ids, 'product_tag' );
			}
		}

		return apply_filters( 'ywdpd_is_valid_to_apply', $is_valid, $type, $product, $this );

	}

	/**
	 * Check if the product is in the list
	 *
	 * @param WC_Product $product The product to check.
	 * @param array      $list_ids The list of term ids.
	 * @param string     $taxonomy_to_check The taxonomy name.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_product_in_list( $product, $list_ids, $taxonomy_to_check = 'product_cat' ) {

		$is_in_list = false;
		if ( 'product' === $taxonomy_to_check ) {
			$ids_to_check = array( $product->get_id() );
			if ( 'variation' === $product->get_type() ) {
				$ids_to_check[] = $product->get_parent_id();
			}
			$is_in_list = count( array_intersect( $list_ids, $ids_to_check ) ) > 0;
		} elseif ( 'product_cat' === $taxonomy_to_check ) {
			$terms      = YWDPD_Taxonomy::get_product_category_ids( $product );
			$is_in_list = count( array_intersect( $list_ids, $terms ) ) > 0;
		} elseif ( 'product_tag' === $taxonomy_to_check ) {
			$terms      = YWDPD_Taxonomy::get_product_tag_ids( $product );
			$is_in_list = count( array_intersect( $list_ids, $terms ) ) > 0;
		} else {
			$terms      = YWDPD_Taxonomy::get_product_term_ids( $product, $taxonomy_to_check );
			$is_in_list = count( array_intersect( $list_ids, $terms ) ) > 0;
		}

		return apply_filters( 'ywdpd_is_product_in_list', $is_in_list, $product, $list_ids, $taxonomy_to_check, $this );
	}

	/**
	 * Check if the exclude option is enabled
	 *
	 * @param string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 * @author YITH <plugins@yithemes.com>
	 * @since 3.0.0
	 */
	public function is_exclude_from_apply_enabled( $context = 'view' ) {
		$is_exclude_activated = yith_plugin_fw_is_true( $this->get_active_exclude() );

		return $is_exclude_activated;
	}

	/**
	 * Check if this product is excluded from this rule.
	 *
	 * @param WC_Product $product The product to check.
	 *
	 * @return bool
	 * @since 3.0.0
	 */
	public function is_product_excluded_from_apply( $product ) {

		$is_excluded = false;
		$type        = $this->get_exclude_rule_for();
		if ( $this->is_exclude_from_apply_enabled() ) {

			$list_ids      = array();
			$taxonomy_name = '';
			if ( 'specific_products' === $type ) {
				$list_ids      = $this->get_exclude_rule_for_products_list();
				$taxonomy_name = 'product';
			} elseif ( 'specific_categories' === $type ) {
				$list_ids      = $this->get_exclude_rule_for_categories_list();
				$taxonomy_name = 'product_cat';
			} elseif ( 'specific_tag' === $type ) {
				$list_ids      = $this->get_exclude_rule_for_tags_list();
				$taxonomy_name = 'product_tag';
			}

			if ( count( $list_ids ) > 0 && ! empty( $taxonomy_name ) ) {
				$is_excluded = $this->is_product_in_list( $product, $list_ids, $taxonomy_name );
			}
		}

		return apply_filters( 'ywdpd_is_product_excluded_from_apply', $is_excluded, $type, $product, $this );
	}
}
