<?php

namespace App\Http\Controllers\Admin;

use App\CategoryManufacturer;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Resources\Admin\ProductDetailResource;
use App\Http\Resources\Admin\ProductOptionResource;
use App\Http\Resources\Admin\ProductResourceDetail;
use App\Http\Resources\Admin\ProductRuleResource;
use App\Jobs\ProcessProductDetail;
use App\Jobs\ProcessProductRule;
use App\PricingRule;
use App\PricingRuleDetail;
use App\PricingRuleDetailItem;
use App\PricingRuleOption;
use App\ProductImage;
use App\ProductPurchaseMethod;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use App\Http\Resources\Admin\ProductResource;
use App\Http\Resources\Admin\ProductListResource;
use App\Product;
use App\Keyword;
use App\ProductDetail;
use App\ProductDetailOptionDetail;
use App\ProductKeyword;
use App\ProductOption;
use App\ProductOptionDetail;
use Validator;

/**
 * Class ProductController
 *
 * @package App\Http\Controllers
 */
class ProductController extends Controller
{
    const ITEM_PER_PAGE = 100;

    public function index(Request $request)
    {
        $searchParams = $request->all();
        $list = Product::notDeleted();
        $limit = Arr::get($searchParams, 'limit', static::ITEM_PER_PAGE);
        $keyword = Arr::get($searchParams, 'keyword', '');
        $status = Arr::get($searchParams, 'status', '');

        if (!empty($keyword)) {
            $list->where(function ($query) use ($keyword) {
                $query->where('name', 'LIKE', '%' . $keyword . '%')
                    ->orWhere('id', '=', $keyword)
                    ->orWhere('sku', 'LIKE', '%' . $keyword . '%')
                    ->orWhere('slug', 'LIKE', '%' . $keyword . '%')
                    ->orWhere('price_from', 'LIKE', '%' . $keyword . '%')
                    ->orWhere('price_to', 'LIKE', '%' . $keyword . '%')
                    ->orWhere('discount', 'LIKE', '%' . $keyword . '%');
            });
        }
        if ($status != '') {
            $list->where('is_activated', $status);
        }
        $list->orderBy('id', 'DESC');

        return ProductListResource::collection($list->paginate($limit));
    }

    public function all(Request $request)
    {
        $searchParams = $request->all();
        $limit = Arr::get($searchParams, 'limit', static::ITEM_PER_PAGE);
        $list = Product::select('id', 'name', 'slug')->notDeleted()->orderBy('position')->orderBy('id');

        return ProductResource::collection($list->paginate($limit));
    }

    public function available()
    {
        $list = Product::select('id', 'name')->notDeleted()->activated()->orderBy('position')->orderBy('id');

        return ProductResource::collection($list->get());
    }

    public function show($id = 0)
    {
        $product = Product::notDeleted()->where('id', $id)->first();
        if (!isset($product)) return response()->json(['errors' => 'Product is not valid'], 403);

        return new ProductResourceDetail($product);
    }

    public function showDetail(Request $request, $id = 0)
    {
        $params = $request->all();
        $limit = Arr::get($params, 'limit', static::ITEM_PER_PAGE);
        $list = ProductDetail::where('product_id', $id);

        return ProductDetailResource::collection($list->paginate($limit));
    }

    public function showOption(Request $request, $id = 0)
    {
        $params = $request->all();
        $limit = Arr::get($params, 'limit', static::ITEM_PER_PAGE);
        $list = ProductOption::where('product_id', $id);

        return ProductOptionResource::collection($list->paginate($limit));
    }

    public function showRule(Request $request, $id = 0)
    {
        $params = $request->all();
        $limit = Arr::get($params, 'limit', static::ITEM_PER_PAGE);
        $list = PricingRule::where('product_id', $id);

        return ProductRuleResource::collection($list->paginate($limit));
    }

    public function store(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'name'         => ['required'],
            'sku'          => ['required'],
            'category_id'  => ['required'],
            'is_activated' => ['required']
        ]);
        if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);
        $params = $request->all();

        $categoryId = null;
        $selectedCategories = $params['category_id'];
        if (is_array($selectedCategories) && isset($selectedCategories[array_key_last($selectedCategories)])) {
            $categoryId = $selectedCategories[array_key_last($selectedCategories)];
        } else {
            $categoryId = $selectedCategories;
        }

        $priceFrom = $params['regular_price_from'];
        $priceTo = $params['regular_price_to'];
        if ($params['discount'] != null && $params['discount'] != "" && $params['discount'] > 0) {
            $priceFrom = $priceFrom - ($priceFrom * $params['discount'] / 100);
            $priceTo = $priceTo - ($priceTo * $params['discount'] / 100);
        }

        $defaultPrice = ($params['default_price'] != "") ? $params['default_price'] : $priceFrom;
        $listOptionIds = [];
        $listOptionDetailIds = [];

        $product = Product::create([
            'category_id'               => $categoryId,
            'manufacturer_id'           => $params['manufacturer_id'],
            'sku'                       => trim($params['sku']),
            'name'                      => trim($params['name']),
            'slug'                      => Helper::slug(trim($params['name'])),
            'image'                     => $params['image'],
            'description'               => $params['description'],
            'regular_price_from'        => $params['regular_price_from'],
            'regular_price_to'          => $params['regular_price_to'],
            'default_price'             => $defaultPrice,
            'standard_construction_fee' => $params['standard_construction_fee'],
            'shipping_cost'             => $params['shipping_cost'],
            'discount'                  => $params['discount'],
            'price_from'                => $priceFrom,
            'price_to'                  => $priceTo,
            'note'                      => $params['note'],
            'policy'                    => $params['policy'],
            'rank'                      => ($params['rank'] != "" && $params['rank'] != null) ? $params['rank'] : null,
            'possible_delivery_time'    => $params['possible_delivery_time'],
            'is_available_in_stock'     => ($params['is_available_in_stock'] === true) ? 1 : 0,
            'is_pricing_manual'         => ($params['is_pricing_manual'] === true) ? 1 : 0,
            'is_activated'              => ($params['is_activated'] === true) ? 1 : 0,
            'is_deleted'                => true,
            'created_at'                => date('Y-m-d H:i:s'),
            'updated_at'                => date('Y-m-d H:i:s')
        ]);

        //Options
        foreach ($params['options'] as $option) {
            $productOption = ProductOption::create([
                'product_id'       => $product->id,
                'name'             => $option['name'],
                'slug'             => Helper::slug($option['name']),
                'title'            => $option['name'],
                'description'      => $option['description'],
                'position'         => 0,
                'allow_option'     => 1,
                'is_template'      => ($option['is_template'] === true) ? 1 : 0,
                'is_check_all'     => $option['is_check_all'],
                'is_indeterminate' => $option['is_indeterminate'],
                'created_at'       => date('Y-m-d H:i:s'),
                'updated_at'       => date('Y-m-d H:i:s')
            ]);

            $listOptionIds[$option['id']] = $productOption->id;

            foreach ($option['details'] as $oItem) {
                $poDetail = ProductOptionDetail::create([
                    'product_option_id' => $productOption->id,
                    'name'              => $oItem['name'],
                    'slug'              => Helper::slug($oItem['name']),
                    'image'             => $oItem['image'],
                    'position'          => 0,
                    'is_check'          => $oItem['is_check'],
                    'created_at'        => date('Y-m-d H:i:s'),
                    'updated_at'        => date('Y-m-d H:i:s')
                ]);

                $listOptionDetailIds[$oItem['id']] = $poDetail->id;
            }
        }

        //Images
        foreach ($params['sliders'] as $item) {
            ProductImage::create([
                'product_id' => $product->id,
                'name'       => $item,
                'file_name'  => $item,
                'position'   => 0,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s')
            ]);
        }

        //ProductPurchaseMethod
        foreach ($params['purchase_method'] as $item) {
            ProductPurchaseMethod::create([
                'product_id' => $product->id,
                'name'       => $item,
                'slug'       => $item,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s')
            ]);
        }

        //Keyword
        $keywords = [];
        foreach ($params['keyword'] as $item) {
            $tag = trim($item['text']);
            $checkKeyword = Keyword::where('is_deleted', 0)
                ->where(function ($query) use ($tag) {
                    $query->where('name', $tag)
                        ->orWhere('name', strtolower($tag))
                        ->orWhere('name', strtoupper($tag))
                        ->orWhere('name', ucfirst($tag));
                })->first();

            if (!$checkKeyword) {
                $checkKeyword = Keyword::create([
                    'name'         => $tag,
                    'is_activated' => true,
                    'is_deleted'   => false,
                    'created_at'   => date('Y-m-d H:i:s'),
                    'updated_at'   => date('Y-m-d H:i:s'),
                ]);
            }

            $keywords[] = [
                'keyword_id' => $checkKeyword->id,
                'product_id' => $product->id,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ];
        }
        if (count($keywords) > 0) ProductKeyword::insert($keywords);

        //Mapping Category with Manufacturer
        $checkCategoryManufacturer = CategoryManufacturer::where('category_id', $categoryId)
            ->where('manufacturer_id', $params['manufacturer_id'])
            ->first();
        if (!$checkCategoryManufacturer) {
            CategoryManufacturer::create([
                'category_id'     => $categoryId,
                'manufacturer_id' => $params['manufacturer_id'],
                'created_at'      => date('Y-m-d H:i:s'),
                'updated_at'      => date('Y-m-d H:i:s')
            ]);
        }

        //Detail
        ProcessProductDetail::dispatch($product)->onQueue(config('queue.connections.redis.queue'));

        return response()->json([
            'status'              => 'success',
            'id'                  => $product->id,
            'listOptionIds'       => $listOptionIds,
            'listOptionDetailIds' => $listOptionDetailIds,
        ], 200);
    }

    public function storeRule(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'productId' => ['required'],
        ]);
        if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);

        $params = $request->all();
        PricingRule::where('product_id', $params['productId'])->delete();
        ProcessProductRule::dispatch($params)->onQueue(config('queue.connections.redis.queue'));
        Product::where('id', $params['productId'])->update(['is_deleted' => false]);

        return response()->json(null, 200);
    }

    public function update(Request $request, $id = 0)
    {
        $validator = Validator::make($request->all(), [
            'name'         => ['required'],
            'sku'          => ['required'],
            'category_id'  => ['required'],
            'is_activated' => ['required']
        ]);
        if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);
        $product = Product::notDeleted()->where('id', $id)->first();
        if (!isset($product)) return response()->json(['errors' => 'Product is not valid'], 403);
        $params = $request->all();

        $categoryId = null;
        $selectedCategories = $params['category_id'];
        if (is_array($selectedCategories) && isset($selectedCategories[array_key_last($selectedCategories)])) {
            $categoryId = $selectedCategories[array_key_last($selectedCategories)];
        } else {
            $categoryId = $selectedCategories;
        }

        $priceFrom = $params['regular_price_from'];
        $priceTo = $params['regular_price_to'];
        if ($params['discount'] != null && $params['discount'] != "" && $params['discount'] > 0) {
            $priceFrom = $priceFrom - ($priceFrom * $params['discount'] / 100);
            $priceTo = $priceTo - ($priceTo * $params['discount'] / 100);
        }

        $defaultPrice = ($params['default_price'] != "") ? $params['default_price'] : $priceFrom;
        $listOptionIds = [];
        $listOptionDetailIds = [];

        $product->update([
            'category_id'               => $categoryId,
            'manufacturer_id'           => $params['manufacturer_id'],
            'name'                      => trim($params['name']),
            'slug'                      => Helper::slug(trim($params['name'])),
            'image'                     => $params['image'],
            'description'               => $params['description'],
            'regular_price_from'        => $params['regular_price_from'],
            'regular_price_to'          => $params['regular_price_to'],
            'default_price'             => $defaultPrice,
            'standard_construction_fee' => $params['standard_construction_fee'],
            'shipping_cost'             => $params['shipping_cost'],
            'discount'                  => $params['discount'],
            'price_from'                => $priceFrom,
            'price_to'                  => $priceTo,
            'note'                      => $params['note'],
            'policy'                    => $params['policy'],
            'rank'                      => ($params['rank'] != "" && $params['rank'] != null) ? $params['rank'] : null,
            'possible_delivery_time'    => $params['possible_delivery_time'],
            'is_available_in_stock'     => ($params['is_available_in_stock'] === true) ? 1 : 0,
            'is_activated'              => ($params['is_activated'] === true) ? 1 : 0,
            'is_pricing_manual'         => ($params['is_pricing_manual'] === true) ? 1 : 0,
            'updated_at'                => date('Y-m-d H:i:s')
        ]);

        //Options
        $productOptionIds = ProductOption::where('product_id', $product->id)->pluck('id')->toArray();
        ProductOptionDetail::whereIn('product_option_id', $productOptionIds)->delete();
        ProductOption::where('product_id', $product->id)->delete();
        foreach ($params['options'] as $option) {
            $productOption = ProductOption::create([
                'product_id'       => $product->id,
                'name'             => $option['name'],
                'slug'             => Helper::slug($option['name']),
                'title'            => $option['name'],
                'description'      => $option['description'],
                'position'         => 0,
                'allow_option'     => 1,
                'is_template'      => ($option['is_template'] === true) ? 1 : 0,
                'is_check_all'     => $option['is_check_all'],
                'is_indeterminate' => $option['is_indeterminate'],
                'created_at'       => date('Y-m-d H:i:s'),
                'updated_at'       => date('Y-m-d H:i:s')
            ]);

            $listOptionIds[$option['id']] = $productOption->id;

            foreach ($option['details'] as $oItem) {
                $poDetail = ProductOptionDetail::create([
                    'product_option_id' => $productOption->id,
                    'name'              => $oItem['name'],
                    'slug'              => Helper::slug($oItem['name']),
                    'image'             => $oItem['image'],
                    'position'          => 0,
                    'is_check'          => $oItem['is_check'],
                    'created_at'        => date('Y-m-d H:i:s'),
                    'updated_at'        => date('Y-m-d H:i:s')
                ]);

                $listOptionDetailIds[$oItem['id']] = $poDetail->id;
            }
        }

        //Images
        ProductImage::where('product_id', $product->id)->delete();
        foreach ($params['sliders'] as $item) {
            ProductImage::create([
                'product_id' => $product->id,
                'name'       => $item,
                'file_name'  => $item,
                'position'   => 0,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s')
            ]);
        }

        //ProductPurchaseMethod
        ProductPurchaseMethod::where('product_id', $product->id)->delete();
        foreach ($params['purchase_method'] as $item) {
            ProductPurchaseMethod::create([
                'product_id' => $product->id,
                'name'       => $item,
                'slug'       => $item,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s')
            ]);
        }

        //Keyword
        $keywords = [];
        ProductKeyword::where('product_id', $product->id)->delete();
        foreach ($params['keyword'] as $item) {
            $tag = trim($item['text']);
            $checkKeyword = Keyword::where('is_deleted', 0)
                ->where(function ($query) use ($tag) {
                    $query->where('name', $tag)
                        ->orWhere('name', strtolower($tag))
                        ->orWhere('name', strtoupper($tag))
                        ->orWhere('name', ucfirst($tag));
                })->first();

            if (!$checkKeyword) {
                $checkKeyword = Keyword::create([
                    'name'         => $tag,
                    'is_activated' => true,
                    'is_deleted'   => false,
                    'created_at'   => date('Y-m-d H:i:s'),
                    'updated_at'   => date('Y-m-d H:i:s'),
                ]);
            }

            $keywords[] = [
                'keyword_id' => $checkKeyword->id,
                'product_id' => $product->id,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ];
        }
        if (count($keywords) > 0) ProductKeyword::insert($keywords);

        //Mapping Category with Manufacturer
        $checkCategoryManufacturer = CategoryManufacturer::where('category_id', $categoryId)
            ->where('manufacturer_id', $params['manufacturer_id'])
            ->first();
        if (!$checkCategoryManufacturer) {
            CategoryManufacturer::create([
                'category_id'     => $categoryId,
                'manufacturer_id' => $params['manufacturer_id'],
                'created_at'      => date('Y-m-d H:i:s'),
                'updated_at'      => date('Y-m-d H:i:s')
            ]);
        }

        //Detail
        ProcessProductDetail::dispatch($product)->onQueue(config('queue.connections.redis.queue'));

        return response()->json([
            'status'              => 'success',
            'id'                  => $product->id,
            'listOptionIds'       => $listOptionIds,
            'listOptionDetailIds' => $listOptionDetailIds,
        ], 200);
    }

    public function checkSku(Request $request, $sku = '')
    {
        if ($sku == '' || $sku == null) return response()->json(['status' => 'error'], 200);
        $params = $request->all();
        $id = Arr::get($params, 'id', "");

        if (!empty($id)) {
            $checkProduct = Product::where('id', $id)->where('sku', $sku)->count();
            if ($checkProduct == 1) {
                return response()->json(['status' => 'success'], 200);
            } else {
                $checkProduct = Product::where('id', '!=', $id)->where('sku', $sku)->count();
                if ($checkProduct > 0) {
                    return response()->json(['status' => 'error'], 200);
                } else {
                    return response()->json(['status' => 'success'], 200);
                }
            }
        } else {
            $checkProduct = Product::where('sku', $sku)->count();
            if ($checkProduct > 0) {
                return response()->json(['status' => 'error'], 200);
            } else {
                return response()->json(['status' => 'success'], 200);
            }
        }
    }

    public function destroy($id = 0)
    {
        $product = Product::notDeleted()->where('id', $id)->first();
        if (!isset($product)) response()->json(['error' => 'Ehhh! Can not delete this product'], 403);

        try {
            $product->update(['is_deleted' => true]);
        } catch (\Exception $ex) {
            response()->json(['error' => $ex->getMessage()], 403);
        }

        return response()->json(null, 204);
    }

    public function destroyMultiple(Request $request)
    {
        $validator = Validator::make($request->all(), ['ids' => 'required']);
        if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);
        $listIds = $request->get('ids', []);
        $categories = Product::notDeleted()->whereIn('id', $listIds)->get();
        if ($categories->count() <= 0) response()->json(['error' => 'Product is not valid.'], 403);
        try {
            Product::notDeleted()->whereIn('id', $listIds)->update(['is_deleted' => true]);
        } catch (\Exception $ex) {
            response()->json(['error' => $ex->getMessage()], 403);
        }

        return response()->json(null, 204);
    }

    public function activateMultiple(Request $request)
    {
        $validator = Validator::make($request->all(), ['ids' => 'required']);
        if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);
        $listIds = $request->get('ids', []);
        $categories = Product::notDeleted()->whereIn('id', $listIds)->get();
        if ($categories->count() <= 0) response()->json(['error' => 'Product is not valid.'], 403);
        try {
            Product::notDeleted()->whereIn('id', $listIds)->update(['is_activated' => true]);
        } catch (\Exception $ex) {
            response()->json(['error' => $ex->getMessage()], 403);
        }

        return response()->json(null, 204);
    }

    public function deactivateMultiple(Request $request)
    {
        $validator = Validator::make($request->all(), ['ids' => 'required']);
        if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);
        $listIds = $request->get('ids', []);
        $categories = Product::notDeleted()->whereIn('id', $listIds)->get();
        if ($categories->count() <= 0) response()->json(['error' => 'Product is not valid.'], 403);
        try {
            Product::notDeleted()->whereIn('id', $listIds)->update(['is_activated' => false]);
        } catch (\Exception $ex) {
            response()->json(['error' => $ex->getMessage()], 403);
        }

        return response()->json(null, 204);
    }

    public function optionsTemplate()
    {
        $list = ProductOption::with('details')
            ->select('id', 'name', 'slug', 'title', 'description')
            ->where('is_template', true)
            ->limit(50)
            ->orderBy('id')
            ->get();

        return response()->json(['data' => $list], 200);
    }

    public function optionsTemplateDestroy()
    {
        ProductOption::where('is_template', true)->update(['is_template' => false]);

        return response()->json(['data' => ['status' => 'success', 'message' => '']], 200);
    }

    private function _updatePricingList($productId)
    {
        $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']]);
        }
    }

    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;
    }
}
