<?php
/**
 * Class WooCommerceProductAddOns
 *
 * @package WCPay\MultiCurrency\Compatibility
 */

namespace WCPay\MultiCurrency\Compatibility;

use WC_Product;
use WCPay\MultiCurrency\MultiCurrency;
use WCPay\MultiCurrency\Utils;

/**
 * Class that controls Multi Currency Compatibility with WooCommerce Product Add Ons Plugin.
 */
class WooCommerceProductAddOns extends BaseCompatibility {

	const ADDONS_CONVERTED_META_KEY = '_wcpay_multi_currency_addons_converted';

	/**
	 * Init the class.
	 *
	 * @return  void
	 */
	public function init() {
		// Add needed actions and filters if Product Add Ons is active.
		if ( class_exists( 'WC_Product_Addons' ) ) {
			if ( ! is_admin() && ! defined( 'DOING_CRON' ) ) {
				add_filter( 'woocommerce_product_addons_option_price_raw', [ $this, 'get_addons_price' ], 50, 2 );
				add_filter( 'woocommerce_product_addons_price_raw', [ $this, 'get_addons_price' ], 50, 2 );
				add_filter( 'woocommerce_product_addons_params', [ $this, 'product_addons_params' ], 50, 1 );
				add_filter( 'woocommerce_product_addons_get_item_data', [ $this, 'get_item_data' ], 50, 3 );
				add_filter( 'woocommerce_product_addons_update_product_price', [ $this, 'update_product_price' ], 50, 4 );
				add_filter( 'woocommerce_product_addons_order_line_item_meta', [ $this, 'order_line_item_meta' ], 50, 4 );
				add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'should_convert_product_price' ], 50, 2 );
			}

			if ( wp_doing_ajax() ) {
				add_filter( 'woocommerce_product_addons_ajax_get_product_price_including_tax', [ $this, 'get_product_calculation_price' ], 50, 3 );
				add_filter( 'woocommerce_product_addons_ajax_get_product_price_excluding_tax', [ $this, 'get_product_calculation_price' ], 50, 3 );
			}
		}
	}

	/**
	 * Checks to see if the product's price should be converted.
	 *
	 * @param bool   $return  Whether to convert the product's price or not. Default is true.
	 * @param object $product Product object to test.
	 *
	 * @return bool True if it should be converted.
	 */
	public function should_convert_product_price( bool $return, $product ): bool {
		// If it's already false, return it.
		if ( ! $return ) {
			return $return;
		}

		// Check for cart items to see if they have already been converted.
		if ( 1 === $product->get_meta( self::ADDONS_CONVERTED_META_KEY ) ) {
			return false;
		}

		return $return;
	}

	/**
	 * Converts the price of an addon from WooCommerce Products Add-on extension.
	 *
	 * @param mixed $price   The price to be filtered.
	 * @param array $type    The type of the addon.

	 * @return mixed         The price as a string or float.
	 */
	public function get_addons_price( $price, $type ) {
		if ( 'percentage_based' === $type['price_type'] ) {
			// If the addon is a percentage_based type $price is actually a percentage
			// and doesn't need any conversion.
			return $price;
		}

		return $this->multi_currency->get_price( $price, 'product' );
	}

	/**
	 * Fixes currency formatting issues in Product Add-Ons. PAO gets these values directly from the db options,
	 * so those values aren't filtered. Luckily, there's a filter.
	 *
	 * @param array $params Product Add-Ons global parameters.
	 *
	 * @return array Adjust parameters.
	 */
	public function product_addons_params( array $params ): array {
		$params['currency_format_num_decimals'] = wc_get_price_decimals();
		$params['currency_format_decimal_sep']  = wc_get_price_decimal_separator();
		$params['currency_format_thousand_sep'] = wc_get_price_thousand_separator();

		return $params;
	}

	/**
	 * Filters the cart item data meta so we can provide the proper name with converted add on price.
	 *
	 * @param array $addon_data The addon data we are filtering/replacing.
	 * @param array $addon      The addon being processed.
	 * @param array $cart_item  The cart item being processed.
	 *
	 * @return array
	 */
	public function get_item_data( $addon_data, $addon, $cart_item ): array {
		$price = isset( $cart_item['addons_price_before_calc'] ) ? $cart_item['addons_price_before_calc'] : $addon['price'];
		$value = $addon['value'];

		/*
		 * 'woocommerce_addons_add_cart_price_to_value'
		 *
		 * Use this filter to display the price next to each selected add-on option.
		 * By default, add-on prices show up only next to flat fee add-ons.
		 *
		 * @param boolean
		 */
		$add_price_to_value = apply_filters( 'woocommerce_addons_add_cart_price_to_value', false, $cart_item );

		if ( 0.0 === (float) $addon['price'] ) {
			$value .= '';
		} elseif ( 'percentage_based' === $addon['price_type'] && 0.0 === (float) $price ) {
			$value .= '';
		} elseif ( 'custom_price' === $addon['field_type'] && $addon['price'] ) {
			if ( class_exists( 'WC_Product_Addons_Helper' ) ) {
				$addon_price = wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon['price'], $cart_item['data'] ) );
				/* translators: %1$s custom addon price in cart */
				$value           .= sprintf( _x( ' (%1$s)', 'custom price addon price in cart', 'woocommerce-payments' ), $addon_price );
				$addon['display'] = $value;
			}
		} elseif ( 'flat_fee' === $addon['price_type'] && $addon['price'] ) {
			if ( class_exists( 'WC_Product_Addons_Helper' ) ) {
				$addon_price = $this->multi_currency->get_price( $addon['price'], 'product' );
				if ( 'input_multiplier' === $addon['field_type'] ) {
					// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
					$addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
				}
				$addon_price = wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon_price, $cart_item['data'] ) );
				/* translators: %1$s flat fee addon price in order */
				$value .= sprintf( _x( ' (+ %1$s)', 'flat fee addon price in cart', 'woocommerce-payments' ), $addon_price );
			}
		} elseif ( 'quantity_based' === $addon['price_type'] && $addon['price'] && $add_price_to_value ) {
			if ( class_exists( 'WC_Product_Addons_Helper' ) ) {
				$addon_price = $this->multi_currency->get_price( $addon['price'], 'product' );
				if ( 'input_multiplier' === $addon['field_type'] ) {
					// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
					$addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
				}
				$addon_price = wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon_price, $cart_item['data'] ) );
				/* translators: %1$s addon price in order */
				$value .= sprintf( _x( ' (%1$s)', 'quantity based addon price in cart', 'woocommerce-payments' ), $addon_price );
			}
		} elseif ( 'percentage_based' === $addon['price_type'] && $addon['price'] && $add_price_to_value ) {
			// Get the percentage cost in the currency in use, and set the meta data on the product that the value was converted.
			$_product = wc_get_product( $cart_item['product_id'] );
			$price    = $this->multi_currency->get_price( $price, 'product' );
			$_product->set_price( $price * ( $addon['price'] / 100 ) );
			$_product->update_meta_data( self::ADDONS_CONVERTED_META_KEY, 1 );
			/* translators: %1$s addon price in order */
			$value .= sprintf( _x( ' (%1$s)', 'percentage based addon price in cart', 'woocommerce-payments' ), WC()->cart->get_product_price( $_product ) );
		}

		return [
			'name'    => $addon['name'],
			'value'   => $value,
			'display' => isset( $addon['display'] ) ? $addon['display'] : '',
		];
	}

	/**
	 * Updates the product price according to converted add on values.
	 *
	 * @param array $updated_prices Prices updated by Product Add-Ons (unused).
	 * @param array $cart_item      Cart item meta data.
	 * @param array $prices         Original prices passed to Product Add-Ons for calculations.
	 *
	 * @return array
	 */
	public function update_product_price( $updated_prices, $cart_item, $prices ): array {
		$price                       = $this->multi_currency->get_price( $prices['price'], 'product' );
		$regular_price               = $this->multi_currency->get_price( $prices['regular_price'], 'product' );
		$sale_price                  = $this->multi_currency->get_price( $prices['sale_price'], 'product' );
		$flat_fees                   = 0;
		$quantity                    = $cart_item['quantity'];
		$price_before_addons         = $price;
		$regular_price_before_addons = $regular_price;
		$sale_price_before_addons    = $sale_price;

		// TODO: Check compat with Smart Coupons.
		// Compatibility with Smart Coupons self declared gift amount purchase.
		$credit_called = ! empty( $_POST['credit_called'] ) ? $_POST['credit_called'] : null;  // phpcs:ignore
		if ( empty( $price ) && ! empty( $credit_called ) ) {
			// Variable $_POST['credit_called'] is an array.
			if ( isset( $credit_called[ $cart_item['data']->get_id() ] ) ) {
				$price         = (float) $credit_called[ $cart_item['data']->get_id() ];
				$regular_price = $price;
				$sale_price    = $price;
			}
		}

		if ( empty( $price ) && ! empty( $cart_item['credit_amount'] ) ) {
			$price         = (float) $cart_item['credit_amount'];
			$regular_price = $price;
			$sale_price    = $price;
		}

		foreach ( $cart_item['addons'] as $addon ) {
			// Percentage based and custom defined addon prices do not get converted, all others do.
			if ( 'percentage_based' === $addon['price_type'] || 'custom_price' === $addon['field_type'] ) {
				$addon_price = $addon['price'];
			} elseif ( 'input_multiplier' === $addon['field_type'] ) {
				// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
				$addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
			} else {
				$addon_price = $this->multi_currency->get_price( $addon['price'], 'product' );
			}

			switch ( $addon['price_type'] ) {
				case 'percentage_based':
					$price         += (float) ( $price_before_addons * ( $addon_price / 100 ) );
					$regular_price += (float) ( $regular_price_before_addons * ( $addon_price / 100 ) );
					$sale_price    += (float) ( $sale_price_before_addons * ( $addon_price / 100 ) );
					break;
				case 'flat_fee':
					$flat_fee       = $quantity > 0 ? (float) ( $addon_price / $quantity ) : 0;
					$price         += $flat_fee;
					$regular_price += $flat_fee;
					$sale_price    += $flat_fee;
					$flat_fees     += $flat_fee;
					break;
				default:
					$price         += (float) $addon_price;
					$regular_price += (float) $addon_price;
					$sale_price    += (float) $addon_price;
					break;
			}
		}

		// Let ourselves know this item has had add ons converted.
		$cart_item['data']->update_meta_data( self::ADDONS_CONVERTED_META_KEY, 1 );

		return [
			'price'                => $price,
			'regular_price'        => $regular_price,
			'sale_price'           => $sale_price,
			'addons_flat_fees_sum' => $flat_fees,
		];
	}

	/**
	 * Filters the meta data for order line items so that we can properly set values in the names.
	 *
	 * @param array                  $meta_data A key/value for the meta data to be inserted for the line item.
	 * @param array                  $addon     The addon being processed.
	 * @param \WC_Order_Item_Product $item      Order item data.
	 * @param array                  $values    Order item values.
	 *
	 * @return array A key/value for the meta data to be inserted for the line item.
	 */
	public function order_line_item_meta( array $meta_data, array $addon, \WC_Order_Item_Product $item, array $values ): array {

		$add_price_to_value = apply_filters( 'woocommerce_addons_add_order_price_to_value', false, $item );

		$value = $addon['value'];

		// Pass the timestamp as the add-on value in order to save the timestamp to the DB.
		if ( isset( $addon['timestamp'] ) ) {
			$value = $addon['timestamp'];
		}

		// If there is an add-on price, add the price of the add-on to the label name.
		if ( $addon['price'] && $add_price_to_value ) {
			$product = $item->get_product();

			if ( 'percentage_based' === $addon['price_type'] && 0.0 !== (float) $product->get_price() ) {
				// Calculate the percentage price.
				$addon_price = $product->get_price() * ( $addon['price'] / 100 );
			} elseif ( 'custom_price' === $addon['field_type'] ) {
				// Custom prices do not get converted.
				$addon_price = $addon['price'];
			} elseif ( 'input_multiplier' === $addon['field_type'] ) {
				// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
				$addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
			} else {
				// Convert all others.
				$addon_price = $this->multi_currency->get_price( $addon['price'], 'product' );
			}
			if ( class_exists( 'WC_Product_Addons_Helper' ) ) {
				$price = html_entity_decode(
					wp_strip_all_tags( wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon_price, $values['data'] ) ) ),
					ENT_QUOTES,
					get_bloginfo( 'charset' )
				);
			}

			if ( 'flat_fee' === $addon['price_type'] && $addon['price'] && $add_price_to_value ) {
				/* translators: %1$s flat fee addon price in order */
				$value .= sprintf( _x( ' (+ %1$s)', 'flat fee addon price in order', 'woocommerce-payments' ), $price );
			} elseif ( ( 'quantity_based' === $addon['price_type'] || 'percentage_based' === $addon['price_type'] ) && $addon['price'] && $add_price_to_value ) {
				/* translators: %1$s addon price in order */
				$value .= sprintf( _x( ' (%1$s)', 'addon price in order', 'woocommerce-payments' ), $price );
			} elseif ( 'custom_price' === $addon['field_type'] ) {
				/* translators: %1$s custom addon price in order */
				$value = sprintf( _x( ' (%1$s)', 'custom addon price in order', 'woocommerce-payments' ), $price );
			}

			$meta_data['raw_price'] = $this->multi_currency->get_price( $addon['price'], 'product' );
		}

		$meta_data['value'] = $value;
		return $meta_data;
	}

	/**
	 * Gets the product price during ajax requests from the product page.
	 *
	 * @param float       $price    Price to get converted.
	 * @param int         $quantity Quantity of the product selected.
	 * @param \WC_Product $product  WC_Product related to the price.
	 *
	 * @return float Adjusted price.
	 */
	public function get_product_calculation_price( float $price, int $quantity, \WC_Product $product ): float {
		return $this->multi_currency->get_price( $price / $quantity, 'product' ) * $quantity;
	}
}
