<?php

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


/**
 * Handles Wiaas Cart specific tasks
 *
 * Class Wiaas_Cart
 */
class Wiaas_Cart {

	private static $cart_doc_types = array(
		'template_questionaire' => 'order_questionaire',
		'template_agreement' => 'order_agreement'
	);


	public static function init() {
		add_action( 'woocommerce_checkout_create_order_line_item', array( __CLASS__, 'add_order_item_meta' ), 10, 3 );

		add_filter( 'woocommerce_hidden_order_itemmeta', array( __CLASS__, 'hidden_order_item_meta' ) );

		add_action( 'woocommerce_before_calculate_totals', array( __CLASS__, 'on_calculate_totals' ), 99, 1);

		add_action( 'woocommerce_cart_loaded_from_session', array( __CLASS__, 'on_calculate_totals' ), 99, 1);

		add_action('woocommerce_checkout_create_order', array(__CLASS__, 'add_additional_order_data'), 99);
	}

	/**
	 * Retrieve cart documents (templates and uploded documents)
	 * @return array
	 */
	public static function get_cart_documents() {
		$templates = self::_get_cart_templates();
		$uploaded_documents = self::_get_cart_uploaded_documents();

		$has_pending_uploads = false;

		foreach ($templates as $type => $package_documents) {
			$uploaded_type = self::$cart_doc_types[$type];

			if (!isset($uploaded_documents[$uploaded_type])) {
				$has_pending_uploads = true;
				break;
			}

			$uploaded_package_documents = $uploaded_documents[$uploaded_type];

			foreach ($package_documents as $package_id => $package_document) {
				if (!isset($uploaded_package_documents[$package_id])) {
					$has_pending_uploads = true;
					break;
				}
			}
		}

		return array(
			'templates' => $templates,
			'uploaded' => $uploaded_documents,
			'pending' => $has_pending_uploads
		);
	}

	/** Uploaded document for cart
	 *
	 * @param $doc_type
	 * @param $package_item_key
	 *
	 * @return string | WP_Error
	 */
	public static function upload_cart_document($doc_type, $package_item_key) {
		try {
			$result = Wiaas_Document_Upload::upload_document_version();

			// File upload failed
			if (is_wp_error($result)) {
				$code = $result->get_error_code();

				if ($code === 'wiaas_upload_error_missing_file' || $code === 'wiaas_upload_error_invalid_extension') {
					wc_add_notice($result->get_error_message(), 'error');
				} else {
					wc_add_notice('File upload failed!', 'error');
				}

				return false;
			}

			// save uploaded cart documents info to cart item
			WC()->cart->cart_contents[ $package_item_key ]['_wiaas_documents'][$doc_type] = array(
				'version' => $result,
				'key' => wp_generate_uuid4(),
			);;

			// persist changes
			WC()->cart->calculate_totals();

			return true;

		} catch( Exception $e) {
			wc_add_notice('Could not upload cart document!', 'error');
			return false;
		}
	}

	/**
	 * Handles adding standard wiaas package to cart along with selected addons and options
	 *
	 * Extend cart item data with wiaas package payment info and addons and options
	 *
	 * Every package cart item will be extended with its payment info but only standard package type
	 * will be extended with addons and options arrays
	 *
	 * @param int $package_id Package ID of selected package
	 * @param string $price_id Price ID of selected package payment
	 * @param int $shop_owner_id Shop owner commercial lead ID
	 * @param array $addons_ids Array of selected additional packages IDs
	 * @param array $options_ids Array of selected option packages IDs
	 *
	 * @return bool TRUE if all packages are succesfully added to cart, FALSE otherwise
	 */
	public static function add_package_to_cart($package_id, $price_id, $shop_owner_id, $addons_ids, $options_ids) {
		// try adding package to cart
		try {
			// Check if package is in cart
			if (self::_is_package_in_cart($package_id)) {
				wc_add_notice('Package already in cart!', 'error');
				return false;
			}

			//Check if package exists
			$package = wc_get_product( $package_id );
			if (!$package) {
				wc_add_notice('Package does not exists!', 'error');
				return false;
			}

			//Check if package is available for adding to cart
			if (Wiaas_Package_Status::get_package_status($package_id) !== Wiaas_Package_Status::AVAILABLE){
				wc_add_notice('Package cannot be purchased at the moment!', 'error');
				return false;
			}

			// Retrieve package country
			$country = Wiaas_Countries::get_package_country($package);

			if (empty($country)) {
				wc_add_notice('Package cannot be added do cart!', 'error');
				return false;
			}

			// TODO: Add validation that only packages from the same country can be added to cart at the same time
			update_user_meta( get_current_user_id(), '_wiaas_cart_items_country', $country);

			// TODO: Add validation that only packages from the same shop can be added to cart at the same time
			update_user_meta( get_current_user_id(), '_wiaas_cart_shop_owner_id', $shop_owner_id);

            $customer_id = wiaas_get_current_user_organization_id();

            // Retrieve package price
            $package_prices = Wiaas_Pricing::get_standard_package_customer_prices($package, $customer_id, $shop_owner_id);

			$selected_price_index = array_search($price_id, array_column($package_prices, 'id'));

			// Initialize additional cart item data for wiaas packages
			$wiaas_cart_item_data = array(
				'_wiaas_standard_package' => true,
				'_wiaas_addon_items' => array(),
				'_wiaas_option_items' => array(),
				'_wiaas_payment' => $package_prices[$selected_price_index] ? $package_prices[$selected_price_index] : null,
				'_wiaas_documents' => array()
			);

			$cart_item_key = WC()->cart->add_to_cart($package_id, 1, 0, array(), $wiaas_cart_item_data);

			if (!$cart_item_key) {
				wc_add_notice('Package could not be added to cart!', 'error');
				return false;
			}

			// Add selected additional packages and options
			self::_add_additional_packages_to_cart($cart_item_key, $price_id, $shop_owner_id, $addons_ids, $options_ids);

			// Trigger calculation of total prices after additional packages are added
			WC()->cart->calculate_totals();

			return true;

		} catch( Exception $e) {
			return false;
		}
	}


	/**
	 * Remove given package item key from cart
	 *
	 * @param string $package_cart_item_key
	 *
	 * @return bool
	 */
	public static function remove_package_from_cart($package_cart_item_key) {

		$cart_item = WC()->cart->get_cart_item($package_cart_item_key);

		if (!$cart_item) {
			return false;
		}

		$package_addon_item_keys = $cart_item['_wiaas_addon_items'];
		$package_option_item_keys = $cart_item['_wiaas_option_items'];

		$success = WC()->cart->remove_cart_item($package_cart_item_key);

		if ($success) {
			foreach ($package_addon_item_keys as $package_addon_item_key) {
				WC()->cart->remove_cart_item($package_addon_item_key);
			}

			foreach ($package_option_item_keys as $package_option_item_key) {
				WC()->cart->remove_cart_item($package_option_item_key);
			}
		}

		return $success;
	}

	/**
	 * Update quantity of package in cart
	 * @param string $package_cart_item_key
	 * @param int $new_quantity
	 *
	 * @return bool
	 */
	public static function update_package_quantity($package_cart_item_key, $new_quantity) {
		$cart_item = WC()->cart->get_cart_item($package_cart_item_key);

		if (!$cart_item) {
			return false;
		}

		$package_addon_item_keys = $cart_item['_wiaas_addon_items'];
		$package_option_item_keys = $cart_item['_wiaas_option_items'];

		$success = WC()->cart->set_quantity($package_cart_item_key, $new_quantity, true);

		if ($success) {
			foreach ($package_addon_item_keys as $package_addon_item_key) {
				WC()->cart->set_quantity($package_addon_item_key, $new_quantity, true);
			}

			foreach ($package_option_item_keys as $package_option_item_key) {
				WC()->cart->set_quantity($package_option_item_key, $new_quantity, true);
			}
		}

		return $success;
	}


	/**
	 * Update package cart item with `minimal_fixed_price` as its price
	 * so resulting totals would be sum of these prices
	 * @param $cart
	 */
	public static function on_calculate_totals($cart) {

		foreach ($cart->cart_contents as $key => $cart_item) {
			if (isset($cart_item['_wiaas_standard_package']))  {

				$total = self::get_cart_item_total($cart_item);

				WC()->cart->cart_contents[ $key ]['data']->set_price( $total['fixed_extra'] );

			} else {
				WC()->cart->cart_contents[ $key ]['data']->set_price( 0 );
			}
		}
	}

	/**
	 * Persist used payment type information for package in corresponding order line item.
	 * Also for standard package type list of addons and options will be saved.
	 *
	 * @param $order_item
	 * @param $cart_item_key
	 * @param $cart_item
	 * @param $order
	 *
	 * @return WC_Order_Item
	 */
	public static function add_order_item_meta( $order_item, $cart_item_key, $cart_item ) {
		if (wc_pb_is_bundle_container_cart_item($cart_item) && isset($cart_item['_wiaas_payment'])) {

			$payment = $cart_item['_wiaas_payment'];

			$total = self::get_cart_item_total($cart_item);

			$order_item->add_meta_data( '_wiaas_payment_type', $payment['payment_type'], true );
			$order_item->add_meta_data( '_wiaas_services_extra', $total['services_extra'], true );
			$order_item->add_meta_data( '_wiaas_service_contract_period', $payment['services_contract_period'], true );
			$order_item->add_meta_data( '_wiaas_max_contract_period', $payment['max_contract_period'], true );
			$order_item->add_meta_data( '_wiaas_period_unit', $payment['period_unit'], true );
			$order_item->add_meta_data( '_wiaas_recurrent_extra', $total['recurrent_extra'], true );
			$order_item->add_meta_data( '_wiaas_pay_period', $payment['package_pay_period'], true );
		}

		if (isset($cart_item['_wiaas_standard_package'])) {
			$order_item->add_meta_data( '_wiaas_standard_package', $cart_item['_wiaas_standard_package'], true );
		}

		// add options metadata
		if (isset($cart_item['_wiaas_option_items'])) {
			$order_item->add_meta_data( '_wiaas_option_items', $cart_item['_wiaas_option_items'] );
		}
		if (isset($cart_item['_wiaas_option_for'])) {
			$order_item->add_meta_data( '_wiaas_option_for', $cart_item['_wiaas_option_for'], true );
		}
		if (isset($cart_item['_wiaas_option_group_name'])) {
			$order_item->add_meta_data( '_wiaas_option_group_name', $cart_item['_wiaas_option_group_name'], true );
		}

		// add addons metadata
		if (isset($cart_item['_wiaas_addon_items'])) {
			$order_item->add_meta_data( '_wiaas_addon_items', $cart_item['_wiaas_addon_items'] );
		}
		if (isset($cart_item['_wiaas_addon_for'])) {
			$order_item->add_meta_data( '_wiaas_addon_for', $cart_item['_wiaas_addon_for'], true );
		}

		// add documents associated with the item
		$cart_documents = isset($cart_item['_wiaas_documents']) ? $cart_item['_wiaas_documents'] : array();
		$attachment_document_ids = wiaas_get_object_attached_documents($cart_item['product_id']);
		$item_documents = array();

		foreach ($cart_documents as $type => $cart_document) {
			$item_documents[] = array(
				'key' => $cart_document['key'],
				'version' => $cart_document['version'],
				'type' => $type
			);
		}

		foreach ($attachment_document_ids as $attachment_document_id) {
			$doc_info = Wiaas_Document::get_doc_info($attachment_document_id);

			// add customer visible attachment documents to order
			if ($doc_info['visible'] || !$doc_info['type']) {
				$item_documents[] = array(
					'key' => wp_generate_uuid4(),
					'name' => $doc_info['name'],
					'version' => $doc_info['version'],
					'type' => $doc_info['type']['id'],
				);
			}
		}

		if (count($item_documents) > 0) {
			$order_item->add_meta_data( '_wiaas_documents', $item_documents, true );
		}


		return $order_item;
	}

	/**
	 * Mark extended properties for order as hidden
	 * @param $hidden
	 *
	 * @return array
	 */
	public static function hidden_order_item_meta( $hidden ) {

		return array_merge( $hidden, array(
			'_wiaas_payment_type',
			'_wiaas_services_extra',
			'_wiaas_service_contract_period',
			'_wiaas_max_contract_period',
			'_wiaas_period_unit',
			'_wiaas_recurrent_extra',
			'_wiaas_pay_period',
			'_wiaas_addon_items',
			'_wiaas_addon_for',
			'_wiaas_option_items',
			'_wiaas_option_for',
			'_wiaas_option_group_name',
			'_wiaas_standard_package',
			'_wiaas_documents',
		) );
	}

	/**
	 * Sets additional order data form cart after order is successfully created
	 *
	 * @param WC_Order $order
	 *
	 * @throws WC_Data_Exception
	 *
	 */
	public static function add_additional_order_data($order) {

		// set order currency
		$country = get_user_meta(get_current_user_id(), '_wiaas_cart_items_country', true);
		$currency = empty($country) ? get_woocommerce_currency() : $country['currency'];

		$order->set_currency($currency);

		// set order commercial lead
		$shop_owner_id = get_user_meta(get_current_user_id(), '_wiaas_cart_shop_owner_id', true);
		$shop_owner_id = absint($shop_owner_id);

		$order->add_meta_data('_wiaas_commercial_lead_id', $shop_owner_id);
	}

	/**
	 * Calculate total cost for cart item
	 *
	 * @param $cart_item
	 *
	 * @return array
	 */

	public static function get_cart_item_total($cart_item) {

		$package_price = isset($cart_item['_wiaas_payment']) ? $cart_item['_wiaas_payment'] : array(
			'fixed_extra' => 0,
			'services_extra' => 0,
			'recurrent_extra' => 0
		);

		$total_fixed_extra = $package_price['fixed_extra'];
		$total_services_extra = $package_price['services_extra'];
		$total_recurrent_extra = $package_price['recurrent_extra'];

		$cart_item_addons = wiaas_get_cart_item_addons($cart_item);
		foreach ($cart_item_addons as $cart_item_addon) {
			$addon_price = $cart_item_addon['_wiaas_payment'];
			$total_fixed_extra += $addon_price['fixed_extra'];
			$total_services_extra += $addon_price['services_extra'];
			$total_recurrent_extra += $addon_price['recurrent_extra'];
		}

		$cart_item_options = wiaas_get_cart_item_options($cart_item);
		foreach ($cart_item_options as $cart_item_option) {
			$option_price = $cart_item_option['_wiaas_payment'];
			$total_fixed_extra += $option_price['fixed_extra'];
			$total_services_extra += $option_price['services_extra'];
			$total_recurrent_extra += $option_price['recurrent_extra'];
		}

		return array(
			'fixed_extra' => $total_fixed_extra,
			'services_extra' => $total_services_extra,
			'recurrent_extra' => $total_recurrent_extra
		);
	}

	/**
	 * Retrieves count of wiaas standard packages in cart
	 *
	 * @return int Number of wiaas standard packages in cart
	 */
	public static function get_cart_packages_count() {
		$items = WC()->cart->get_cart_contents();

		return array_reduce($items, function($count, $item) {
			if (isset($item['_wiaas_standard_package'])) {
				$count++;
			}
			return $count;
		}, 0);
	}


	/**
	 * Retrieves packages data from cart
	 * @return array
	 */
	public static function get_cart_packages() {
		$items = WC()->cart->get_cart_contents();

		$shop_owner_id = get_user_meta(get_current_user_id(), '_wiaas_cart_shop_owner_id', true);
		$shop_owner_id = absint($shop_owner_id);

		$package_items = array();

		foreach ($items as $key => $item) {
			if (!isset($item['_wiaas_standard_package'])) {
				continue;
			}

			$package = wc_get_product($item['product_id']);

			// Collect additional packages
			$additional_packages = array_map(
				function($addon_cart_item) {
					$additional_package = wc_get_product($addon_cart_item['product_id']);
					return array(
						'package_id' => $additional_package->get_id(),
						'package_name' => $additional_package->get_title(),
						'prices' => array(
							'fixed_extra' => $addon_cart_item['_wiaas_payment']['fixed_extra'],
							'recurrent_extra' => $addon_cart_item['_wiaas_payment']['recurrent_extra'],
							'services_extra' => $addon_cart_item['_wiaas_payment']['services_extra'],
						)
					);
				},
				wiaas_get_cart_item_addons($item));


			// Collect package options
			$package_options = array_map(
				function($option_cart_item) {
					$option_package = wc_get_product($option_cart_item['product_id']);
					return array(
						'package_id' => $option_package->get_id(),
						'group_name' => $option_cart_item['_wiaas_option_group_name'],
						'package_name' => $option_package->get_title(),
						'prices' => array(
							'fixed_extra' => $option_cart_item['_wiaas_payment']['fixed_extra'],
							'recurrent_extra' => $option_cart_item['_wiaas_payment']['recurrent_extra'],
							'services_extra' => $option_cart_item['_wiaas_payment']['services_extra'],
						)
					);
				},
				wiaas_get_cart_item_options($item));

			$package_items[] = array(
				'package_id' => $item['product_id'],
				'key' => $item['key'],
				'package_name' => $package->get_title(),
				'quantity' => $item['quantity'],

				'commercial_lead_id' => $shop_owner_id,
				'commercial_lead' => wiaas_get_organization_name($shop_owner_id),
				'country' => Wiaas_Countries::get_package_country($package),

				'are_additional_available' => true,
				'additional_packages' => $additional_packages,

				'are_options_available' => true,
				'options' => $package_options,

				'bids' => array(),

				'payment_info' => array(
					'id' => $item['_wiaas_payment']['id'],
					'type' => $item['_wiaas_payment']['payment_type'],
					'period_unit' => $item['_wiaas_payment']['period_unit'],
					'fixed_extra' => $item['_wiaas_payment']['fixed_extra'],
					'recurrent_extra' => $item['_wiaas_payment']['recurrent_extra'],
					'services_extra' => $item['_wiaas_payment']['services_extra'],
				),

				'total_prices' => Wiaas_Cart::get_cart_item_total($item),

				'status' => 'available',
			);
		}

		return $package_items;
	}


	//PRIVATE

	/**
	 * Retrieve cart templates
	 * @return array
	 */
	private static function _get_cart_templates() {
		$items = WC()->cart->get_cart_contents();

		$documents_ids = array();

		foreach ($items as $key => $item) {
			if (!isset($item['_wiaas_standard_package'])) {
				continue;
			}

			$package_documents_ids = get_post_meta(
				$item['product_id'],
				'_wiaas_attached_documents',
				true);

			foreach ($package_documents_ids as $package_document_id) {
				$package_document_id = absint($package_document_id);
				$documents_ids[$package_document_id] ?: array();
				$documents_ids[$package_document_id][] = $item['product_id'];
			}
		}

		$q = new WP_Query();
		$retrieved_items = $q->query(array(
			'post_status' => 'publish',
			'post_type' => 'wiaas_doc',
			'post__in' => array_keys($documents_ids),
			'tax_query' => array(
				array(
					'taxonomy' => 'wiaas_doc_type',
					'field'    => 'slug',
					'terms'    => array_keys(self::$cart_doc_types),
				)
			)
		));

		$cart_documents = array();

		foreach ($retrieved_items as $retrieved_item) {

			$doc_info = Wiaas_Document::get_doc_info($retrieved_item->ID);
			$type = $doc_info['type']['id'];

			$cart_documents[$type] ?: array();

			$package_ids = $documents_ids[$doc_info['id']] ?: array();
			foreach ($package_ids as $package_id) {
				$cart_documents[$type][$package_id] = array(
					'id' => $doc_info['id'],
					'name' => $doc_info['name'],
					'type' => $type,
					'extension' => $doc_info['extension']
				);
			}
		}

		return $cart_documents;
	}

	/**
	 * Retrieve cart uploaded documents
	 * @return array
	 */
	private static function _get_cart_uploaded_documents() {
		$items = WC()->cart->get_cart_contents();

		$cart_documents = array();

		foreach ($items as $key => $item) {
			if (!isset($item['_wiaas_standard_package'])) {
				continue;
			}

			$package_id = $item['product_id'];

			$documents = isset($item['_wiaas_documents']) ? $item['_wiaas_documents'] : array();

			foreach ($documents as $type => $document) {
				$cart_documents[$type] ?: array();
				$cart_documents[$type][$package_id] = array(
					'key' => $document['key'],
					'name' => wiaas_get_doc_version_filename($document['version']),
					'type' => $type,
					'extension' => wiaas_get_doc_version_extension($document['version'])
				);

			}

		}

		return $cart_documents;
	}


	/**
	 * Add selected package options and addons after parent standard package is added to cart
	 * 
	 * @param string $package_cart_item_key
	 * @param int $price_id
	 * @param int $shop_owner_id
	 * @param array $addons_ids
	 * @param array $options_ids
	 *
	 * @throws Exception if any of the addons or options cannot be added to cart
	 */
	private static function _add_additional_packages_to_cart($package_cart_item_key, $price_id, $shop_owner_id, $addons_ids, $options_ids) {

		$parent_item = WC()->cart->get_cart_item($package_cart_item_key);

		$addon_items_keys = array();
		$option_items_keys = array();

        $customer_id = wiaas_get_current_user_organization_id();

		// Try adding package addons to cart
		foreach ($addons_ids as $addon_id) {
			//Check if addon package exists
			$addon_package = wc_get_product($addon_id);
			if (!$addon_package) {
				throw new Exception( __( 'Sorry, additional package does not exist.', 'wiaas' ) );
			}

            // Retrieve addon package price
            $package_prices = Wiaas_Pricing::get_addon_package_customer_price(
                $addon_package,
                $parent_item['data'],
                $customer_id,
	            $shop_owner_id
            );
			$selected_price_index = array_search($price_id, array_column($package_prices, 'id'));

			// Initialize additional cart item data for wiaas addon packages
			$wiaas_addon_cart_item_data = array(
				'_wiaas_addon_for' => $package_cart_item_key,
				'_wiaas_payment' => $package_prices[$selected_price_index] ? $package_prices[$selected_price_index] : null
			);

			$addon_cart_item_key = WC()->cart->add_to_cart($addon_id,
				1, 
				0, 
				array(),
				$wiaas_addon_cart_item_data
			);
			
			if (!$addon_cart_item_key) {
				throw new Exception( __( 'Sorry, additional package could not be added to cart.', 'wiaas' ) );
			}

			$addon_items_keys[] = $addon_cart_item_key;
		}

		// Try adding package options to cart
		foreach ($options_ids as $option_id) {
			//Check if option package exists
			$option_package = wc_get_product($option_id);
			if (!$option_package) {
				throw new Exception( __( 'Sorry, option package does not exist.', 'wiaas' ) );
			}

            // Retrieve option package price
            $package_prices = Wiaas_Pricing::get_option_package_customer_price(
                $option_package,
                $parent_item['data'],
                $customer_id,
	            $shop_owner_id);
			$selected_price_index = array_search($price_id, array_column($package_prices, 'id'));

			// Retrieve option package group name
			$option_group_name = Wiaas_Package_Option_Groups::get_group_name_for_package_option($parent_item['data'], $option_package);


			// Initialize additional cart item data for wiaas option packages
			$wiaas_option_cart_item_data = array(
				'_wiaas_option_for' => $package_cart_item_key,
				'_wiaas_payment' => $package_prices[$selected_price_index] ? $package_prices[$selected_price_index] : null,
				'_wiaas_option_group_name' => $option_group_name,
			);

			$option_cart_item_key = WC()->cart->add_to_cart($option_id,
				1,
				0, 
				array(),
				$wiaas_option_cart_item_data
			);

			if (!$option_cart_item_key) {
				throw new Exception( __( 'Sorry, package option could not be added to cart.', 'wiaas' ) );
			}

			$option_items_keys[] = $option_cart_item_key;
		}

		WC()->cart->cart_contents[ $package_cart_item_key ]['_wiaas_addon_items'] = $addon_items_keys;
		WC()->cart->cart_contents[ $package_cart_item_key ]['_wiaas_option_items'] = $option_items_keys;
	}

	/**
	 * Check if package is added to cart
	 * @param int $package_id
	 *
	 * @return bool
	 */
	private static function _is_package_in_cart($package_id) {
		$cart_item = WC()->cart->get_cart_contents();

		foreach ($cart_item as $item) {
			if($item['product_id'] === $package_id) {
				return true;
			}
		}

		return false;
	}
}

Wiaas_Cart::init();