<?php
namespace Mewz\WCAS\Aspects\Workers;

use Automattic\WooCommerce\Internal\ProductAttributesLookup\DataRegenerator;
use Automattic\WooCommerce\Internal\ProductAttributesLookup\LookupDataStore;
use Mewz\Framework\Base\Aspect;
use Mewz\QueryBuilder\DB;
use Mewz\WCAS\Aspects\Front\ProductLimits;
use Mewz\WCAS\Util\Attributes;
use Mewz\WCAS\Util\Limits;
use Mewz\WCAS\Util\Products;

class ProductAttributesLookup extends Aspect
{
	public $changed_products = [];

	public function __load()
	{
	    return apply_filters('mewz_wcas_attribute_lookup_table_compatibility', true);
	}

	public function __hooks()
	{
		add_action('shutdown', [$this, 'shutdown']);

		// update lookup table when doing a full table regeneration
		add_action('update_option_woocommerce_attribute_lookup_last_products_page_processed', [$this, 'after_regeneration_step_page'], 10, 3);
		add_action('update_option_woocommerce_attribute_lookup_processed_count', [$this, 'after_regeneration_step_processed'], 10, 3);

		// we should only update the lookup table when it's been explicitly enabled
		if (get_option('woocommerce_attribute_lookup_enabled') !== 'yes') {
			return;
		}

		// update lookup table when doing partial table regeneration
		if (get_option('woocommerce_attribute_lookup_direct_updates') === 'yes') {
			add_action('woocommerce_after_product_object_save', [$this, 'after_product_object_save'], 10, 2);
			add_action('untrashed_post', [$this, 'untrashed_post'], 10, 2);
		} else {
			add_action('woocommerce_run_product_attribute_lookup_update_callback', [$this, 'run_product_attribute_lookup_update'], 20, 2);
		}

		add_action('mewz_wcas_product_stock_affected', [$this, 'add_update_task']);
		add_action('mewz_wcas_task_update_product_attribute_lookup', [$this, 'task_update_product_attribute_lookup']);
	}

	public function after_product_object_save($product)
	{
		$this->changed_products[] = $product->get_id();
	}

	public function untrashed_post($post_id)
	{
	    $post_type = get_post_type($post_id);

		if (in_array($post_type, ['product', 'product_variation'])) {
			$this->changed_products[] = $post_id;
		}
	}

	public function run_product_attribute_lookup_update($product_id, $action)
	{
		if ($action === LookupDataStore::ACTION_DELETE || !Limits::product_limits_active()) {
			return;
		}

		$this->update_lookup_table_products($product_id);
	}

	public function shutdown()
	{
		global $plugin_page;

		if ($this->changed_products) {
			$this->add_update_task($this->changed_products);
		}

		if ($plugin_page === 'wc-status' && !empty($_REQUEST['regenerate_product_attribute_lookup_data_product_id'])) {
			if (!class_exists(ProductLimits::class, false)) {
				$this->plugin->aspects->load(ProductLimits::class);
			}

			$product_id = (int)$_REQUEST['regenerate_product_attribute_lookup_data_product_id'];

			$this->update_lookup_table_products($product_id);
		}
	}

	public function after_regeneration_step_page($prev, $page)
	{
		$page = (int)$page;

		if ($page <= 0 || !Limits::product_limits_active()) {
			return;
		}

		$product_ids = wc_get_products([
			'limit' => DataRegenerator::PRODUCTS_PER_GENERATION_STEP,
			'page' => $page,
			'orderby' => ['ID' => 'ASC'],
			'return' => 'ids',
			'suppress_filters' => true,
		]);

		if ($product_ids) {
			$this->update_lookup_table_products($product_ids);
		}
	}

	public function after_regeneration_step_processed($prev, $processed)
	{
		$page = ceil((int)$processed / DataRegenerator::PRODUCTS_PER_GENERATION_STEP);

		$this->after_regeneration_step_page(null, $page);
	}

	public function add_update_task($product_ids)
	{
		$this->tasks->add('update_product_attribute_lookup', ['product_ids' => implode(',', $product_ids)]);
	}

	public function task_update_product_attribute_lookup($data)
	{
		if (empty($data['product_ids'])) {
			return $this->tasks->kill(400);
		}

		if (!Limits::product_limits_active()) {
			return $this->tasks->kill(501);
		}

		if (!has_action('woocommerce_run_product_attribute_lookup_update_callback')) {
			return $this->tasks->kill(501);
		}

		$product_ids = array_keys(array_flip(explode(',', $data['product_ids'])));

		if (!$product_ids) {
			return $this->tasks->kill(400);
		}

		$this->update_lookup_table_products($product_ids);
	}

	public function update_lookup_table_products($product_ids)
	{
		$results = DB::table('wc_product_attributes_lookup')
			->select('product_id, product_or_parent_id AS parent_id, taxonomy, term_id, in_stock')
			->where('product_or_parent_id IN ? OR (product_id != product_or_parent_id AND product_id IN ?)', (array)$product_ids)
			->asc('product_id')
			->distinct()
			->get(ARRAY_A);

		if (!$results) return;

		$grouped = [];

		foreach ($results as $row) {
			$product_id = (int)$row['product_id'];

			if (!isset($grouped[$product_id]['parent_id'])) {
				$grouped[$product_id]['parent_id'] = (int)$row['parent_id'];
			}

			$grouped[$product_id]['rows'][] = $row;
		}

		$table = DB::prefix('wc_product_attributes_lookup');
		$update_query = "UPDATE $table SET in_stock = ? WHERE product_id = ? AND taxonomy = ? AND term_id = ?";
		$parent_attributes = [];

		mewz_wcas_log('Updating ' . count($grouped) . ' product(s) in attributes lookup table...');

		foreach ($grouped as $product_id => $data) {
			unset($grouped[$product_id]);

			$product = wc_get_product($product_id);
			if (!$product) continue;

			$parent_id = $data['parent_id'];
			$excluded = Products::is_product_excluded($product, $parent_id);

			foreach ($data['rows'] as $row) {
				if (!$excluded && $product instanceof \WC_Product_Variation) {
					$term_slug = Attributes::get_term_prop($row['term_id'], $row['taxonomy'], 'slug');
					$product_variation = $term_slug ? [$row['taxonomy'] => $term_slug] : [];
					$any_stock_limit = false;

					foreach ($product->get_attributes() as $attr_tax => $attr_term) {
						if ($attr_term === '' && ($attr_tax !== $row['taxonomy'] || !$term_slug)) {
							if (!isset($parent_attributes[$parent_id])) {
								$parent = wc_get_product($parent_id);
								if (!$parent) break;

								$attributes = [];

								foreach ($parent->get_attributes() as $attr) {
									$attributes[$attr->get_name()] = $attr;
								}

								$parent_attributes[$parent_id] = $attributes;
							}

							if (empty($parent_attributes[$parent_id][$attr_tax])) {
								continue;
							}

							if (is_object($parent_attributes[$parent_id][$attr_tax])) {
								$attributes = Attributes::sluggify_attributes([$attr_tax => $parent_attributes[$parent_id][$attr_tax]->get_options()]);
								$parent_attributes[$parent_id][$attr_tax] = $attributes[$attr_tax];
							}

							$product_variation[$attr_tax] = $parent_attributes[$parent_id][$attr_tax];
							$any_stock_limit = true;
						}
					}

					Products::set_prop($product, 'variation', $product_variation);

					if ($any_stock_limit) {
						Products::set_prop($product, 'any_stock_limit', true);
					}
				}

				$in_stock = $product->is_in_stock() ? '1' : '0';

				if ($in_stock !== $row['in_stock']) {
					DB::query($update_query, $in_stock, $product_id, $row['taxonomy'], (int)$row['term_id']);
					//do_action('woocommerce_run_product_attribute_lookup_update_callback', (int)$result['product_id'], LookupDataStore::ACTION_UPDATE_STOCK);
				}
			}

			if ($this->context->task && $this->tasks->near_limits()) {
				mewz_wcas_log('Server limits reached. Dispatching a new task for ' . count($grouped) . ' product(s)...');

				$this->add_update_task(array_keys($grouped));
				return;
			}
		}

		mewz_wcas_log('Update complete.');
	}
}
