<?php

namespace Prodigy\Includes\Content;

use Prodigy\includes\helpers\Prodigy_Elementor;
use Prodigy\includes\helpers\Prodigy_Includes_Helper;
use Prodigy\Includes\Helpers\Prodigy_Platform_Settings;
use Prodigy\includes\models\Prodigy_Product_Attributes;
use Prodigy\Includes\Prodigy;
use SplMaxHeap;
use SplMinHeap;

defined( 'ABSPATH' ) || exit;

/**
 * Class RemoteProduct
 *
 * @version 2.0.0
 */
class Prodigy_Product_Parser extends Prodigy_Main_Content {

	const REMOTE_NAME_CATEGORIES                     = 'categories';
	const REMOTE_NAME_TAGS                           = 'tags';
	const REMOTE_NAME_VARIANTS                       = 'variants';
	const REMOTE_NAME_MASTER_VARIANT                 = 'master-variant';
	const REMOTE_NAME_DESCRIPTIVE_ATTRIBUTES         = 'descriptive-attributes';
	const REMOTE_NAME_VARIANT_ATTRIBUTES             = 'variant-attributes';
	const REMOTE_NAME_INCLUDE_DESCRIPTIVE_ATTRIBUTES = 'properties';
	const REMOTE_NAME_IMAGES                         = 'images';
	const SUBSCRIPTION_PLANS                         = 'subscription-plans';
	const QUANTITY_PRICE_BREAKS                      = 'quantity-price-breaks';

	/**
	 * Product personalization type
	 */
	const PERSONALIZATION_TYPE_FLAT = 'flat';

	/**
	 * Product personalization type
	 */
	const PERSONALIZATION_TYPE_PERCENT = 'percentage';

	/** @var string[] */
	public static array $personalization_type_mapper = array(
		self::PERSONALIZATION_TYPE_FLAT    => '$',
		self::PERSONALIZATION_TYPE_PERCENT => '%',
	);

	/**
	 * Response relation logo-options type of data
	 */
	const LOGO_OPTIONS = 'logo-options';

	/**
	 * Response relation logo type of data
	 */
	const LOGO = 'logos';

	/**
	 * Response relation logo-locations type of data
	 */
	const LOGO_LOCATIONS = 'logo-locations';

	/**
	 * Response relation multiple-logo-charge-settings type of data
	 */
	const MULTIPLE_LOGO_CHARGE_SETTINGS = 'multiple-logo-charge-settings';

	/**
	 * Response relation option type of data
	 */
	const OPTIONS = 'options';

	/**
	 * Response relation option type of data
	 */
	const PERSONALIZATIONS = 'personalizations';

	/**
	 * Response relation option type of data
	 */
	const PERSONALIZATION_FIELDS = 'personalization-fields';


	const MAX_RANGE_LIMIT = 99999;

	/**
	 * @var array $product
	 */
	private array $product;

	/**
	 * Include by types
	 *
	 * @var array
	 */
	private array $parseInclude = array();

	/** @var array  */
	private array $included_by_type = array();

	/** @var array  */
	public array $filter_variants;

	/**
	 * @param array $product
	 */
	public function __construct( array $product ) {
		parent::__construct();
		$this->product          = $product;
		$this->included_by_type = Prodigy_Includes_Helper::prepare_included_by_type( $product['included'] ?? array() );
	}

	/**
	 * @return string
	 */
	public function get_title(): string {
		$title = '';
		if ( isset( $this->product['data'] ) ) {
			$title = $this->product['data']['attributes']['name'];
		}

		return $title;
	}


	/**
	 * @return string
	 */
	public function get_display_options_type(): string {
		$type = '';
		if ( isset( $this->product['data']['attributes'] ) ) {
			$type = $this->product['data']['attributes']['display-options-type'];
		}

		return $type;
	}

	/**
	 * @return string
	 */
	public function get_description(): string {
		$description = '';
		if ( isset( $this->product['data'] ) ) {
			$description = $this->product['data']['attributes']['description'];
		}

		return $description;
	}

	/**
	 * @return array
	 */
	public function get_categories(): array {
		$categories        = array();
		$filter_categories = $this->included_by_type[ self::REMOTE_NAME_CATEGORIES ] ?? array();

		if ( ! empty( $filter_categories ) ) {
			foreach ( $filter_categories as $remote_category ) {
				if ( isset( $remote_category['id'] ) ) {
					$local_category = Prodigy_Product_Attributes::get_term_id_by_meta_key( Prodigy::PRODIGY_HOSTED_CATEGORY_RELATION, (int) $remote_category['id'] );
					$categories[]   = $local_category;
				}
			}
		}

		return $categories;
	}

	/**
	 * @return array
	 */
	public function get_tags(): array {
		$tags        = array();
		$filter_tags = $this->included_by_type[ self::REMOTE_NAME_TAGS ] ?? array();

		if ( ! empty( $filter_tags ) ) {
			foreach ( $filter_tags as $remote_tag ) {
				$tags[] = $remote_tag['attributes']['name'];
			}
		}

		return $tags;
	}

	/**
	 * @return string
	 */
	public function get_main_sku(): string {
		$id_master_variant = '';
		$sku               = '';

		if ( isset( $this->product['data']['relationships'] ) ) {
			foreach ( $this->product['data']['relationships'] as $key => $relationship ) {
				if ( $key === self::REMOTE_NAME_MASTER_VARIANT ) {
					$id_master_variant = $relationship['data']['id'];
				}
			}
		}

		$info_include = $this->parse_included_by_id( (int) $id_master_variant, self::REMOTE_NAME_VARIANTS );

		if ( ! empty( $info_include['attributes']['sku'] ) ) {
			$sku = $info_include['attributes']['sku'];
		}

		return $sku;
	}

	/**
	 * Get the charge amount
	 *
	 * @return string
	 */
	public function get_charge_amount(): string {
		return $this->product['data']['attributes']['setup-charge-amount'] ?? '';
	}

	/**
	 *
	 * @return array
	 */
	public function get_main_price(): array {
		$data          = array();
		$data['price'] = '';
		if ( ! empty( $this->product['data']['attributes']['price'] ) ) {
			$data['price'] = $this->product['data']['attributes']['price'];
		}

		$data['sale-price'] = '';
		if ( ! empty( $this->product['data']['attributes']['sale-price'] ) ) {
			$data['sale-price'] = $this->product['data']['attributes']['sale-price'];
		}

		if ( ! empty( $this->product['data']['attributes']['price-range']['min_price'] ) ) {
			$data['range']['min_price'] = $this->product['data']['attributes']['price-range']['min_price'];
		}

		if ( ! empty( $this->product['data']['attributes']['price-range']['max_price'] ) ) {
			$data['range']['max_price'] = $this->product['data']['attributes']['price-range']['max_price'];
		}

		return $data;
	}

	/**
	 * @return array|mixed
	 */
	public function get_range_current_price(): array {
		if ( empty( $this->get_variants() ) ) {
			return $this->get_main_price();
		}

		$obj_min = new SplMinHeap();
		$obj_max = new SplMaxHeap();
		foreach ( $this->get_variants() as $variants ) {
			foreach ( $variants as $variant ) {
				$obj_max->insert( $variant['attributes']['attributes']['price'] );
				$obj_min->insert( $variant['attributes']['attributes']['sale-price'] );
			}
		}

		$data['sale-price'] = $obj_min->top() ?? '';
		$data['price']      = $obj_max->top() ?? '';

		return $data;
	}

	/**
	 * @return array
	 */
	public function get_personalizations_fields(): array {
		$personalization_fields      = array();
		$personalization_ids         = array();
		$relation_of_personalization = $this->included_by_type[ self::PERSONALIZATIONS ] ?? array();

		if ( isset( $relation_of_personalization, $relation_of_personalization[ key( $relation_of_personalization ) ] ) ) {
			foreach ( $relation_of_personalization[ key( $relation_of_personalization ) ]['relationships']['personalization-fields']['data'] as $key => $relationship ) {
				if ( $relationship['type'] === self::PERSONALIZATION_FIELDS ) {
					$personalization_ids[ $key ] = $relationship['id'];
				}
			}
		}

		foreach ( $personalization_ids as $id ) {
			$personalization_fields['personalization_id'] = $relation_of_personalization[ key( $relation_of_personalization ) ]['id'];
			$personalization_fields[ $id ]                = $this->parse_included_by_id( (int) $id, self::PERSONALIZATION_FIELDS );
			$personalization_fields['title']              = $relation_of_personalization[ key( $relation_of_personalization ) ]['attributes']['title'];
			$personalization_fields['modifier-type']      = $relation_of_personalization[ key( $relation_of_personalization ) ]['attributes']['price-modifier-type'];
			$personalization_fields['modifier-value']     = $relation_of_personalization[ key( $relation_of_personalization ) ]['attributes']['price-modifier-value'];
		}

		return $personalization_fields;
	}


	/**
	 * @return array
	 */
	public function get_tiered_price_range(): array {
		$data = array();
		if ( ! empty( $this->get_variants() ) ) {
			$price = array();
			foreach ( $this->get_variants() as $variants ) {
				foreach ( $variants as $key => $variant ) {
					$price[ $key ] = $variant['attributes']['attributes']['price-quantity-modifier'];
				}
			}
		} else {
			$master = $this->get_master_variant();
			$price  = $master['attributes']['price-quantity-modifier'] ?? array();
		}

		$price                = empty( $price ) ? array( 0 ) : $price;
		$tiered_prices        = $this->get_tired_prices();
		$data['min_price']    = min( (array) $price ) + $this->get_flat_tiered_price( $tiered_prices );
		$data['max_price']    = max( (array) $price ) + $this->get_flat_tiered_price( $tiered_prices );
		$data['min_quantity'] = $this->get_min_tiered_quantity( $tiered_prices );

		return $data;
	}


	/**
	 * @param array $variant
	 * @param int   $items_number
	 *
	 * @return float
	 */
	public function calculate_tiered_price( array $variant, int $items_number ): float {
		$tiered_prices_ranges = $this->get_tired_prices();

		$price = 0;
		if ( isset( $variant['attributes'] ) && ! empty( $tiered_prices_ranges ) ) {
			foreach ( $tiered_prices_ranges as $range ) {
				if ( ( $range['attributes']['min-quantity'] <= $items_number ) && ( $items_number <= $range['attributes']['max-quantity'] ) && isset( $range['attributes'], $variant['attributes'] ) ) {
					$price = $range['attributes']['flat-price'] + $variant['attributes']['price-quantity-modifier'];
				}
			}

			if ( $items_number >= $tiered_prices_ranges[ count( $tiered_prices_ranges ) - 1 ]['attributes']['max-quantity'] ) {
				$price = $tiered_prices_ranges[ count( $tiered_prices_ranges ) - 1 ]['attributes']['flat-price'] + $variant['attributes']['price-quantity-modifier'];
			} elseif ( isset( $tiered_prices_ranges[0]['attributes'] ) && $items_number <= $tiered_prices_ranges[0]['attributes']['min-quantity'] ) {
				$price = $tiered_prices_ranges[0]['attributes']['flat-price'] + $variant['attributes']['price-quantity-modifier'];
			}
		}

		return $price;
	}

	/**
	 * @param array $tiered_prices
	 *
	 * @return float
	 */
	public function get_min_tiered_quantity( array $tiered_prices ): float {
		return $tiered_prices[0]['attributes']['min-quantity'] ?? 0;
	}

	/**
	 * @param array $tiered_prices
	 *
	 * @return float
	 */
	public function get_flat_tiered_price( array $tiered_prices ): float {
		return $tiered_prices[0]['attributes']['flat-price'] ?? 0;
	}

	/**
	 * @return false|int
	 */
	public function get_id_master_variant() {
		$id_master_variant = false;
		if ( isset( $this->product['data']['relationships'] ) && ! empty( $this->product['data']['relationships'] ) ) {
			foreach ( $this->product['data']['relationships'] as $key => $relationship ) {
				if ( $key === self::REMOTE_NAME_MASTER_VARIANT ) {
					$id_master_variant = $relationship['data']['id'];
				}
			}
		}

		return $id_master_variant;
	}

	/**
	 * @return array
	 */
	public function get_master_variant_logos(): array {
		return $this->get_logos_by_option();
	}


	/**
	 * @return array
	 */
	public function get_master_variant(): array {
		return $this->parse_included_by_id( (int) $this->get_id_master_variant(), self::REMOTE_NAME_VARIANTS );
	}

	/**
	 * @return array
	 */
	public function get_prepared_variants(): array {
		$variants            = array();
		$default_logo_option = null;

		$this->filter_variants = $this->included_by_type[ self::REMOTE_NAME_VARIANTS ] ?? array();

		if ( isset( $this->product['data']['attributes']['default-logo-option-id'] ) ) {
			$default_logo_options_info = $this->parse_logos_options_by_type_and_relation_id( $this->product['data']['attributes']['default-logo-option-id'] );
			if ( isset(
				$this->product['data']['attributes']['default-logo-option-id'],
				$default_logo_options_info[ $this->product['data']['attributes']['default-logo-option-id'] ]['option_id']
			) ) {
				$default_logo_option_id = $default_logo_options_info[ $this->product['data']['attributes']['default-logo-option-id'] ]['option_id'];
				$default_logo_option    = $this->parse_included_by_id( (int) $default_logo_option_id, self::OPTIONS );
			}
		}

		$options = $this->get_attributes_options();

		if ( ! empty( $this->filter_variants ) && is_array( $this->filter_variants ) ) {
			foreach ( $this->filter_variants as $key => $remote_variant ) {
				if ( ! $remote_variant['attributes']['master'] ) {
					foreach ( $remote_variant['attributes']['options'] as $item ) {
						if ( isset( $item['option_slug'] ) && ! empty( $options[ $item['property_slug'] ][ $item['option_slug'] ] ) ) {
							$variants[ $key ][ $item['property_slug'] ][ $item['option_slug'] ]['attribute'] = $item['property_slug'];
							$variants[ $key ][ $item['property_slug'] ][ $item['option_slug'] ]['name']      = $item['value'];
							$variants[ $key ][ $item['property_slug'] ][ $item['option_slug'] ]['slug']      = $item['option_slug'];
							$variants[ $key ][ $item['property_slug'] ][ $item['option_slug'] ]['option_id'] = $options[ $item['property_slug'] ][ $item['option_slug'] ]['option_id'] ?? 0;

							if ( isset( $options[ $item['property_slug'] ][ $item['option_slug'] ]['color'] ) ) {
								$variants[ $key ][ $item['property_slug'] ][ $item['option_slug'] ]['color'] = $options[ $item['property_slug'] ][ $item['option_slug'] ]['color'];
							}
							if ( isset( $options[ $item['property_slug'] ][ $item['option_slug'] ]['image'] ) ) {
								$variants[ $key ][ $item['property_slug'] ][ $item['option_slug'] ]['image'] = $options[ $item['property_slug'] ][ $item['option_slug'] ]['image'];
							}

							if ( isset( $default_logo_option_id, $default_logo_option ) ) {
								$variants[ $key ][ $item['property_slug'] ][ $item['option_slug'] ]['default'] = $default_logo_option['attributes']['slug'] === $item['option_slug'];
							}

							if ( isset( $options[ $item['property_slug'] ][ $item['option_slug'] ]['logos'] ) ) {
								$variants[ $key ][ $item['property_slug'] ][ $item['option_slug'] ]['logos'] = $options[ $item['property_slug'] ][ $item['option_slug'] ]['logos'];
							}
						}
					}
				}
			}
		}

		foreach ( $variants as $key => $variant ) {
			foreach ( $variant as $k => $item ) {
				if ( isset( $k, $key, $variants[ $key ][ $k ][ key( $item ) ], $options[ $k ][ key( $item ) ] ) ) {
					$variants[ $key ][ $k ][ key( $item ) ]['sort'] = $options[ $k ][ key( $item ) ]['sort'];
				}
			}
		}

		return $variants;
	}


	/**
	 * @return array
	 */
	public function get_option_variants_intersect(): ?array {
		$option_intersect = array();
		$options          = $this->get_prepared_variants();
		foreach ( $options as $key => $color ) {
			foreach ( $color as $attr => $item ) {
				$slug                              = key( $item );
				$option_intersect[ $key ][ $attr ] = $slug;
			}
		}

		return $option_intersect;
	}

	/**
	 * @return array
	 */
	public function get_variants(): array {
		$variants              = array();
		$this->filter_variants = $this->included_by_type[ self::REMOTE_NAME_VARIANTS ] ?? array();

		if ( ! empty( $this->filter_variants ) ) {
			foreach ( $this->filter_variants as $remote_variant ) {
				if ( is_array( $remote_variant['attributes']['options'] ) && ! $remote_variant['attributes']['master'] ) {
					foreach ( $remote_variant['attributes']['options'] as $option ) {

						if ( ! isset( $variants[ $option['name'] ] ) ) {
							$variants[ $option['name'] ] = array();
						}

						if ( isset( $option['value'], $option['name'] ) && ! in_array( $option['value'], $variants[ $option['name'] ], true ) ) {
							$variants[ $option['name'] ][ $option['value'] ]['name']       = $option['value'];
							$variants[ $option['name'] ][ $option['value'] ]['attributes'] = $remote_variant;
						}
					}
				}
			}
		}

		return $variants;
	}

	/**
	 * @param array $params
	 *
	 * @return false | int
	 * @example ['Gray', '32']
	 */
	public function get_variant_id_by_params( array $params ) {
		$params = array_map(
			static function ( $value ) {
				return strtolower( $value['name'] ?? $value );
			},
			$params
		);

		$filter_variants = $this->included_by_type[ self::REMOTE_NAME_VARIANTS ] ?? array();

		foreach ( $filter_variants as $filter_variant ) {
			if ( ! empty( $filter_variant['attributes']['options'] ) ) {
				$matches = true;
				foreach ( $filter_variant['attributes']['options'] as $option ) {
					if ( ! in_array( $option['option_slug'], $params, true ) ) {
						$matches = false;
						break;
					}
				}
				if ( $matches ) {
					return $filter_variant['id'];
				}
			}
		}

		return null;
	}

	/**
	 * @param array $params
	 * @param int   $id_variant
	 *
	 * @return array|false
	 */
	public function get_variant_info( array $params = array(), int $id_variant = 0 ): array {
		$data = array();
		if ( empty( $id_variant ) ) {
			if ( empty( $params ) ) {
				return array();
			}
			$id_variant = $this->get_variant_id_by_params( $params );
		}

		if ( ! empty( $id_variant ) ) {
			$obj_variant = $this->parse_included_by_id( $id_variant, self::REMOTE_NAME_VARIANTS );
		}

		if ( isset( $obj_variant['attributes'] ) ) {
			$data['attributes'] = $obj_variant['attributes'];
		}

		if ( isset( $obj_variant['relationships'] ) ) {
			foreach ( $obj_variant['relationships'] as $key => $relationship ) {
				if ( isset( $relationship['data']['id'], $relationship['data']['type'] ) ) {
					$data[ $key ] = $this->parse_included_by_id( (int) $relationship['data']['id'], $relationship['data']['type'] );
				}
			}
		}

		$data['remote_variant_id'] = $id_variant;

		return $data;
	}

	/**
	 * @return array
	 */
	public function get_images(): array {
		$filter_variants = $this->included_by_type[ self::REMOTE_NAME_VARIANTS ] ?? array();

		$data = array();

		if ( ! empty( $filter_variants ) ) {
			foreach ( $filter_variants as $filter_variant ) {
				if ( isset( $filter_variant['attributes']['image-url'] ) ) {
					$data[ $filter_variant['id'] ] = $filter_variant['attributes']['image-url'];
				}
			}
		}

		return $data;
	}

	/**
	 * @return array
	 */
	public function get_images_include(): array {
		$ids = array();

		if ( isset( $this->product['data']['relationships'] ) && ! empty( $this->product['data']['relationships'] ) ) {
			foreach ( $this->product['data']['relationships'][ self::REMOTE_NAME_IMAGES ]['data'] as $key => $image ) {
				$ids[ $this->product['data']['id'] ][ $key ] = $image['id'];
			}
		}

		if ( isset( $this->product['included'] ) ) {
			foreach ( $this->product['included'] as $item ) {
				if ( ( $item['type'] === 'options' ) && ! empty( $item['relationships'][ self::REMOTE_NAME_IMAGES ]['data'] ) ) {
					foreach ( $item['relationships'][ self::REMOTE_NAME_IMAGES ]['data'] as $key => $image_item ) {
						$ids[ $item['id'] ][ $key ] = $image_item['id'];
					}
				}
			}
		}

		return $this->parse_included_obj_by_ids( $this->format_image_array( $ids ), $this->product, self::REMOTE_NAME_IMAGES );
	}

	/**
	 * @param string $option
	 *
	 * @return array
	 */
	public function get_image_by_option( string $option ): array {
		$ids = array();
		if ( isset( $this->product['included'] ) ) {
			foreach ( $this->product['included'] as $item ) {
				if (
					$item['type'] === 'options' &&
					$item['attributes']['value'] === $option &&
					! empty( $item['relationships'][ self::REMOTE_NAME_IMAGES ]['data'] )
				) {
					foreach ( $item['relationships'][ self::REMOTE_NAME_IMAGES ]['data'] as $key => $image_item ) {
						$ids[ $item['id'] ][ $key ] = $image_item['id'];
					}
				}
			}
		}

		return $this->parse_included_obj_by_ids( $this->format_image_array( $ids ), $this->product ?? array(), self::REMOTE_NAME_IMAGES );
	}


	/**
	 * @param array $ids
	 *
	 * @return array
	 */
	private function format_image_array( array $ids ): array {
		$mapped_ids = array();
		foreach ( $ids as $id ) {
			if ( ! empty( $id ) ) {
				foreach ( $id as $item ) {
					if ( ! empty( $item ) ) {
						$mapped_ids[ $item ] = $item;
					}
				}
			}
		}

		return $mapped_ids;
	}


	/**
	 * @return false
	 */
	public function get_status_range(): bool {
		$status = false;
		if ( isset( $this->product['data']['attributes'] ) ) {
			$status = $this->product['data']['attributes']['display-price-range'];
		}

		return $status;
	}

	/**
	 * @return bool
	 */
	public function get_min_price_option(): bool {
		return Prodigy_Platform_Settings::is_product_page_price_range_settings();
	}

	/**
	 * @return array
	 */
	public function get_all_images_relation(): array {
		$properties = $this->get_images_include();
		$data       = array();
		foreach ( $properties as $property ) {
			$data[ $property['id'] ] = $property['attributes'];
		}

		return $data;
	}

	/**
	 * @param int    $id
	 * @param string $type
	 *
	 * @return array|mixed
	 */
	protected function parse_included_by_id( int $id, string $type = '' ) {
		if ( ! empty( $type ) && isset( $this->included_by_type[ $type ] ) ) {
			return $this->included_by_type[ $type ][ $id ] ?? array();
		}

		$stack = $this->product['included'] ?? array();

		foreach ( $stack as $remote_include ) {
			if ( (int) $remote_include['id'] === $id ) {
				return $remote_include;
			}
		}

		return array();
	}

	/**
	 * @return array
	 */
	public function get_up_sell_products(): array {
		return $this->get_up_sell_products_main( $this->product ?? array() );
	}

	/**
	 * @return array
	 */
	public function get_cross_sell_products(): array {
		return $this->get_cross_sell_products_main( $this->product ?? array() );
	}

	/**
	 * @param string $attribute_type
	 *
	 * @return array
	 */
	public function get_attribute_properties( string $attribute_type ): array {
		$id_relationships = array();
		if ( isset( $this->product['data']['relationships'] ) && ! empty( $this->product['data']['relationships'] ) ) {
			$data_parse       = $this->product['data']['relationships'][ $attribute_type ];
			$id_relationships = wp_list_pluck( $data_parse['data'], 'id', 'id' );
		}

		return $this->parse_included_obj_by_ids( $id_relationships, $this->product ?? array(), self::REMOTE_NAME_INCLUDE_DESCRIPTIVE_ATTRIBUTES );
	}

	/**
	 * @return array
	 */
	public function get_descriptive_option(): array {
		$properties = $this->get_attribute_properties( self::REMOTE_NAME_DESCRIPTIVE_ATTRIBUTES );

		$data = array();
		foreach ( $properties as $property ) {
			$options = wp_list_pluck( $property['relationships']['options']['data'], 'id' );
			if ( is_array( $options ) ) {
				foreach ( $options as $option ) {
					$option_obj                                = $this->parse_included_obj_by_id( $this->product, $option, 'options' );
					$data[ $property['attributes']['name'] ][] = $option_obj['attributes']['value'];
				}
			}
		}

		return $data;
	}

	/**
	 * @return array
	 */
	public function get_descriptive_properties(): array {
		$id_relationships = array();

		if ( isset( $this->product['data']['relationships'] ) && ! empty( $this->product['data']['relationships'] ) ) {
			$data_parse       = $this->product['data']['relationships'][ self::REMOTE_NAME_DESCRIPTIVE_ATTRIBUTES ];
			$id_relationships = wp_list_pluck( $data_parse['data'], 'id', 'id' );
		}

		return $this->parse_included_obj_by_ids( $id_relationships, $this->product, self::REMOTE_NAME_INCLUDE_DESCRIPTIVE_ATTRIBUTES );
	}

	/**
	 * @return bool
	 */
	public function is_tiered_price(): bool {
		return $this->product['data']['attributes']['tiered-price'] ?? false;
	}

	/**
	 * @return array
	 */
	public function get_tired_prices(): array {
		$result = array();
		if ( $this->is_tiered_price() ) {
			$data_parse        = $this->product['data']['relationships'][ self::QUANTITY_PRICE_BREAKS ];
			$relationships_ids = wp_list_pluck( $data_parse['data'], 'id', 'id' );
			$result            = $this->parse_included_obj_by_ids( $relationships_ids, $this->product, self::QUANTITY_PRICE_BREAKS );
		}

		return $this->calculate_tier_discount( $result );
	}

	/**
	 * @param array $prices
	 *
	 * @return array
	 */
	private function calculate_tier_discount( array $prices ): array {
		foreach ( $prices as $key => $price ) {
			$firstTierPrice = $prices[0]['attributes']['flat-price'];

			if ( ! ( $firstTierPrice && $price['attributes']['flat-price'] ) ) {
				$prices[ $key ]['attributes']['discount'] = '-';
			}

			$discount = $firstTierPrice - $price['attributes']['flat-price'];
			if ( $discount < 0 ) {
				$prices[ $key ]['attributes']['discount'] = '-';

			}

			if ( $key === 0 ) {
				$prices[0]['attributes']['discount'] = '-';
			} else {
				$prices[ $key ]['attributes']['discount'] = round( ( $discount / $firstTierPrice ) * 100, 2 ) . '% ' . '<span class="text-nowrap">' . '(' . '$' . round( $discount, 2 ) . ' per unit' . ')' . '</span>';
			}
		}

		return $prices;
	}


	/**
	 * @param int   $default_logo_option_id
	 * @param array $options
	 *
	 * @return array
	 */
	private function sort_options( int $default_logo_option_id, array $options ): array {
		foreach ( $options as $key => $option ) {
			if ( (int) $option === $default_logo_option_id ) {
				$index = $key;
			}
		}
		if ( isset( $index ) ) {
			$element = $options[ $index ];
			array_splice( $options, $index, 1 );
			array_splice( $options, 0, 0, $element );
		}

		return $options;
	}

	/**
	 * @return array
	 */
	public function get_attributes_options(): array {
		$data                   = array();
		$default_logo_option_id = 0;
		$attributes             = $this->get_attribute_properties( self::REMOTE_NAME_VARIANT_ATTRIBUTES );

		if ( isset( $this->product['data']['attributes']['default-logo-option-id'] ) ) {
			$default_logo_options_info = $this->parse_logos_options_by_type_and_relation_id( $this->product['data']['attributes']['default-logo-option-id'] );
			if ( isset(
				$this->product['data']['attributes']['default-logo-option-id'],
				$default_logo_options_info[ $this->product['data']['attributes']['default-logo-option-id'] ]['option_id']
			) ) {
				$default_logo_option_id = $default_logo_options_info[ $this->product['data']['attributes']['default-logo-option-id'] ]['option_id'];
			}
		}

		foreach ( $attributes as $attribute ) {
			$options     = wp_list_pluck( $attribute['relationships']['options']['data'], 'id' );
			$is_variants = ! empty( $this->product['data']['relationships']['variants']['data'] );
			if ( is_array( $options ) && $is_variants ) {
				if ( isset( $default_logo_option_id ) ) {
					$options = $this->sort_options( $default_logo_option_id, $options );
				}

				foreach ( $options as $option_key => $option_value_id ) {
					$option_obj   = $this->parse_included_obj_by_id( $this->product, $option_value_id, 'options' );
					$logo_options = $this->get_logo_by_option_id( $option_value_id );

					$option_images = array();
					if ( isset( $option_obj['relationships']['images']['data'] ) ) {
						foreach ( $option_obj['relationships']['images']['data'] as $key => $image ) {
							$option_images[ $key ] = $this->parse_included_obj_by_id( $this->product, $image['id'], 'images' );
							if ( isset( $option_images[ $key ]['attributes']['filename'] ) ) {
								unset( $option_images[ $key ]['attributes']['filename'] );
							}
						}
					}

					if ( isset( $attribute['attributes']['slug'], $option_obj['attributes']['slug'] ) ) {
						$data[ $attribute['attributes']['slug'] ][ $option_obj['attributes']['slug'] ]['name'] = $option_obj['attributes']['value'];
						if ( $attribute['attributes']['with-visual-swatch'] && $option_obj['attributes']['image-url'] ) {
							$data[ $attribute['attributes']['slug'] ][ $option_obj['attributes']['slug'] ]['image'] = $option_obj['attributes']['image-url'];
						} elseif ( $attribute['attributes']['with-visual-swatch'] && $option_obj['attributes']['color'] ) {
							$data[ $attribute['attributes']['slug'] ][ $option_obj['attributes']['slug'] ]['color'] = $option_obj['attributes']['color'];
						}
						$data[ $attribute['attributes']['slug'] ][ $option_obj['attributes']['slug'] ]['default'] = $option_obj['attributes']['default'];
						$data[ $attribute['attributes']['slug'] ][ $option_obj['attributes']['slug'] ]['sort']    = $option_key;
						if ( ! empty( $logo_options ) ) {
							$data[ $attribute['attributes']['slug'] ][ $option_obj['attributes']['slug'] ]['logos'] = $logo_options;
						}
						if ( ! empty( $option_images ) ) {
							$data[ $attribute['attributes']['slug'] ][ $option_obj['attributes']['slug'] ]['images'] = $this->add_logos_for_image( $option_images, $logo_options );
						}
						$data[ $attribute['attributes']['slug'] ][ $option_obj['attributes']['slug'] ]['option_id'] = $option_value_id;
					}
				}
			}
		}

		if (
			get_option( 'prodigy_general_manual_sorting' ) === 'yes' &&
			is_plugin_active( 'elementor-pro/elementor-pro.php' ) && did_action( 'elementor/loaded' ) ) {
			$sorted_attributes = Prodigy_Elementor::get_sorted_product_attributes();
			if ( ! empty( $sorted_attributes ) ) {
				return Prodigy_Elementor::sort_attributes( $data, $sorted_attributes );
			}
		}

		return $this->set_default_swatch_option_if_single_variant_option( $data );
	}


	/**
	 * @param array $variants
	 *
	 * @return array
	 */
	private function set_default_swatch_option_if_single_variant_option( array $variants ): array {
		foreach ( $variants as $key => $item ) {
			if ( count( $item ) === 1 ) {
				$item_key                                 = key( $item );
				$variants[ $key ][ $item_key ]['default'] = true;
			}
		}

		return $variants;
	}

	/**
	 * @param array $variants
	 *
	 * @return bool
	 */
	public static function is_single_variant( array $variants ): bool {
		return array_reduce(
			$variants,
			function ( $carry, $item ) {
				return $carry && ( count( $item ) === 1 );
			},
			true
		);
	}

	/**
	 * @param array $images
	 * @param array $images_logos
	 *
	 * @return array
	 */
	private function add_logos_for_image( array $images, array $images_logos ): array {
		foreach ( $images as $key => $image ) {
			foreach ( $images_logos as $logo_key => $logo ) {
				if ( (int) $image['id'] === $logo['image_id'] ) {
					$images[ $key ]['logos'][ $logo_key ] = $logo;
				}
			}
		}

		return $images;
	}

	/**
	 * @param int $option_id
	 *
	 * @return array
	 */
	public function get_logo_by_option_id( int $option_id ): array {
		$prepared_logos         = $this->get_logos_by_option();
		$logo_options           = array();
		$default_logo_option_id = $this->product['data']['attributes']['default-logo-option-id'] ?? 0;

		foreach ( $prepared_logos as $key => $logo ) {
			if ( isset( $logo['option_id'] ) && $logo['option_id'] === $option_id ) {
				$logo_options[ $key ] = $logo;
			}
		}

		if ( isset( $logo_options[ $default_logo_option_id ] ) ) {
			$logo_options = array( $default_logo_option_id => $logo_options[ $default_logo_option_id ] ) + $logo_options;
		}

		return $logo_options;
	}


	/**
	 * @return array
	 */
	public function get_logos_by_option(): array {
		$logos          = array();
		$logo_relations = $this->get_logo_option_relations();
		if ( isset( $logo_relations[ self::LOGO_OPTIONS ] ) ) {
			foreach ( $logo_relations[ self::LOGO_OPTIONS ] as $item ) {
				$logo_info                          = $this->parse_logos_options_by_type_and_relation_id( $item['id'] );
				$logos[ $item['id'] ]               = reset( $logo_info );
				$logos[ $item['id'] ]['is_default'] = $this->product['data']['attributes']['default-logo-option-id'] == $item['id'];
			}
		}

		return $logos;
	}

	/**
	 * @param  int $relation_id
	 *
	 * @return array
	 */
	private function parse_logos_options_by_type_and_relation_id( int $relation_id = 0 ): array {
		$relation_id = $relation_id ?? 0;

		if ( isset( $this->included_by_type[ self::LOGO_OPTIONS ], $relation_id ) ) {
			$logo_option_attributes = $this->included_by_type[ self::LOGO_OPTIONS ][ $relation_id ]['attributes'] ?? array();
		}

		if ( isset( $this->included_by_type[ self::LOGO_LOCATIONS ], $logo_option_attributes['logo-location-id'] ) ) {
			$location_info = $this->included_by_type[ self::LOGO_LOCATIONS ][ $logo_option_attributes['logo-location-id'] ]['attributes'] ?? null;
		}

		if ( isset( $this->included_by_type[ self::LOGO ], $logo_option_attributes['logo-id'] ) ) {
			$logo_info = $this->included_by_type[ self::LOGO ][ $logo_option_attributes['logo-id'] ]['attributes'] ?? null;
		}

		return array(
			$relation_id => array_filter(
				array(
					'option_id'   => $logo_option_attributes['option-id'] ?? null,
					'image_id'    => $logo_option_attributes['image-id'] ?? null,
					'logo_id'     => $logo_option_attributes['logo-id'] ?? null,
					'location_id' => $logo_option_attributes['logo-location-id'] ?? null,
					'location'    => $location_info ?? null,
					'logo'        => $logo_info ?? null,
				)
			),
		);
	}

	/**
	 * @return array
	 */
	public function get_logo_option_relations(): array {
		$logo_relations = array();
		if ( isset( $this->product['data']['relationships'] ) ) {
			foreach ( $this->product['data']['relationships'] as $key => $relationship ) {
				if ( $key === self::LOGO_OPTIONS ) {
					$logo_relations[ $key ] = $relationship['data'];
				}
			}
		}

		return $logo_relations;
	}

	/**
	 * @return array
	 */
	public function get_logos(): array {
		$logos = $this->included_by_type[ self::LOGO ] ?? array();

		usort(
			$logos,
			static function ( $a, $b ) {
				if ( $a['attributes']['position'] == $b['attributes']['position'] ) {
					return 0;
				}

				if ( $a['attributes']['position'] < $b['attributes']['position'] ) {
					return - 1;
				}

				return 1;
			}
		);

		return $logos;
	}


	/**
	 * @return array
	 */
	public function get_logo_locations(): array {
		$locations = $this->included_by_type[ self::LOGO_LOCATIONS ] ?? array();

		usort(
			$locations,
			static function ( $a, $b ) {
				if ( $a['attributes']['position'] == $b['attributes']['position'] ) {
					return 0;
				}

				if ( $a['attributes']['position'] < $b['attributes']['position'] ) {
					return - 1;
				}

				return 1;
			}
		);

		return $locations;
	}

	/**
	 * @param array $attributes
	 *
	 * @return array
	 */
	private function normalize_multiple_logo_charge_settings_attributes( array $attributes ): array {
		if ( empty( $attributes['custom-amounts'] ) || ! is_array( $attributes['custom-amounts'] ) ) {
			return $attributes;
		}
		$flat = array();
		foreach ( $attributes['custom-amounts'] as $item ) {
			if ( is_array( $item ) && array_key_exists( 'amount', $item ) ) {
				$flat[] = $item['amount'];
			} elseif ( is_scalar( $item ) ) {
				$flat[] = $item;
			}
		}
		$attributes['custom-amounts'] = $flat;

		return $attributes;
	}

	/**
	 * @return array
	 */
	public function get_multiple_logo_charge_settings(): array {
		$settings = $this->included_by_type[ self::MULTIPLE_LOGO_CHARGE_SETTINGS ] ?? array();

		if ( empty( $settings ) ) {
			return array();
		}

		$first = reset( $settings );

		$attributes = $first['attributes'] ?? array();

		return $this->normalize_multiple_logo_charge_settings_attributes( $attributes );
	}

	/**
	 * @param int $product_id
	 *
	 * @return array
	 */
	public function get_subscriptions( int $product_id = 0 ): array {
		$conditions_data = array();
		if ( isset( $this->product['data']['relationships'] ) ) {
			foreach ( $this->product['data']['relationships'] as $key => $relationship ) {
				if ( ! empty( $product_id ) && $key === self::REMOTE_NAME_VARIANTS ) {
					foreach ( $relationship['data'] as $variant ) {
						if ( (int) $product_id === (int) $variant['id'] ) {
							$product_id = $variant['id'];
						}
						break;
					}
				} elseif ( empty( $product_id ) && $key === self::REMOTE_NAME_MASTER_VARIANT ) {
					$product_id = $relationship['data']['id'];
					break;
				}
			}
			if ( ! empty( $product_id ) ) {
				$info_include = $this->parse_included_by_id( (int) $product_id, self::REMOTE_NAME_VARIANTS );
				$plans        = $info_include['relationships']['subscription-plan'] ?? array();

				if ( isset( $plans['data']['id'] ) ) {
					$include_conditions                   = $this->parse_included_by_id( $plans['data']['id'], self::SUBSCRIPTION_PLANS );
					$conditions                           = $include_conditions['relationships']['subscription-conditions']['data'] ?? array();
					$conditions_data['subscription-only'] = $include_conditions['attributes']['subscription-only'];

					foreach ( $conditions as $key => $item ) {
						if ( $item['type'] === 'subscription-conditions' ) {
							$conditions_data[ $key ] = $this->parse_included_by_id( $item['id'] );
						}
					}
				}
			}
		}

		return $conditions_data;
	}
}
