<?php

namespace iwbvel\classes\services\product_update\variation_handlers;

defined('ABSPATH') || exit(); // Exit if accessed directly

use iwbvel\classes\repositories\Product;
use iwbvel\classes\services\background_process\VariationBackgroundProcess;
use iwbvel\classes\services\product_update\Variation_Update_Handler;

class Replace_Variation_Handler extends Variation_Update_Handler
{
    private $attribute_taxonomies;
    private $combinations;
    private $attributes;
    private $is_processing;
    private $product_repository;
    private $max_process_count;
    private $is_background_process;
    private $rest;
    private $product_ids;
    private $update_data;

    public function __construct()
    {
        $this->product_repository = Product::get_instance();
        $attribute_taxonomies = wc_get_attribute_taxonomies();
        $this->attribute_taxonomies = array_column($attribute_taxonomies, 'attribute_id', 'attribute_name');
        $this->max_process_count = 50;
        $this->rest = false;
    }

    public function is_processing()
    {
        return $this->is_processing;
    }

    public function update($product_ids, $update_data)
    {
        if (empty($product_ids) || !is_array($product_ids) || empty($update_data) || !is_array($update_data)) {
            return false;
        }

        $this->rest = (!empty($update_data['rest']) && $update_data['rest'] === true);
        $this->is_background_process = (!empty($update_data['background_process']) && $update_data['background_process'] === true);
        $this->product_ids = $product_ids;
        $this->update_data = $update_data;

        if (empty($this->update_data['value']['combinations']) || empty($this->update_data['value']['attributes'])) {
            return false;
        }

        $total_process = count($product_ids) * count($this->update_data['value']['combinations']);
        if ($total_process > $this->max_process_count && VariationBackgroundProcess::is_enable()) {
            $this->push_to_queue($total_process);
        } else {
            $this->do_update();
        }

        return true;
    }

    private function do_update()
    {
        foreach ($this->product_ids as $product_id) {
            $product = $this->product_repository->get_product(intval($product_id));
            if (!($product instanceof \WC_Product)) {
                continue;
            }

            if ($product instanceof \WC_Product_Variation) {
                continue;
            }

            if (isset($this->update_data['value']['product_type']) && $this->update_data['value']['product_type'] != $product->get_type()) {
                $product_classname = \WC_Product_Factory::get_product_classname($product->get_id(), sanitize_text_field($this->update_data['value']['product_type']));
                $product = new $product_classname($product->get_id());
                $product->save();
                $product = $this->product_repository->get_product(intval($product_id));
            }

            if (!$this->rest) {
                $this->attributes = $this->update_data['value']['attributes'];
                $this->attributes_update($product);
                $product = $this->product_repository->get_product(intval($product_id));
            }

            $this->combinations = $this->update_data['value']['combinations'];
            $this->variations_update($product);

            if ($this->is_background_process && count($this->combinations) < 1) {
                $background_process = VariationBackgroundProcess::get_instance();
                $background_process->add_completed_task(1);
            }
        }

        return true;
    }

    private function push_to_queue($total_process)
    {
        $this->is_processing = true;
        $background_process = VariationBackgroundProcess::get_instance();
        foreach ($this->product_ids as $product_id) {
            $product = $this->product_repository->get_product(intval($product_id));
            if (!($product instanceof \WC_Product)) {
                continue;
            }

            if ($product instanceof \WC_Product_Variation) {
                continue;
            }

            $combinations = array_chunk($this->update_data['value']['combinations'], $this->max_process_count);
            $i = 1;
            foreach ($combinations as $key => $items) {
                $background_process->push_to_queue([
                    'handler' => 'variation_replace',
                    'update_class' => self::class,
                    'product_id' => $product->get_id(),
                    'update_data' => [
                        'name' => 'bulk_variation_update',
                        'type' => 'variation',
                        'action' => 'replace_variations',
                        'rest' => ($i > 1),
                        'value' => [
                            'product_type' => 'variable',
                            'combinations' => $items,
                            'attributes' => $this->update_data['value']['attributes'],
                        ]
                    ]
                ]);
                $background_process->save();
                $i++;
            }
        }

        $background_process->set_total_tasks($total_process);
        $background_process->start();
    }

    private function attributes_update($product_object)
    {
        $product_attributes = [];
        foreach ($this->attributes as $name => $terms) {
            if (empty($terms['ids'])) {
                continue;
            }

            $attribute_name = str_replace('pa_', '', $name);
            if (!empty($this->attribute_taxonomies[$attribute_name])) {
                $new_object = new \WC_Product_Attribute();
                $new_object->set_id($this->attribute_taxonomies[$attribute_name]);
                $new_object->set_name($name);
                $new_object->set_options(array_map('intval', $terms['ids']));
                $new_object->set_position((!empty($product_attributes)) ? count($product_attributes) : 0);
                $new_object->set_visible(true);
                $new_object->set_variation(true);
                $product_attributes[$name] = $new_object;
            }

            $this->attributes[$name] = $terms['slugs'];
        }

        $product_object->set_attributes($product_attributes);
        return $product_object->save();
    }

    private function variations_update($product)
    {
        $old_variations = [];
        $product_children = $product->get_children();
        $product_attributes = $product->get_attributes();

        if (!empty($product_children) && is_array($product_children)) {
            foreach ($product_children as $variation_id) {
                $variation = $this->product_repository->get_product(intval($variation_id));
                if (!($variation instanceof \WC_Product_Variation)) {
                    continue;
                }

                $variation_attributes = $variation->get_attributes();
                if (!empty($variation_attributes)) {
                    foreach ($variation_attributes as $key => $value) {
                        if (empty($value)) {
                            if (!empty($product_attributes) && isset($product_attributes[$key])) {
                                $options = $product_attributes[$key]->get_options();
                                if (!empty($options[0])) {
                                    $term_object = get_term_by('term_id', intval($options[0]), $key);
                                    if ($term_object instanceof \WP_Term) {
                                        $variation_attributes[$key] = $term_object->slug;
                                    }
                                }
                            }
                        }
                    }
                    $old_variations[$variation->get_id()] = $variation_attributes;
                }
            }
        }

        $menu_order = 0;
        foreach ($this->combinations as $combination) {
            if (empty($combination) || !is_array($combination)) {
                continue;
            }

            $variation_object = null;

            if (!empty($old_variations)) {
                foreach ($old_variations as $variation_id => $items) {
                    if ($items == $combination) {
                        $variation_object = $this->product_repository->get_product(intval($variation_id));
                        if ($variation_object->get_status() == 'trash') {
                            $variation_object = null;
                        }
                    }
                }
            }

            if (empty($variation_object) || !($variation_object instanceof \WC_Product_Variation)) {
                $variation_object = new \WC_Product_Variation();
                $variation_object->set_parent_id($product->get_id());
            }

            $sanitized_attributes = [];
            foreach ($combination as $attr_name => $t_slug) {
                $sanitized_attributes[sanitize_title($attr_name)] = sanitize_title($t_slug);
            }

            $variation_object->set_attributes($sanitized_attributes);
            $variation_object->set_menu_order($menu_order);
            $variation_object->save();

            if (!empty($old_variations) && isset($old_variations[$variation_object->get_id()])) {
                unset($old_variations[$variation_object->get_id()]);
            }

            $menu_order++;

            if ($this->is_background_process) {
                $background_process = VariationBackgroundProcess::get_instance();
                $background_process->add_completed_task(1);
            }
        }

        if (!$this->rest) {
            if (!empty($old_variations)) {
                foreach (array_keys($old_variations) as $variation_id) {
                    wp_delete_post(intval($variation_id), true);
                }
            }
        }

        return $product->save();
    }
}
