<?php
/**
 * This file belongs to the YIT Framework.
 *
 * This source file is subject to the GNU GENERAL PUBLIC LICENSE (GPL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl-3.0.txt
 *
 * @package YITH\PreOrder\Includes
 * @author YITH <plugins@yithemes.com>
 */

if ( ! defined( 'ABSPATH' ) || ! defined( 'YITH_WCPO_VERSION' ) ) {
	exit; // Exit if accessed directly.
}

if ( ! function_exists( 'yith_ywpo_get_roles' ) ) {
	/**
	 * Return the roles of users
	 *
	 * @return array
	 */
	function yith_ywpo_get_roles() {
		global $wp_roles;
		$roles = array();

		foreach ( $wp_roles->get_names() as $key => $role ) {
			$roles[ $key ] = translate_user_role( $role );
		}

		return array_merge( array( 'all' => esc_html__( 'All', 'yith-pre-order-for-woocommerce' ) ), $roles );
	}
}

if ( ! function_exists( 'yith_ywpo_times_product_has_been_bought_by_customer' ) ) {
	/**
	 * Return how many times a product has been bought by the same customer.
	 *
	 * @param string     $customer_email The customer's email.
	 * @param string|int $product_id     The product's ID.
	 *
	 * @return int|bool
	 */
	function yith_ywpo_times_product_has_been_bought_by_customer( $customer_email, $product_id ) {
		global $wpdb;

		$customer_email = esc_sql( $customer_email );
		$product_id     = esc_sql( $product_id );
		$statuses       = apply_filters( 'ywpo_max_qty_order_statuses', "'wc-processing', 'wc-completed', 'wc-yith-pre-ordered'", $product_id, $customer_email );

		$query = "
			SELECT im.meta_key, im.meta_value, COUNT( * ) as count FROM $wpdb->posts AS p
			INNER JOIN $wpdb->postmeta AS pm ON p.ID = pm.post_id
			INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON p.ID = i.order_id
			INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id
			WHERE p.post_status IN ( $statuses )
			AND pm.meta_key IN ( '_billing_email' )
			AND pm.meta_value IN ( %s )
			AND im.meta_key IN ( '_product_id', '_variation_id' )
			AND im.meta_value = %d
			GROUP BY im.meta_value";

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
		$result = $wpdb->get_row( $wpdb->prepare( $query, $customer_email, $product_id ) );

		if ( is_object( $result ) && ! empty( $result->count ) ) {
			return (int) $result->count;
		}
		return false;
	}
}

if ( ! function_exists( 'ywpo_update_release_date_itemmeta_by_product' ) ) {
	/**
	 * Return how many times a product has been bought by the same customer.
	 *
	 * @param string|int $product_id       The WC_Product object or product ID.
	 * @param int        $old_release_date The previous release date in timestamp format.
	 * @param int        $new_release_date The new release date in timestamp format.
	 *
	 * @return array Emails of affected orders.
	 */
	function ywpo_update_release_date_itemmeta_by_product( $product_id, $old_release_date, $new_release_date ) {
		global $wpdb;

		$product = wc_get_product( $product_id );

		if ( $product->is_type( 'variation' ) ) {
			$order_items_query = "SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_variation_id' AND meta_value = %d";
		} else {
			$order_items_query = "SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_product_id' AND meta_value = %d";
		}

		$order_item_ids = $wpdb->get_col( $wpdb->prepare( $order_items_query, $product->get_id() ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared

		$emails = array();

		try {
			if ( $order_item_ids && is_array( $order_item_ids ) ) {
				foreach ( $order_item_ids as $order_item_id ) {
					$order = wc_get_order( wc_get_order_id_by_order_item_id( $order_item_id ) );

					if ( ! $order instanceof WC_Order ) {
						continue;
					}

					$invalid_statuses = apply_filters(
						'ywpo_update_release_date_invalid_order_statuses',
						array( 'pending', 'cancelled', 'refunded', 'failed', 'trash' ),
						$product_id,
						$order->get_id(),
						$order_item_id,
						$old_release_date,
						$new_release_date
					);

					if ( in_array( $order->get_status(), $invalid_statuses, true ) ) {
						continue;
					}
					$item              = new WC_Order_Item_Product( $order_item_id );
					$item_release_date = $item->get_meta( '_ywpo_item_for_sale_date' );

					if (
						'yes' === $item->get_meta( '_ywpo_item_preorder' ) &&
						'waiting' === $item->get_meta( '_ywpo_item_status' ) &&
						( ! empty( $item_release_date ) && $old_release_date === $item_release_date )
					) {
						wc_update_order_item_meta( $order_item_id, '_ywpo_item_for_sale_date', $new_release_date );
						$emails[] = $order->get_billing_email();
					}
				}
			}
		} catch ( Exception $e ) {
			new WP_Error( $e->getCode(), $e->getMessage() );
		}

		return apply_filters( 'ywpo_update_release_date_emails', array_unique( $emails ), $emails, $product_id, $old_release_date, $new_release_date );
	}
}

if ( ! function_exists( 'ywpo_is_upfront_cart' ) ) {
	/**
	 * Check if the order will be charged in upfront mode.
	 *
	 * @return bool
	 */
	function ywpo_is_upfront_cart() {
		return apply_filters( 'ywpo_is_upfront_cart', 'upfront' === ywpo_get_cart_charge_type() );
	}
}

if ( ! function_exists( 'ywpo_is_upon_release_cart' ) ) {
	/**
	 * Check if the order will be charged in upon release mode.
	 *
	 * @return bool
	 */
	function ywpo_is_upon_release_cart() {
		return apply_filters( 'ywpo_is_upon_release_cart', 'upon_release' === ywpo_get_cart_charge_type() );
	}
}

if ( ! function_exists( 'ywpo_is_pay_later_cart' ) ) {
	/**
	 * Check if the order will be charged in upon release mode through the Pay Later gateway.
	 *
	 * @return bool
	 */
	function ywpo_is_pay_later_cart() {
		return apply_filters( 'ywpo_is_pay_later_cart', 'pay_later' === ywpo_get_cart_charge_type() );
	}
}

if ( ! function_exists( 'ywpo_is_upfront_order' ) ) {
	/**
	 * Check if the order will be charged in upfront mode.
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 * @return bool
	 */
	function ywpo_is_upfront_order( $order ) {
		return apply_filters( 'ywpo_is_upfront_order', 'upfront' === ywpo_get_order_charge_type( $order ) );
	}
}

if ( ! function_exists( 'ywpo_is_upon_release_order' ) ) {
	/**
	 * Check if the order will be charged in upon release mode.
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 * @return bool
	 */
	function ywpo_is_upon_release_order( $order ) {
		return apply_filters( 'ywpo_is_upon_release_order', 'upon_release' === ywpo_get_order_charge_type( $order ) );
	}
}

if ( ! function_exists( 'ywpo_get_cart_charge_type' ) ) {
	/**
	 * Get the charge type (upfront or upon release) the pre-order will use.
	 *
	 * @return string
	 */
	function ywpo_get_cart_charge_type() {
		$mode = get_option( 'ywpo_charge_type', 'upfront' );

		if ( ! WC()->cart ) {
			return '';
		}

		foreach ( WC()->cart->get_cart() as $cart_item ) {
			$product = isset( $cart_item['data'] ) && $cart_item['data'] instanceof WC_Product ? $cart_item['data'] : false;
			if ( $product && YITH_Pre_Order_Utils::is_pre_order_active( $product ) ) {
				if ( 'yes' === YITH_Pre_Order_Utils::get_override_charge_type( $product ) ) {
					$mode = ! empty( YITH_Pre_Order_Utils::get_charge_type( $product ) ) ? YITH_Pre_Order_Utils::get_charge_type( $product ) : $mode;
					break;
				}
			}
		}
		return $mode;
	}
}

if ( ! function_exists( 'ywpo_check_pre_order_cart_errors' ) ) {
	/**
	 * Check if the charge type (upfront or upon release) in the current WC()->cart is mixed.
	 *
	 * @return array|bool
	 */
	function ywpo_check_pre_order_cart_errors() {
		$global_charge_type                            = get_option( 'ywpo_charge_type', 'upfront' );
		$prevent_mixing_regular_and_pre_order_products = 'yes' === get_option( 'yith_wcpo_mixing', 'no' );
		$one_pre_order_in_cart                         = 'yes' === get_option( 'yith_wcpo_one_pre_order_in_cart', 'no' );

		$errors = array();

		if ( ! WC()->cart ) {
			return false;
		}

		$type_to_check = $global_charge_type;

		$i = 0;

		$pre_order_count       = 0;
		$regular_product_count = 0;
		$upon_release_count    = 0;
		foreach ( WC()->cart->get_cart() as $cart_item ) {
			$product = isset( $cart_item['data'] ) && $cart_item['data'] instanceof WC_Product ? $cart_item['data'] : false;
			$type    = '';
			if ( $product && YITH_Pre_Order_Utils::is_pre_order_active( $product ) ) {
				$pre_order_count++;
				if ( 'yes' === YITH_Pre_Order_Utils::get_override_charge_type( $product ) ) {
					$override_charge_type = ! empty( YITH_Pre_Order_Utils::get_charge_type( $product ) ) ? YITH_Pre_Order_Utils::get_charge_type( $product ) : '';
					if ( 0 === $i ) {
						$type_to_check = $override_charge_type;
					}
					$type = $override_charge_type;
				} else {
					if ( 0 === $i ) {
						$type_to_check = $global_charge_type;
					}
					$type = $global_charge_type;
				}
				if ( 'upon_release' === $type ) {
					$upon_release_count++;
				}
			} else {
				if ( 0 === $i ) {
					$type_to_check = 'upfront';
				}
				$type = 'upfront';
				$regular_product_count++;
			}

			if ( $i > 0 ) {
				if ( $type && $type !== $type_to_check ) {
					$errors[] = 'charge_type_mixed';
				}
				if ( $upon_release_count > 1 ) {
					$errors[] = 'multiple_upon_release';
				}
				if ( $prevent_mixing_regular_and_pre_order_products && ! in_array( 'pre_order_mixed', $errors, true ) ) {
					if ( $pre_order_count > 0 && $regular_product_count > 0 ) {
						$errors[] = 'pre_order_mixed';
					}
				}
				if ( $one_pre_order_in_cart && ! in_array( 'more_than_pre_order', $errors, true ) ) {
					if ( $pre_order_count > 1 ) {
						$errors[] = 'more_than_pre_order';
					}
				}
			}
			$i++;
		}
		if ( ! empty( $errors ) ) {
			return $errors;
		}
		return false;
	}
}

if ( ! function_exists( 'ywpo_get_order_charge_type' ) ) {
	/**
	 * Get the order's charge type (upfront or upon release).
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 * @return string
	 */
	function ywpo_get_order_charge_type( $order ) {
		$order = wc_get_order( $order );
		return apply_filters( 'ywpo_get_order_charge_type', $order instanceof WC_Order ? $order->get_meta( '_ywpo_order_charge_type' ) : '', $order );
	}
}

if ( ! function_exists( 'ywpo_set_order_charge_type' ) ) {
	/**
	 * Set the order's charge type ('upfront' or 'upon_release').
	 *
	 * @param string              $charge_type The charge type.
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 */
	function ywpo_set_order_charge_type( $charge_type, $order ) {
		$order = wc_get_order( $order );
		if ( $order instanceof WC_Order ) {
			$order->update_meta_data( '_ywpo_order_charge_type', apply_filters( 'ywpo_set_order_charge_type', $charge_type, $order ) );
			$order->save();
		}
	}
}

if ( ! function_exists( 'ywpo_get_order_status_for_pre_order' ) ) {
	/**
	 * Get the value for the 'ywpo_order_status' option.
	 *
	 * @return string
	 */
	function ywpo_get_order_status_for_pre_order() {
		return apply_filters( 'ywpo_get_order_status_for_pre_order', get_option( 'ywpo_order_status', 'default' ) );
	}
}

if ( ! function_exists( 'ywpo_product_is_eligible_for_auto_pre_order' ) ) {
	/**
	 * Check if the product is eligible for the automatic pre-order mode.
	 *
	 * @param WC_Product $product The product WC_Product object.
	 * @return bool
	 */
	function ywpo_product_is_eligible_for_auto_pre_order( $product ) {
		$specific_outofstock_products   = get_option( 'ywpo_specific_outofstock_products', array() );
		$specific_outofstock_categories = get_option( 'ywpo_specific_outofstock_categories', array() );

		if ( 'variation' === $product->get_type() ) {
			$parent        = wc_get_product( $product->get_parent_id() );
            $product_is_in = in_array( (string) $parent->get_id(), $specific_outofstock_products, true );
            $categories    = $parent->get_category_ids();
		} else {
            $product_is_in = in_array( (string) $product->get_id(), $specific_outofstock_products, true );
            $categories    = $product->get_category_ids();
		}
		$cat_is_in = array_intersect( $categories, $specific_outofstock_categories );

		$auto_pre_order_enabled = 'yes' === get_option( 'yith_wcpo_enable_pre_order_auto_outofstock_notification', 'no' );
		$auto_pre_order_mode    = get_option( 'ywpo_automatic_pre_order_mode', 'all' );

		return $auto_pre_order_enabled && 'all' === $auto_pre_order_mode || $auto_pre_order_enabled && 'specific' === $auto_pre_order_mode && ( $product_is_in || $cat_is_in );
	}
}

if ( ! function_exists( 'ywpo_automatic_pre_order_check' ) ) {
	/**
	 * Enable the automatic pre-order mode for the products that are eligible.
	 *
	 * @param WC_Product $product The product WC_Product object.
	 * @return bool
	 */
	function ywpo_automatic_pre_order_check( $product ) {
		$return = false;

		$stock_status = $product->get_stock_status( 'edit' );

		if ( apply_filters( 'ywpo_automatic_pre_order_check_control_by_stock_quantity', false, $product, $stock_status ) ) {
			$availability = $product->get_stock_quantity();
			if ( $availability > 0 ) {
				return $return;
			}
		}

		if ( ! ( 'outofstock' === $stock_status || 'onbackorder' === $stock_status ) ) {
			return $return;
		}

		$auto_pre_order_condition = ywpo_product_is_eligible_for_auto_pre_order( $product );

		if ( $auto_pre_order_condition && 'manual' !== YITH_Pre_Order_Utils::get_override_pre_order_mode( $product ) ) {
			$return = true;
		} elseif ( ! $auto_pre_order_condition && 'auto' === YITH_Pre_Order_Utils::get_pre_order_mode( $product ) ) {
			$return = true;
		}
		return $return;
	}
}

if ( ! function_exists( 'ywpo_is_pay_later_order' ) ) {
	/**
	 * Check if the order has been placed through the Pay Later gateway.
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 * @return bool
	 */
	function ywpo_is_pay_later_order( $order ) {
		$order = wc_get_order( $order );
		return apply_filters( 'ywpo_is_pay_later_order', $order instanceof WC_Order && 'yes' === $order->get_meta( '_ywpo_pay_later' ), $order );
	}
}

if ( ! function_exists( 'ywpo_order_will_need_payment_token' ) ) {
	/**
	 * Check if the order will be charged upon release and no payment token exists yet.
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 * @return bool
	 */
	function ywpo_order_will_need_payment_token( $order ) {
		$order = wc_get_order( $order );
		if ( ywpo_order_has_payment_token( $order ) ) {
			return false;
		}

		// if the order is charged upon release and no payment token exists then it requires payment tokenization.
		return ( ywpo_is_upon_release_order( $order ) && ! ywpo_is_pay_later_order( $order ) );
	}
}

if ( ! function_exists( 'ywpo_order_has_payment_token' ) ) {
	/**
	 * Check if the order contains a payment token
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 * @return bool
	 */
	function ywpo_order_has_payment_token( $order ) {
		$order = wc_get_order( $order );
		return apply_filters( 'ywpo_order_has_payment_token', ! ! $order->get_meta( '_ywpo_has_payment_token' ), $order );
	}
}

if ( ! function_exists( 'ywpo_all_items_are_completed' ) ) {
	/**
	 * Check if there are no waiting pre-order items inside the order.
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 *
	 * @return bool
	 */
	function ywpo_all_items_are_completed( $order ) {
		$return = false;
		$order  = wc_get_order( $order );
		if ( ! $order instanceof WC_Order ) {
			return $return;
		}

		$pre_order_items = $order->get_meta( '_ywpo_pre_order_items' );
		if ( ! empty( $pre_order_items ) && is_array( $pre_order_items ) ) {
			$length          = count( $pre_order_items );
			$completed_count = 0;
			$need_update = false;
			foreach ( $pre_order_items as $item_id => $status ) {
				$item = $order->get_item( $item_id );
				if ( ! $item instanceof WC_Order_Item_Product ) {
					unset( $pre_order_items[$item_id] );
					$need_update = true;
					continue;
				}
				$item_status = $item->get_meta( '_ywpo_item_status' );
				if (
					( 'completed' === $item_status && 'completed' === $status ) ||
					( 'cancelled' === $item_status && 'cancelled' === $status )
				) {
					$completed_count++;
				}
			}
			if ( $need_update ) {
				$order->update_meta_data( '_ywpo_pre_order_items', $pre_order_items );
			}
			$return = $length === $completed_count;
		}
		return apply_filters( 'ywpo_all_items_are_completed', $return, $order );
	}
}

if ( ! function_exists( 'ywpo_all_items_are_cancelled' ) ) {
	/**
	 * Check if all pre-order items inside the order are cancelled.
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 *
	 * @return bool
	 */
	function ywpo_all_items_are_cancelled( $order ) {
		$return = false;
		$order  = wc_get_order( $order );
		if ( ! $order instanceof WC_Order ) {
			return $return;
		}

		$pre_order_items = $order->get_meta( '_ywpo_pre_order_items' );
		if ( ! empty( $pre_order_items ) && is_array( $pre_order_items ) ) {
			$length          = count( $pre_order_items );
			$cancelled_count = 0;
			$need_update = false;
			foreach ( $pre_order_items as $item_id => $status ) {
				$item = $order->get_item( $item_id );
				if ( ! $item instanceof WC_Order_Item_Product ) {
					unset( $pre_order_items[$item_id] );
					$need_update = true;
					continue;
				}
				$item_status = $item->get_meta( '_ywpo_item_status' );
				if ( 'cancelled' === $item_status && 'cancelled' === $status ) {
					$cancelled_count++;
				}
			}
			if ( $need_update ) {
				$order->update_meta_data( '_ywpo_pre_order_items', $pre_order_items );
			}
			$return = $length === $cancelled_count;
		}
		return apply_filters( 'ywpo_all_items_are_cancelled', $return, $order );
	}
}

if ( ! function_exists( 'ywpo_is_completed_pre_order' ) ) {
	/**
	 * Check if the Pre-Order is completed.
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 * @return bool
	 */
	function ywpo_is_completed_pre_order( $order ) {
		$order = wc_get_order( $order );
		return apply_filters(
			'ywpo_is_completed_pre_order',
			$order instanceof WC_Order && 'completed' === $order->get_meta( '_ywpo_status' ),
			$order
		);
	}
}

if ( ! function_exists( 'ywpo_mark_as_paid' ) ) {
	/**
	 * Set the Pre-Order as paid.
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 */
	function ywpo_mark_as_paid( $order ) {
		$order = wc_get_order( $order );
		if ( $order instanceof WC_Order ) {
			$order->update_meta_data( '_ywpo_pending_payment', apply_filters( 'ywpo_mark_as_paid', 'no', $order ) );
			$order->save();
		}
	}
}

if ( ! function_exists( 'ywpo_order_is_paid' ) ) {
	/**
	 * Check if the order is paid.
	 *
	 * @param WC_Order|string|int $order The WC_Order object or order ID.
	 * @return bool Returns true if there is any 'date_paid' in the order.
	 */
	function ywpo_order_is_paid( $order ) {
		$order = wc_get_order( $order );
		return apply_filters( 'ywpo_order_is_paid', $order instanceof WC_Order && ! ! $order->get_date_paid(), $order );
	}
}

if ( ! function_exists( 'ywpo_set_pre_order_waiting' ) ) {
	/**
	 * Set the Pre-Order as waiting.
	 *
	 * @param WC_Order $order The WC_Order object.
	 */
	function ywpo_set_pre_order_waiting( $order ) {
		ywpo_set_pre_order_status( $order, 'waiting' );
	}
}

if ( ! function_exists( 'ywpo_set_pre_order_completed' ) ) {
	/**
	 * Set the Pre-Order as completed.
	 *
	 * @param WC_Order $order The WC_Order object.
	 */
	function ywpo_set_pre_order_completed( $order ) {
		ywpo_set_pre_order_status( $order, 'completed' );
	}
}

if ( ! function_exists( 'ywpo_set_pre_order_cancelled' ) ) {
	/**
	 * Set the Pre-Order as cancelled.
	 *
	 * @param WC_Order $order The WC_Order object.
	 */
	function ywpo_set_pre_order_cancelled( $order ) {
		ywpo_set_pre_order_status( $order, 'cancelled' );
	}
}

if ( ! function_exists( 'ywpo_set_pre_order_status' ) ) {
	/**
	 * Set the Pre-Order status.
	 *
	 * @param WC_Order $order  The WC_Order object.
	 * @param string   $status The pre-order status to set. Valid values are 'waiting', 'completed', 'cancelled'.
	 */
	function ywpo_set_pre_order_status( $order, $status ) {
		$order = wc_get_order( $order );
		if ( $order instanceof WC_Order ) {
			$order->update_meta_data( '_ywpo_status', apply_filters( 'ywpo_set_pre_order_status', $status, $order ) );
			$order->save();
		}
	}
}

if ( ! function_exists( 'ywpo_get_integrated_gateways' ) ) {
	/**
	 * Get a list of the gateways that are supported for Upon Release pre-orders
	 *
	 * @return array The gateways array.
	 */
	function ywpo_get_integrated_gateways() {
		return apply_filters( 'ywpo_integrated_gateways', array( 'yith-stripe', 'yith-stripe-connect', 'yith-braintree-cc', 'stripe' ) );
	}
}

if ( ! function_exists( 'ywpo_exist_integrated_gateways' ) ) {
	/**
	 * Check if there is at least one integrated gateway plugin activated on the site currently.
	 *
	 * @return bool
	 */
	function ywpo_exist_integrated_gateways() {
		foreach ( ywpo_get_integrated_gateways() as $gateway ) {
			if ( in_array( $gateway, WC()->payment_gateways()->get_payment_gateway_ids(), true ) ) {
				return true;
			}
		}
		return false;
	}
}
