<?php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Datasource\EntityInterface;

class ProductsTable extends Table
{
	public function initialize(array $config): void
	{
		parent::initialize($config);
		$this->setTable('product');
		$this->belongsTo('Category', ['foreignKey' => 'category_id']);
		$this->belongsTo('User', ['foreignKey' => 'user_id']);
		$this->hasMany('ProductImage', ['foreignKey' => 'product_id', 'dependent' => true]);
		$this->hasMany('ProductAttribute', ['foreignKey' => 'product_id', 'dependent' => true]);
		$this->hasMany('ProductAttributeCombination', ['foreignKey' => 'product_id', 'dependent' => true]);
		$this->hasMany('OrderProduct', ['foreignKey' => 'product_id', 'dependent' => true]);
	}

	public function getDetails(int $id): ?EntityInterface
	{
		return $this->find()
			->where(['Products.id' => $id])
			->contain(['ProductImage'])
			->first();
	}

	public function getObjectsForCache(int $id, string $type): array|EntityInterface|null
	{
		$query = $this->find()
			->where(['Products.id' => $id, 'Products.status <' => 2])
			->contain(['User']);

		if ($type === 'user') {
			return $query->all()->toList();
		}

		return $query->first();
	}

	public function getDetailsWithAttributes(int $id): ?EntityInterface
	{
		return $this->find()
			->where(['Products.id' => $id])
			->contain([
				'Category',
				'User',
				'ProductImage',
				'ProductAttribute.ProductAttributeVariation',
				'ProductAttributeCombination'
			])
			->first();
	}

	public function getDetailsWithUser(int $id): ?EntityInterface
	{
		return $this->find()
			->where(['Products.id' => $id])
			->contain(['User'])
			->first();
	}

	public function getAll(): array
	{
		return $this->find()
			->contain(['Category', 'ProductImage', 'Store.StoreLocation.Country'])
			->orderDesc('Products.id')
			->all()
			->toList();
	}

	public function getProductsAgainstCategory(int $category_id, int $starting_point): array
	{
		return $this->find()
			->where(['Products.category_id' => $category_id, 'Products.status' => 1])
			->contain(['Category', 'ProductImage', 'ProductAttribute.ProductAttributeVariation'])
			->limit(10)
			->offset($starting_point * 10)
			->orderDesc('Products.id')
			->all()
			->toList();
	}

	public function getProductsAgainstUser(int $user_id, int $starting_point): array
	{
		return $this->find()
			->where(['Products.user_id' => $user_id])
			->contain(['Category', 'ProductImage', 'ProductAttribute.ProductAttributeVariation'])
			->limit(10)
			->offset($starting_point * 10)
			->orderDesc('Products.id')
			->all()
			->toList();
	}

	public function getPromotedProducts(int $starting_point): array
	{
		return $this->find()
			->where(['Products.promote' => 1])
			->contain(['ProductImage', 'ProductAttribute.ProductAttributeVariation'])
			->limit(10)
			->offset($starting_point * 10)
			->orderDesc('Products.id')
			->all()
			->toList();
	}

	public function getTopViewedProducts(int $starting_point): array
	{
		return $this->find()
			->contain(['ProductImage', 'ProductAttribute.ProductAttributeVariation'])
			->limit(10)
			->offset($starting_point * 10)
			->orderDesc('Products.view')
			->all()
			->toList();
	}

	public function searchProduct(string $keyword, int $starting_point): array
	{
		return $this->find()
			->where([
				'OR' => [
					['Products.title LIKE' => "%{$keyword}%"],
					['Products.description LIKE' => "%{$keyword}%"]
				],
				'Products.status' => 1
			])
			->contain(['Category', 'ProductImage', 'ProductAttribute.ProductAttributeVariation'])
			->limit(10)
			->offset($starting_point * 10)
			->orderDesc('Products.view')
			->all()
			->toList();
	}

	public function filterProducts(?float $min_price = null, ?float $max_price = null, ?string $keyword = null): array
	{
		$query = $this->find()
			->contain(['Category', 'ProductImage', 'Store.StoreLocation.Country'])
			->where(['Products.status' => 1]);

		if ($min_price !== null) {
			$query->andWhere(['Products.price >=' => $min_price]);
		}

		if ($max_price !== null) {
			$query->andWhere(['Products.price <=' => $max_price]);
		}

		if ($keyword !== null) {
			$query->andWhere(['Products.title LIKE' => "%{$keyword}%"]);
		}

		return $query->all()->toList();
	}

	public function filterProductsWithCategory(?float $min_price = null, ?float $max_price = null, ?string $keyword = null, int $category_id): array
	{
		$query = $this->find()
			->contain(['Category', 'ProductImage', 'Store.StoreLocation.Country'])
			->where(['Products.status' => 1, 'Products.category_id' => $category_id]);

		if ($min_price !== null) {
			$query->andWhere(['Products.price >=' => $min_price]);
		}

		if ($max_price !== null) {
			$query->andWhere(['Products.price <=' => $max_price]);
		}

		if ($keyword !== null) {
			$query->andWhere(['Products.title LIKE' => "%{$keyword}%"]);
		}

		return $query->all()->toList();
	}

	public function filterProductsWithCategoryAndStore(?float $min_price = null, ?float $max_price = null, int $category_id, int $store_id): array
	{
		$query = $this->find()
			->contain(['Category', 'ProductImage', 'Store.StoreLocation.Country'])
			->where([
				'Products.status' => 1,
				'Products.category_id' => $category_id,
				'Products.store_id' => $store_id
			]);

		if ($min_price !== null) {
			$query->andWhere(['Products.price >=' => $min_price]);
		}

		if ($max_price !== null) {
			$query->andWhere(['Products.price <=' => $max_price]);
		}

		return $query->all()->toList();
	}

	public function filterProductsWithStore(?float $min_price = null, ?float $max_price = null, int $store_id): array
	{
		$query = $this->find()
			->contain(['Category', 'ProductImage', 'Store.StoreLocation.Country'])
			->where(['Products.status' => 1, 'Products.store_id' => $store_id]);

		if ($min_price !== null) {
			$query->andWhere(['Products.price >=' => $min_price]);
		}

		if ($max_price !== null) {
			$query->andWhere(['Products.price <=' => $max_price]);
		}

		return $query->all()->toList();
	}

	public function filterProductsWithHighestPrice(?float $min_price = null, ?float $max_price = null, string $keyword, float $highest_price): array
	{
		$query = $this->find()
			->contain(['Category', 'ProductImage', 'Store.StoreLocation.Country'])
			->where(['Products.status' => 1]);

		if ($min_price !== null) {
			$query->andWhere(['Products.price >=' => $min_price]);
		}

		if ($max_price !== null) {
			$query->andWhere(['Products.price <=' => $max_price]);
		}

		$query->andWhere(['Products.title LIKE' => "%{$keyword}%"]);

		return $query
			->orderDesc('Products.price')
			->all()
			->toList();
	}

	public function filterProductsWithLowestPrice(?float $min_price = null, ?float $max_price = null, string $keyword, float $lowest_price): array
	{
		$query = $this->find()
			->contain(['Category', 'ProductImage', 'Store.StoreLocation.Country'])
			->where(['Products.status' => 1]);

		if ($min_price !== null) {
			$query->andWhere(['Products.price >=' => $min_price]);
		}

		if ($max_price !== null) {
			$query->andWhere(['Products.price <=' => $max_price]);
		}

		$query->andWhere(['Products.title LIKE' => "%{$keyword}%"]);

		return $query
			->orderAsc('Products.price')
			->all()
			->toList();
	}
}
