<?php

namespace StoreEngine\API;

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

use StoreEngine\classes\AbstractProduct;
use StoreEngine\Classes\Price;
use StoreEngine\Classes\Product\VariableProduct;
use StoreEngine\Classes\Variation;
use StoreEngine\Utils\Helper;
use Throwable;
use WP_Post;
use WP_REST_Request;

class Product {

	public static function init() {
		$self = new self();

		add_filter( 'rest_prepare_' . Helper::PRODUCT_POST_TYPE, [ $self, 'extend_product_rest_response' ], 10, 3 );
		add_action( 'rest_insert_storeengine_product', [ $self, 'save_product_data' ], 10, 3 );
	}

	public function extend_product_rest_response( $item, $post, $request ) {
		$context = $request->get_param( 'context' );
		$product                    = Helper::get_product( $item->data['id'] );
		$item->data['product_type'] = $product->get_type();
		$item->data['prices']       = Helper::get_prices_array_by_product_id( $item->data['id'], $context );
		$item->data['integrations'] = array_map( fn( $integration ) => [
			'id'             => $integration->integration->get_id(),
			'product_id'     => $integration->price->get_product_id(),
			'price_id'       => $integration->price->get_id(),
			'integration_id' => $integration->integration->get_integration_id(),
			'provider'       => $integration->integration->get_provider(),
			'course_ids'     => 'storeengine/course-bundle' === $integration->integration->get_provider() ? get_post_meta( $integration->integration->get_integration_id(), 'academy_course_bundle_courses_ids', true ) ?? [] : [],
		], Helper::get_integrations_by_product_id( $item->data['id'] ) );

		$attributes = [];

		foreach ( $product->get_attributes() as $taxonomy => $terms ) {
			$taxonomyKey  = Helper::strip_attribute_taxonomy_name( $taxonomy );
			$attributes[] = [
				'label' => $taxonomyKey,
				'ids'   => array_map( fn( $term ) => $term->term_id, $terms ),
			];
		}

		$item->data['attributes'] = $attributes;

		if ( 'bundled' === $item->data['product_type'] ) {
			$item->data['bundles'] = $product->get_bundles();
		}

		if ( 'variable' === $item->data['product_type'] ) {
			$item->data['variants'] = array_map( function ( $variant ) {
				$data       = [];
				$taxonomies = [];
				foreach ( $variant->get_attributes() as $attribute ) {
					if ( ! taxonomy_exists( $attribute->taxonomy ) ) {
						continue;
					}
					$data[] = [
						'label' => get_taxonomy( $attribute->taxonomy )->label,
						'value' => $attribute->name,
					];

					$taxonomies[ Helper::strip_attribute_taxonomy_name( $attribute->taxonomy ) ] = $attribute->term_id;
				}

				return [
					'id'                => $variant->get_id(),
					'taxonomies'        => $taxonomies,
					'data'              => $data,
					'featured_image_id' => $variant->get_featured_image(),
					'pricing_id'        => $variant->get_pricing_id(),
					'price'             => $variant->get_price(),
					'sku'               => $variant->get_sku(),
				];
			}, $product->get_variants() );
		}

		return $item;
	}

	public function save_product_data( WP_Post $post, WP_REST_Request $request, bool $creating ) {
		$this->save_attributes( $post, $request );

		// @TODO Update price props, this will reduces extra ajax endpoint for saving/creating price
		//       Also, improve ux as adding price will no longer need product id.

		// Updating custom sort-order.
		if ( ! $creating ) {
			$prices = $request->get_param( 'prices' );
			if ( ! empty( $prices ) && is_array( $prices ) ) {
				foreach ( $prices as $index => [ 'id' => $id, 'price_name' => $price_name ] ) {
					$price = new Price( $id );
					$price->set_name( $price_name );
					$price->set_order( $index );
					$price->save();
				}
			}
		}
	}


	public function save_attributes( WP_Post $post, WP_REST_Request $request ) {
		// Can be simple, variable, bundled, etc.
		$old_type     = get_post_meta( $post->ID, '_storeengine_product_type', true );
		$product_type = $request->get_param( 'product_type' ) ?? 'simple';
		$variants     = $request->get_param( 'variants' );
		$bundles      = $request->get_param( 'bundles' );

		// Handle variable/variations.
		if ( 'variable' === $old_type && ( $old_type !== $product_type || empty( $variants ) || ! is_array( $variants ) ) ) {
			$variants     = [];
			$product_type = 'simple';
			$product      = new VariableProduct( $post->ID );
			foreach ( $product->get_variants() as $variation ) {
				$variation->delete();
			}
		}

		if ( $variants && is_array( $variants ) ) {
			$product_type         = 'variable';
			$product              = new VariableProduct( $post->ID );
			$new_variations_data  = [];
			$edit_variations_data = [];

			foreach ( $variants as $variant ) {
				if ( ! isset( $variant['taxonomies'] ) || ! is_array( $variant['taxonomies'] ) ) {
					continue;
				}

				if ( isset( $variant['id'] ) ) {
					$edit_variations_data[ $variant['id'] ] = $variant;
				} else {
					$new_variations_data[] = $variant;
				}
			}

			foreach ( $product->get_variants() as $variation ) {
				if ( isset( $edit_variations_data[ $variation->get_id() ] ) ) {
					$this->save_variation_data( $variation, $product->get_id(), $edit_variations_data[ $variation->get_id() ] );
				} else {
					$variation->delete();
				}
			}

			if ( ! empty( $new_variations_data ) ) {
				foreach ( $new_variations_data as $new_variation_data ) {
					$this->save_variation_data( new Variation(), $product->get_id(), $new_variation_data );
				}
			}
		}

		// Handle product bundles.
		if ( 'bundled' === $old_type && ( $old_type !== $product_type || empty( $bundles ) || ! is_array( $bundles ) ) ) {
			$bundles = [];
			delete_post_meta( $post->ID, '_storeengine_product_bundles' );
			$product_type = 'simple';
		}

		if ( $bundles && is_array( $bundles ) ) {
			$data   = [];
			$prices = [];

			foreach ( $bundles as $bundle ) {
				$product_id = absint( $bundle['product_id'] ?? 0 );
				$price_id   = absint( $bundle['price_id'] ?? 0 );
				$quantity   = absint( $bundle['quantity'] ?? 1 );

				if ( ! $product_id || ! $price_id || ! $quantity ) {
					continue;
				}

				try {
					$price = new Price( $price_id );

					$prices[] = $price->get_price();

					$data[] = [
						'product_id' => $price->get_product_id(),
						'price_id'   => $price->get_id(),
						'quantity'   => $quantity,
					];
				} catch ( Throwable $e ) {
					Helper::log_error( $e );
				}
			}

			if ( ! empty( $data ) ) {
				$min_max = array_unique( [ min( $prices ), max( $prices ) ] );
				update_post_meta( $post->ID, '_storeengine_product_bundle_max_min_prices', $min_max );
				update_post_meta( $post->ID, '_storeengine_product_bundles', $data );

				$product_type = 'bundled';
			}
		}

		if ( $product_type ) {
			update_post_meta( $post->ID, '_storeengine_product_type', $product_type );
		}

		$product = Helper::get_product( $post->ID );

		$prices = [];

		foreach ( $product->get_prices() as $price ) {
			$prices[] = $price->get_price();
		};

		if ( $prices ) {
			$min_max = array_unique( [ min( $prices ), max( $prices ) ] );

			update_post_meta( $post->ID, '_storeengine_product_max_min_prices', $min_max );
		}

		$unformatted_attributes = $request->get_param( 'attributes' );
		if ( ! is_array( $unformatted_attributes ) ) {
			return;
		}

		$attributes = [];

		foreach ( $unformatted_attributes as $unformatted_attribute ) {
			$attributes[ $unformatted_attribute['label'] ] = $unformatted_attribute['ids'];
		}

		$unformatted_existence_attributes = $product->get_attributes();
		$existence_attributes             = [];

		foreach ( $unformatted_existence_attributes as $taxonomy => $terms ) {
			$taxonomyKey                          = Helper::strip_attribute_taxonomy_name( $taxonomy );
			$existence_attributes[ $taxonomyKey ] = array_map( fn( $term ) => $term->term_id, $terms );
		}

		if ( $attributes !== $existence_attributes ) {
			$product->set_attributes_order( array_map( fn( $taxonomy ) => Helper::get_attribute_taxonomy_name( $taxonomy ), array_keys( $attributes ) ) );

			foreach ( $attributes as $key => $value ) {
				wp_set_object_terms( $post->ID, array_map( fn( $val ) => (int) sanitize_text_field( $val ), $value ), Helper::get_attribute_taxonomy_name( sanitize_text_field( $key ) ) );
			}

			// Update the order.
			$update_values = [];
			$update_cases  = [];

			foreach ( $attributes as $taxonomy => $terms ) {
				foreach ( $terms as $order => $term_id ) {
					$update_cases[]  = "WHEN tr.term_taxonomy_id = $term_id THEN $order";
					$update_values[] = $term_id;
				}
			}

			// Execute bulk UPDATE if there are items to update
			if ( ! empty( $update_cases ) ) {
				global $wpdb;
				$update_query = "UPDATE {$wpdb->term_relationships} tr
							INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
							SET tr.term_order = CASE " . implode( ' ', $update_cases ) . ' END
							WHERE tr.object_id = %d
							AND tr.term_taxonomy_id IN
							(' . implode( ',', array_fill( 0, count( $update_values ), '%d' ) ) . ')';
				// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
				$wpdb->query( $wpdb->prepare( $update_query, $post->ID, ...$update_values ) );
				// phpcs:enable PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
			}
			wp_cache_flush_group( AbstractProduct::CACHE_GROUP );
		}
	}

	private function save_variation_data( Variation $variation, int $product_id, array $data ) {
		$variation->set_product_id( $product_id );
		$price = isset( $data['price'] ) && is_numeric( $data['price'] ) ? (float) sanitize_text_field( $data['price'] ) : null;
		$variation->set_price( $price );
		$pricing_id = (int) sanitize_text_field( $data['pricing_id'] ?? 0 );
		$variation->set_price_id( $pricing_id > 0 ? $pricing_id : null );
		$featured_image_id = (int) sanitize_text_field( $data['featured_image_id'] ?? 0 );
		$variation->set_featured_image( $featured_image_id > 0 ? $featured_image_id : null );
		$variation->set_sku( sanitize_text_field( $data['sku'] ) );
		$term_ids = array_map( fn( $term_id ) => (int) sanitize_text_field( $term_id ), $data['taxonomies'] );
		$term_ids = array_values( $term_ids );
		$variation->set_attributes( $term_ids );
		$variation->save();
	}
}
