<?php

namespace iwbvel\classes\services\product_update\variation_handlers;

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

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

class Swap_Variation_Handler extends Variation_Update_Handler
{
    private $is_processing;
    private $max_process_count;
    private $product_ids;
    private $update_data;

    public function __construct()
    {
        $this->max_process_count = 10;
    }

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

    public function update($product_ids, $update_data)
    {
        if (empty($update_data['value']['attribute']) || empty($update_data['value']['from_term']) || empty($update_data['value']['to_term'])) {
            return false;
        }

        $this->product_ids = $product_ids;
        $this->update_data = $update_data;

        if (count($product_ids) > $this->max_process_count && VariationBackgroundProcess::is_enable()) {
            $this->push_to_queue();
        } else {
            $this->do_swap();
        }

        return true;
    }

    private function push_to_queue()
    {
        $background_process = VariationBackgroundProcess::get_instance();
        $this->is_processing = true;
        $i = 1;
        foreach ($this->product_ids as $product_id) {
            $background_process->push_to_queue([
                'handler' => 'variation_swap',
                'update_class' => self::class,
                'variable_id' => intval($product_id),
                'update_data' => $this->update_data
            ]);
            if ($i == $this->max_process_count || $i == count($this->product_ids)) {
                $background_process->save();
                $i = 0;
            }
            $i++;
        }

        $background_process->set_total_tasks(count($this->product_ids));
        $background_process->start();
    }

    private function do_swap()
    {
        $history_repository = History::get_instance();
        foreach ($this->product_ids as $product_id) {
            $product = wc_get_product(intval($product_id));
            if (!($product instanceof \WC_Product_Variable)) {
                continue;
            }

            $attribute_name = sanitize_text_field($this->update_data['value']['attribute']);
            $from_term = intval($this->update_data['value']['from_term']);
            $to_term = intval($this->update_data['value']['to_term']);
            $product_repository = Product::get_instance();
            $product_variations = (!empty($this->update_data['value']['variation_ids']) && is_array($this->update_data['value']['variation_ids'])) ? array_map('intval', $this->update_data['value']['variation_ids']) : $product_repository->get_product_variation_ids($product->get_id());

            if (!empty($this->update_data['history_id'])) {
                $prev_value = [
                    'variation_ids' => $this->update_data['value']['variation_ids'],
                    'attribute' => $attribute_name,
                    'from_term' => $to_term,
                    'to_term' => $from_term,
                ];
            }

            // update product attribute
            $this->attribute_update($product, [
                'variation_ids' => $this->update_data['value']['variation_ids'],
                'attribute_name' => $attribute_name,
                'from_term' => $from_term,
                'to_term' => $to_term,
            ]);

            // update variations
            if (!empty($product_variations)) {
                foreach ($product_variations as $variation_id) {
                    $variation = wc_get_product(intval($variation_id));
                    if (!($variation instanceof \WC_Product_Variation)) {
                        continue;
                    }

                    $variation_attributes = $variation->get_attributes();
                    if (!isset($variation_attributes[strtolower(urlencode($attribute_name))])) {
                        continue;
                    }

                    $from_term_object = get_term_by('term_id', $from_term, $attribute_name);
                    $to_term_object = get_term_by('term_id', $to_term, $attribute_name);
                    if (!($from_term_object instanceof \WP_Term) || !($to_term_object instanceof \WP_Term)) {
                        continue;
                    }

                    if ($variation_attributes[strtolower(urlencode($attribute_name))] == $from_term_object->slug) {
                        $variation_attributes[strtolower(urlencode($attribute_name))] = $to_term_object->slug;
                        $variation->set_attributes($variation_attributes);
                        $variation->save();
                    }
                }
            }

            if (!empty($this->update_data['history_id'])) {
                $history_repository->save_history_item([
                    'history_id' => intval($this->update_data['history_id']),
                    'historiable_id' => intval($product->get_id()),
                    'name' => 'swap_terms',
                    'sub_name' => '',
                    'type' => 'variation',
                    'action' => 'swap_variations',
                    'prev_value' =>  $prev_value,
                    'new_value' => $this->update_data['value'],
                    'prev_total_count' => 1,
                    'new_total_count' => 1,
                ]);
            }

            if (isset($this->update_data['background_process']) && $this->update_data['background_process'] === true) {
                $background_process = VariationBackgroundProcess::get_instance();
                $background_process->add_completed_task(1);
            }
        }
    }

    private function attribute_update($product, $data)
    {
        $product_attributes = $product->get_attributes();
        $attribute_object = $product_attributes[$data['attribute_name']];
        if (empty($attribute_object) || !($attribute_object instanceof \WC_Product_Attribute)) {
            return false;
        }

        $attribute_options = $attribute_object->get_options();
        $term_key = array_search($data['from_term'], $attribute_options);
        if ($term_key === false) {
            return false;
        }

        $attribute_options[$term_key] = $data['to_term'];

        $new_object = new \WC_Product_Attribute();
        $new_object->set_id($attribute_object->get_id());
        $new_object->set_name($attribute_object->get_name());
        $new_object->set_options(array_unique($attribute_options));
        $new_object->set_position($attribute_object->get_position());
        $new_object->set_visible(true);
        $new_object->set_variation(true);

        $product_attributes[$data['attribute_name']] = $new_object;
        $product->set_attributes($product_attributes);
        return $product->save();
    }
}
