<?php

namespace App\Http\Controllers;

use App\FaceSwapJob;
use App\Helpers\Helper;
use App\Http\Resources\ProductItemDetailResource;
use App\Jobs\ProcessFaceSwap;
use App\Jobs\ProcessFaceSwapInsightface;
use App\Keyword;
use App\Laravue\Acl;
use App\ProductCategory;
use App\ProductColor;
use App\ProductDetail;
use App\ProductImage;
use App\ProductKeyword;
use App\ProductSchedule;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use App\Http\Resources\ProductResource;
use App\Http\Resources\ProductDetailResource;
use App\Product;
use App\Category;
use DB;
use Validator;

class ProductController extends Controller
{
	const ITEM_PER_PAGE = 25;
	private $_user = null;
	
	public function __construct()
	{
		$this->_user = auth('api')->user();
	}
	
	public function index(Request $request)
	{
		$searchParams = $request->all();
		$limit = Arr::get($searchParams, 'limit', static::ITEM_PER_PAGE);
		$keyword = Arr::get($searchParams, 'keyword', '');
		$type = Arr::get($searchParams, 'type', '');
		$gender = Arr::get($searchParams, 'gender', '');
		$categoryIds = Arr::get($searchParams, 'category', null);
		$colorIds = Arr::get($searchParams, 'color', null);
		$priceFrom = Arr::get($searchParams, 'price_from', '');
		$priceTo = Arr::get($searchParams, 'price_to', '');
		$filterData = Arr::get($searchParams, 'filter', "");
		$sortBy = Arr::get($searchParams, 'sort_by', 'id');
		$sortType = Arr::get($searchParams, 'sort_type', 'asc');
		$ids = Arr::get($searchParams, 'ids', []);
		
		$list = Product::select('*')->isPublished()->where('type', 0)->whereIn('order_status', [1, 2]);
		
		if (!empty($keyword)) {
			$list->where('name', 'LIKE', '%' . $keyword . '%');
		}
		
		if (!empty($type) && in_array($type, [1, 2, 3])) {
			$list->where('type', $type);
		}
		
		if (!empty($gender) && in_array($gender, [0, 1])) {
			$list->where('gender', 1);
		}
		
		if (isset($categoryIds) && is_array($categoryIds)) {
			$productIds = ProductCategory::whereIn('category_id', $categoryIds)->pluck('product_id')->toArray();
			$list->whereIn('id', $productIds);
		}
		
		if (isset($colorIds) && is_array($colorIds)) {
			$productIds = ProductColor::whereIn('color_id', $colorIds)->pluck('product_id')->toArray();
			$list->whereIn('id', $productIds);
		}
		
		if (!empty($priceFrom)) {
			$list->where('default_price', '>=', $priceFrom);
		}
		
		if (!empty($priceTo)) {
			$list->where('default_price', '<=', $priceTo);
		}
		
		if (isset($ids) && is_array($ids) && count($ids) > 0) {
			$list->whereIn('id', $ids);
		}
		
		if (!empty($filterData)) {
			$filterData = json_decode($filterData, true);
			if (isset($filterData['scene']) && count($filterData['scene']) > 0) {
				$list->whereIn('scene_id', $filterData['scene']);
			}
			if (isset($filterData['size']) && count($filterData['size']) > 0) {
				$list->whereIn('size', $filterData['size']);
			}
			
			if (isset($filterData['price_from']) && $filterData['price_from'] != "") {
				$list->where('default_price', '>=', (int)$filterData['price_from']);
			}

			if (isset($filterData['price_to']) && $filterData['price_to'] != "") {
				$list->where('default_price', '<=', (int)$filterData['price_to']);
			}
			
			$dateFrom = isset($filterData['date_from']) && $filterData['date_from'] != "" ? $filterData['date_from'] : null;
			$dateTo = isset($filterData['date_to']) && $filterData['date_to'] != "" ? $filterData['date_to'] : null;

			if ($dateFrom || $dateTo) {
				$storageLimit = $dateFrom ?: $dateTo;
				$list->where(function($q) use ($storageLimit) {
					$q->whereNull('storage_date')
						->orWhere('storage_date', '<=', $storageLimit);
				});
			}

			if ($dateFrom && $dateTo) {
				$scheduleFilterIds = ProductSchedule::where('short_date', '>=', $dateFrom)
					->where('short_date', '<=', $dateTo)
					->pluck('product_id')->toArray();
				$list->whereNotIn('id', $scheduleFilterIds);
			}
			
			if (isset($filterData['category']) && count($filterData['category']) > 0) {
				$categoryFilterIds = ProductCategory::whereIn('category_id', $filterData['category'])->pluck('product_id')->toArray();
				$list->whereIn('id', $categoryFilterIds);
			}
			
			if (isset($filterData['color']) && count($filterData['color']) > 0) {
				$colorFilterIds = ProductColor::whereIn('color_id', $filterData['color'])->pluck('product_id')->toArray();
				$list->whereIn('id', $colorFilterIds);
			}
			
			if (isset($filterData['keyword']) && count($filterData['keyword']) > 0) {
				$keywordFilterIds = ProductKeyword::whereIn('keyword_id', $filterData['keyword'])->pluck('product_id')->toArray();
				$list->whereIn('id', $keywordFilterIds);
			}
			
			if (isset($filterData['rank']) && count($filterData['rank']) > 0) {
				$list->whereIn('rank', $filterData['rank']);
			}

			if (isset($filterData['order_status']) && count($filterData['order_status']) > 0) {
				$list->whereIn('order_status', $filterData['order_status']);
			}

			if (isset($filterData['hip_min']) && $filterData['hip_min'] != "") {
				$list->whereRaw('CAST(hip AS UNSIGNED) >= ?', [(int)$filterData['hip_min']]);
			}

			if (isset($filterData['hip_max']) && $filterData['hip_max'] != "") {
				$list->whereRaw('CAST(hip AS UNSIGNED) <= ?', [(int)$filterData['hip_max']]);
			}
		}
		
		if (in_array($sortBy, [0, 1, 2]) && in_array($sortType, ['asc', 'desc'])) {
			if ($sortBy == 1) {
				$list->orderBy('name', $sortType);
			} elseif ($sortBy == 2) {
				$list->orderBy('default_price', $sortType);
			} else {
				$list->orderBy('id', $sortType);
			}
		} else {
			$list->orderBy('id', 'DESC');
		}
		
		return ProductResource::collection($list->paginate($limit));
	}
	
	public function mypage(Request $request)
	{
		$searchParams = $request->all();
		$limit = Arr::get($searchParams, 'limit', static::ITEM_PER_PAGE);
		$keyword = Arr::get($searchParams, 'keyword', '');
		$status = Arr::get($searchParams, 'status', '');
		$categoryId = Arr::get($searchParams, 'categoryId', '');
		$subCategoryId = Arr::get($searchParams, 'subCategoryId', '');
		$priceFrom = Arr::get($searchParams, 'priceFrom', '');
		$priceTo = Arr::get($searchParams, 'priceTo', '');
		$type = Arr::get($searchParams, 'type', '');
		
		$list = Product::select('*')->where('is_deleted', false);
		
		if ($this->_user != null && $this->_user->hasRole(['user', 'owner']) && $this->_user->can(Acl::PERMISSION_PRODUCT_OWNER_MANAGE)) {
			$list->where('created_by', $this->_user->id);
		}
		
		if (!empty($keyword)) {
			$list->where(function ($query) use ($keyword) {
				$query->where('name', 'LIKE', '%' . $keyword . '%')
					->orWhere('price_from', 'LIKE', '%' . $keyword . '%')
					->orWhere('price_to', 'LIKE', '%' . $keyword . '%')
					->orWhere('discount', 'LIKE', '%' . $keyword . '%');
			});
		}
		
		if (!empty($type) && in_array($type, [0, 1, 2])) {
			$list->where('type', $type);
		}
		
		if (!empty($priceFrom)) {
			$list->where('price_from', '>=', $priceFrom);
		}
		
		if (!empty($priceTo)) {
			$list->where('price_from', '<=', $priceTo);
		}
		
		if ($status != '') {
			$list->where('is_activated', $status);
		}
		
		$list->orderBy('id', 'DESC');
		
		return ProductResource::collection($list->paginate($limit));
	}
	
	public function top(Request $request)
	{
		$searchParams = $request->all();
		$limit = Arr::get($searchParams, 'limit', static::ITEM_PER_PAGE);
		$list = Product::select('*')->isPublished()
			->where('type', 0)
			->whereIn('order_status', [1, 2])
			->orderBy('id', 'DESC');

		return ProductResource::collection($list->paginate($limit));
	}
	
	public function show(Request $request, $id = 0)
	{
		$previewCode = $request->get('p');
		
		if (!empty($previewCode)) {
			// Preview mode: check preview code
			$product = Product::select('*')
				->where('id', $id)
				->where('preview', $previewCode)
				->first();
		} else {
			// Normal mode: only published products
			$product = Product::select('*')
				->isPublished()
				->where('id', $id)
				->first();
		}
		
		if (!isset($product)) return response()->json(['data' => null, 'errors' => __('messages.product_invalid')], 200);
		
		return new ProductDetailResource($product);
	}
	
	public function estimateCheck(Request $request, $id = 0)
	{
		$product = Product::select('*')
			->isPublished()
			->where('id', $id)
			->first();
		
		if (!isset($product)) return response()->json(['data' => null, 'errors' => __('messages.product_invalid')], 200);
		
		$params = $request->all();
		$listOptionIds = [];
		foreach ($params['options'] as $item) {
			$listOptionIds[] = $item['value'];
		}
		
		$productDetail = ProductDetail::select(
			'product_details.id as id',
			'product_details.product_id as product_id',
			'product_details.sku as sku',
			'product_details.code as code',
			'product_details.price as price',
			'product_option_details.id as option_detail_id',
			'product_option_details.name as option_detail_name'
		)
			->join('product_detail_option_details', 'product_details.id', '=', 'product_detail_option_details.product_detail_id')
			->join('product_option_details', 'product_detail_option_details.product_option_detail_id', '=', 'product_option_details.id')
			->where('product_details.product_id', $product->id)
			->whereIn('product_option_details.id', $listOptionIds)
			->get();
		
		$list = [];
		foreach ($productDetail as $item) {
			if (!isset($list[$item->id])) {
				$list[$item->id] = [
					'id'         => $item->id,
					'product_id' => $item->product_id,
					'sku'        => $item->sku,
					'code'       => $item->code,
					'price'      => $item->price,
					'options'    => [],
				];
			}
			$list[$item->id]['options'][] = $item->option_detail_id;
		}
		
		$checkDetailId = '';
		$checkDetailCode = '';
		foreach ($list as $item) {
			if (count($item['options']) == count($listOptionIds)) {
				$checkMatch = true;
				foreach ($listOptionIds as $oId) {
					if (!in_array($oId, $item['options'])) $checkMatch = false;
				}
				if ($checkMatch) {
					$checkDetailId = $item['id'];
					$checkDetailCode = $item['code'];
					break;
				}
			}
		}
		
		$status = 'error';
		$rs = [];
		if ($checkDetailCode != null && $checkDetailCode != "") {
			$status = 'success';
			$rs = [
				'id'          => $checkDetailId,
				'code'        => $checkDetailCode,
				'purchase_id' => $params['purchase_id'],
			];
		}
		
		return response()->json([
			'status' => $status,
			'data'   => $rs
		], 200);
	}
	
	public function estimateShow($id = 0, $code = '')
	{
		$product = Product::select('*')
			->isPublished()
			->where('id', $id)
			->first();
		
		$productDetail = ProductDetail::where('product_id', $id)
			->where('code', $code)
			->first();
		
		if (!isset($product) || !isset($productDetail)) return response()->json(['status' => 'error', 'data' => null, 'errors' => __('messages.product_invalid')], 200);
		
		return new ProductItemDetailResource($productDetail);
	}
	
	public function ranking(Request $request)
	{
		//$searchParams = $request->all();
		//$limit = Arr::get($searchParams, 'limit', static::ITEM_PER_PAGE);
		
		$listA = Product::select('id', 'category_id', 'name', 'slug', 'sku', 'discount', 'description', 'image', 'note', 'policy', 'possible_delivery_time', 'regular_price_from', 'regular_price_to', 'price_from', 'price_to')
			->isPublished()
			->orderBy('id', 'DESC')
			->limit(6)
			->get();
		
		$listB = Product::select('id', 'category_id', 'name', 'slug', 'sku', 'discount', 'description', 'image', 'note', 'policy', 'possible_delivery_time', 'regular_price_from', 'regular_price_to', 'price_from', 'price_to')
			->with('category')
			->isPublished()
			->orderBy('id', 'DESC')
			->limit(12)
			->get();
		
		$listC = Product::select('id', 'category_id', 'name', 'slug', 'sku', 'discount', 'description', 'image', 'note', 'policy', 'possible_delivery_time', 'regular_price_from', 'regular_price_to', 'price_from', 'price_to')
			->isPublished()
			->orderBy('id', 'DESC')
			->limit(12)
			->get();
		
		$listD = Product::select('id', 'category_id', 'name', 'slug', 'sku', 'discount', 'description', 'image', 'note', 'policy', 'possible_delivery_time', 'regular_price_from', 'regular_price_to', 'price_from', 'price_to')
			->isPublished()
			->orderBy('id', 'DESC')
			->limit(12)
			->get();
		
		return response()->json(['data' => [
			'list_1' => $listA,
			'list_2' => $listB,
			'list_3' => $listC,
			'list_4' => $listD,
		]], 200);
	}
	
	public function rankingCategory(Request $request, $id = 0)
	{
		$searchParams = $request->all();
		$limit = Arr::get($searchParams, 'limit', static::ITEM_PER_PAGE);
		
		$list = Product::select('products.*', DB::raw('count(products.id) as total_sale'))
			->join('order_details', 'order_details.product_id', '=', 'products.id')
			->where('products.is_deleted', false)
			->where('products.is_activated', true)
			->groupBy('products.id');
		
		if ($id != null && $id != "" && $id != 0) {
			$category = Category::where('id', $id)->first();
			if (isset($category)) {
				$categories = Category::select('id', 'parent_id')
					->where('id', '!=', $category->parent_id)
					->where('id', '!=', $id)
					->where('parent_id', '!=', 0)
					->whereNotNull('parent_id')
					->get();
				
				$listIds = $this->detectChildCategoryId($categories, $id);
				$listIds[] = $category->id;
				$list->whereIn('products.category_id', $listIds);
			}
		}
		
		$list->orderBy('total_sale', 'DESC')->orderBy('products.id', 'ASC');
		
		return ProductResource::collection($list->paginate($limit));
	}
	
	public function rankingManufacturer(Request $request, $id = 0)
	{
		$searchParams = $request->all();
		$limit = Arr::get($searchParams, 'limit', static::ITEM_PER_PAGE);
		$list = Product::select('*')
			->isPublished()
			->where('manufacturer_id', $id)
			->whereNotNull('rank')
			->orderBy('rank', 'DESC')
			->orderBy('updated_at', 'DESC');
		
		return ProductResource::collection($list->paginate($limit));
	}
	
	public function register(Request $request)
	{
		$validator = Validator::make($request->all(), [
			'name' => ['required'],
		]);
		if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);
		$params = $request->all();
		
		$product = Product::create([
			'sku'                   => rand(1111, 9999),
			'code'                  => 'BA-' . rand(1111, 9999) . '-M',
			'type'                  => $params['type'],
			'name'                  => trim($params['name']),
			'slug'                  => Helper::slug(trim($params['name'])),
			'image'                 => isset($params['sliders'][0]) ? $params['sliders'][0] : 'no-image.png',
			'gender'                => (in_array($params['gender'], [0, 1])) ? $params['gender'] : 0,
			'size'                  => isset($params['size']) ? $params['size'] : 1,
			'height'                => trim($params['height']),
			'sleeve'                => trim($params['sleeve']),
			'sleeve_length'         => trim($params['sleeve_length']),
			'scene_id'              => isset($params['wearing_scene']) ? trim($params['wearing_scene']) : null,
			'rank'                  => isset($params['wearing_scene']) ? trim($params['wearing_scene']) : null,
			'hip'                   => isset($params['styling_map']) ? trim($params['styling_map']) : 'Nothing',
			'primary_width'         => trim($params['primary_width']),
			'second_width'          => trim($params['second_width']),
			'explanation'           => trim($params['explanation']),
			'default_price'         => isset($params['rental_price']) ? trim($params['rental_price']) : 0,
			'note'                  => $params['note'],
			'is_cleaning_service'   => isset($params['is_cleaning_service']) ? $params['is_cleaning_service'] : 1,
			'is_storage_period'     => isset($params['is_storage_period']) ? $params['is_storage_period'] : 1,
			'is_pricing_manual'     => 1,
			'is_available_in_stock' => 1,
			'created_by'            => ($this->_user != null) ? $this->_user->id : 1,
			'is_activated'          => 0,
			'is_deleted'            => false,
			'created_at'            => date('Y-m-d H:i:s'),
			'updated_at'            => date('Y-m-d H:i:s')
		]);
		
		//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')
			]);
		}
		
		//Category
		foreach ($params['category'] as $item) {
			ProductCategory::create([
				'product_id'  => $product->id,
				'category_id' => $item,
				'created_at'  => date('Y-m-d H:i:s'),
				'updated_at'  => date('Y-m-d H:i:s')
			]);
		}
		
		//Color
		foreach ($params['color'] as $item) {
			ProductColor::create([
				'product_id' => $product->id,
				'color_id'   => $item,
				'created_at' => date('Y-m-d H:i:s'),
				'updated_at' => date('Y-m-d H:i:s')
			]);
		}
		
		return response()->json([
			'status' => 'success',
			'id'     => $product->id,
		], 200);
	}
	
	public function faceswap(Request $request)
	{
		$validator = Validator::make($request->all(), [
			'source' => ['required'],
			'target' => ['required'],
		]);
		if ($validator->fails()) return response()->json(['errors' => $validator->errors()], 403);
		$params = $request->all();
		$code = md5(rand(1, 99999999) . date('YmdHis'));
		$sourceImg = public_path('uploads/files/') . $params['source'];
		$targetImg = public_path('uploads/files/') . $params['target'];
		$outputImg = public_path('uploads/files/') . $code . ".jpg";
		
		if (!file_exists($sourceImg) || !file_exists($targetImg)) {
			return response()->json([
				'status' => 'error',
				'key'    => null,
			], 200);
		}
		
		//Progressing Image
		$job = FaceSwapJob::create([
			'code'        => $code,
			'source_path' => $sourceImg,
			'target_path' => $targetImg,
			'output_path' => $outputImg,
			'status'      => 'pending'
		]);
		
		ProcessFaceSwapInsightface::dispatch($job)->onQueue(config('queue.connections.redis.queue'));
		
		return response()->json([
			'status' => 'progressing',
			'key'    => $code,
		], 200);
	}
	
	public function faceswapChecking($code)
	{
		$job = FaceSwapJob::where('code', $code)->first();
		
		if (!$job || ($job && $job->status == "failed")) {
			return response()->json([
				'status'  => 'error',
				'key'     => $code,
				'message' => __('messages.no_image_for_faceswap')
			], 200);
		}
		
		if ($job->status == "finished") {
			$resultImg = public_path('uploads/files/') . $code . ".jpg";
			
			if (!file_exists($resultImg)) {
				return response()->json([
					'status'  => 'error',
					'key'     => $code,
					'message' => __('messages.faceswap_process_fails')
				], 200);
			}
			
			return response()->json([
				'status' => 'success',
				'key'    => $code,
			], 200);
		}
		
		return response()->json([
			'status' => 'progressing',
			'key'    => $code,
		], 200);
	}
	
	private function detectChildCategoryId($list = [], $categoryId = '')
	{
		$listIds = [];
		foreach ($list as $item) {
			if ($item->parent_id == $categoryId) {
				$listIds[] = $item->id;
				$subIds = $this->detectChildCategoryId($list, $item->id);
				$listIds = array_merge($listIds, $subIds);
			}
		}
		
		return $listIds;
	}
}
