<?php

namespace App\Jobs;

use App\PricingRule;
use App\PricingRuleDetail;
use App\PricingRuleDetailItem;
use App\PricingRuleOption;
use App\Product;
use App\ProductDetail;
use App\ProductDetailOptionDetail;
use App\ProductOption;
use App\ProductOptionDetail;
use Illuminate\Bus\Queueable;
use Illuminate\Bus\Batchable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class ProcessProductRule implements ShouldQueue
{
    use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $params;

    /**
     * The number of seconds the job can run before timing out.
     *
     * @var int
     */
    public $timeout = 3600;

    /**
     * Indicate if the job should be marked as failed on timeout.
     *
     * @var bool
     */
    public $failOnTimeout = false;

    /**
     * Create a new job instance.
     *
     * @param $params
     */
    public function __construct($params)
    {
        $this->onQueue(config('queue.connections.redis.queue'));
        $this->params = $params;
    }

    /**
     * @return bool
     */
    public function isFailOnTimeout(): bool
    {
        return $this->failOnTimeout;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        try {
            $productId = $this->params['productId'];
            $listOptionIds = $this->params['listOptionIds'];
            $listOptionDetailIds = $this->params['listOptionDetailIds'];
            $pricingRulesData = $this->params['pricingRulesData'];

            foreach ($pricingRulesData as $item) {
                $pricingRule = PricingRule::create([
                    'product_id' => $productId,
                    'price'      => $item['price'],
                    'created_at' => date('Y-m-d H:i:s'),
                    'updated_at' => date('Y-m-d H:i:s')
                ]);

                $listGroup = [];
                foreach ($item['options'] as $subItem) {
                    PricingRuleOption::create([
                        'pricing_rule_id'  => $pricingRule->id,
                        'option_id'        => $listOptionIds[$subItem['id']],
                        'option_detail_id' => ($subItem['value'] != "" && isset($listOptionDetailIds[$subItem['value']])) ? $listOptionDetailIds[$subItem['value']] : null,
                        'created_at'       => date('Y-m-d H:i:s'),
                        'updated_at'       => date('Y-m-d H:i:s')
                    ]);

                    $groupItems = [];
                    if ($subItem['value'] == null || $subItem['value'] == "") {
                        $groupItems = ProductOptionDetail::where('product_option_id', $listOptionIds[$subItem['id']])->pluck('id')->toArray();
                    } else {
                        $groupItems[] = $listOptionDetailIds[$subItem['value']];
                    }
                    if (count($groupItems) > 0) $listGroup[] = $groupItems;
                }

                $strPriceArr = [];
                $strPriceList = explode("|", $item['list']);
                foreach ($strPriceList as $stIt) {
                    $a = [];
                    if ($stIt != null && $stIt != "") {
                        $pIt = explode("__", $stIt);
                        $a['price'] = $pIt[1];
                        $oArr = explode("_", $pIt[0]);
                        foreach ($oArr as $oVal) {
                            $a['options'][] = $listOptionDetailIds[$oVal];
                        }
                    }
                    if (count($a) > 0) $strPriceArr[] = $a;
                }

                $prList = [];
                $rs = $this->combinations($listGroup);
                foreach ($rs as $rIt) {
                    $rItPrice = null;
                    foreach ($strPriceArr as $aIt) {
                        $checkInclude = true;
                        foreach ($rIt as $bIt) {
                            if (!in_array($bIt, $aIt['options'])) {
                                $checkInclude = false;
                            }
                        }
                        if ($checkInclude) {
                            $rItPrice = $aIt['price'];
                        }
                    }

                    $prList[] = [
                        'price'        => $rItPrice,
                        'option_items' => $rIt,
                    ];
                }

                //Import rule item
                foreach ($prList as $pIt) {
                    $pDetail = PricingRuleDetail::create([
                        'pricing_rule_id' => $pricingRule->id,
                        'price'           => ($pIt['price'] != null && $pIt['price'] != "" && $pIt['price'] != "null") ? $pIt['price'] : null,
                        'created_at'      => date('Y-m-d H:i:s'),
                        'updated_at'      => date('Y-m-d H:i:s')
                    ]);

                    foreach ($pIt['option_items'] as $pOption) {
                        PricingRuleDetailItem::create([
                            'pricing_rule_detail_id' => $pDetail->id,
                            'product_option_id'      => $pOption,
                            'created_at'             => date('Y-m-d H:i:s'),
                            'updated_at'             => date('Y-m-d H:i:s')
                        ]);
                    }
                }
            }

            //Update Price
            $product = Product::where('id', $productId)->first();
            $productDetails = ProductDetail::with('option_detail')->where('product_id', $product->id)->get();
            $priceRules = PricingRule::with('detail', 'pricing')->where('product_id', $product->id)->get();
            $defaultPrice = $product->default_price;
            $listProducts = [];
            $listRule = [];

            foreach ($productDetails as $item) {
                $optionDetails = [];
                foreach ($item->option_detail as $subItem) {
                    $optionDetails[] = $subItem->product_option_detail_id;
                }
                $listProducts[] = [
                    'id'             => $item->id,
                    'price'          => $defaultPrice,
                    'option_details' => $optionDetails
                ];
            }

            foreach ($priceRules as $rItem) {
                $price = (!empty($rItem->price)) ? $rItem->price : $defaultPrice;
                $groups = [];
                foreach ($rItem->pricing as $pItem) {
                    $pOptions = [];
                    foreach ($pItem->options as $oItem) {
                        $pOptions[] = $oItem->product_option_id;
                    }
                    $groups[] = [
                        'id'      => $pItem->id,
                        'price'   => ($pItem->price != "" && $pItem->price != null) ? $pItem->price : $price,
                        'options' => $pOptions,
                    ];
                }

                $listRule[] = [
                    'id'     => $rItem->id,
                    'price'  => $price,
                    'groups' => $groups,
                ];
            }

            foreach ($listRule as $item) {
                foreach ($item['groups'] as $subItem) {
                    $iPrice = $subItem['price'];
                    foreach ($listProducts as $lKey => $lItem1) {
                        $check = true;
                        foreach ($subItem['options'] as $optionDetailId) {
                            if (!in_array($optionDetailId, $lItem1['option_details'])) $check = false;
                        }
                        if ($check) $listProducts[$lKey]['price'] = $iPrice;
                    }
                }
            }

            foreach ($listProducts as $item) {
                ProductDetail::where('id', $item['id'])->update(['price' => $item['price']]);
            }
        } catch (\Exception $exception) {
            logger("Update product rule error: " . $exception->getMessage());
        }
    }

    private function combinations($arrays, $i = 0)
    {
        if (!isset($arrays[$i])) {
            return array();
        }

        if ($i == count($arrays) - 1) {
            return $arrays[$i];
        }

        $tmp = $this->combinations($arrays, $i + 1);
        $result = array();

        foreach ($arrays[$i] as $v) {
            foreach ($tmp as $t) {
                $result[] = is_array($t) ? array_merge(array($v), $t) : array($v, $t);
            }
        }

        return $result;
    }
}
