<?php
/** 
 * @package     VikRestaurants
 * @subpackage  core
 * @author      E4J s.r.l.
 * @copyright   Copyright (C) 2023 E4J s.r.l. All Rights Reserved.
 * @license     http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
 * @link        https://vikwp.com
 */

// No direct access
defined('ABSPATH') or die('No script kiddies please!');

/**
 * VikRestaurants restaurant product model.
 *
 * @since 1.9
 */
class VikRestaurantsModelMenusproduct extends JModelVRE
{
	/**
	 * Basic item loading implementation.
	 *
	 * @param   mixed    $pk   An optional primary key value to load the row by, or an array of fields to match.
	 *                         If not set the instance property value is used.
	 * @param   boolean  $new  True to return an empty object if missing.
	 *
	 * @return  mixed    The record object on success, null otherwise.
	 */
	public function getItem($pk, $new = false)
	{
		$product = parent::getItem($pk, $new);

		if (!$product)
		{
			return null;
		}

		$product->options = [];
		$product->attributes = [];

		if ($product->id)
		{
			$db = JFactory::getDbo();

			$query = $db->getQuery(true)
				->select('*')
				->from($db->qn('#__vikrestaurants_section_product_option'))
				->where($db->qn('id_product') . ' = ' . (int) $product->id)
				->order($db->qn('ordering') . ' ASC');

			$db->setQuery($query);
			$product->options = $db->loadObjectList();

			/**
			 * Load assigned attributes.
			 * 
			 * @since 1.11
			 */
			$product->attributes = $this->getAttributes((int) $product->id);
		}

		return $product;
	}

	/**
	 * Returns a list of attributes attached to the specified product.
	 * 
	 * @param   int    $idProduct  The ID of the product.
	 * 
	 * @return  int[]  An array of attribute IDs.
	 * 
	 * @since   1.11
	 */
	public function getAttributes(int $idProduct)
	{
		if (!$idProduct)
		{
			// save a query in case of missing ID
			return [];
		}

		$db = JFactory::getDbo();

		$q = $db->getQuery(true)
			->select($db->qn('id_attribute'))
			->from($db->qn('#__vikrestaurants_section_product_attr_assoc'))
			->where($db->qn('id_menuentry') . ' = ' . $idProduct);

		$db->setQuery($q);
		
		// get rid of any duplicates
		return array_values(array_unique($db->loadColumn()));
	}

	/**
	 * Basic save implementation.
	 *
	 * @param 	mixed  $data  Either an array or an object of data to save.
	 *
	 * @return 	mixed  The ID of the record on success, false otherwise.
	 */
	public function save($data)
	{
		$data = (array) $data;

		$isNew = empty($data['id']);

		if (isset($data['tags']))
		{
			// commit tags
			$data['tags'] = JModelVRE::getInstance('tag')->writeTags($data['tags'], 'products', 'name');
		}

		// attempt to save the record
		$id = parent::save($data);

		if (!$id)
		{
			return false;
		}

		if (isset($data['attributes']))
		{
			/**
			 * Attach the specified attributes to the saved product.
			 * 
			 * @since 1.11
			 */
			JModelVRE::getInstance('productattribute')->setRelation($id, $data['attributes']);
		}

		if (isset($data['options']))
		{
			$model = JModelVRE::getInstance('productoption');

			// iterate all the provided options
			foreach ($data['options'] as $i => $option)
			{
				if (is_string($option))
				{
					// JSON given, decode it
					$option = json_decode($option, true);
				}

				if ($isNew)
				{
					// unset ID to create a copy
					$option['id'] = 0;
				}

				// update ordering
				$option['ordering'] = $i + 1;
				// attach option to this product
				$option['id_product'] = $id;

				// save option
				$model->save($option);
			}
		}

		return $id;
	}

	/**
	 * Extend delete implementation to delete any related records
	 * stored within a separated table.
	 *
	 * @param   mixed    $ids  Either the record ID or a list of records.
	 *
	 * @return  boolean  True on success, false otherwise.
	 */
	public function delete($ids)
	{
		// only int values are accepted
		$ids = array_map('intval', (array) $ids);

		// invoke parent first
		if (!parent::delete($ids))
		{
			// nothing to delete
			return false;
		}

		$db = JFactory::getDbo();

		// load any relation with the sections
		$q = $db->getQuery(true)
			->select($db->qn('id'))
			->from($db->qn('#__vikrestaurants_section_product_assoc'))
			->where($db->qn('id_product') . ' IN (' . implode(',', $ids) . ')' );

		$db->setQuery($q);

		if ($assoc = $db->loadColumn())
		{
			// delete all the relations between the sections and the removed products
			JModelVRE::getInstance('sectionproduct')->delete($assoc);
		}

		// load any assigned option
		$q = $db->getQuery(true)
			->select($db->qn('id'))
			->from($db->qn('#__vikrestaurants_section_product_option'))
			->where($db->qn('id_product') . ' IN (' . implode(',', $ids) . ')' );

		$db->setQuery($q);

		if ($options = $db->loadColumn())
		{
			// delete all the options that belong to the removed products
			JModelVRE::getInstance('productoption')->delete($options);
		}

		// load any assigned attributes
		$q = $db->getQuery(true)
			->select($db->qn('id'))
			->from($db->qn('#__vikrestaurants_section_product_attr_assoc'))
			->where($db->qn('id_menuentry') . ' IN (' . implode(',', $ids) . ')' );

		$db->setQuery($q);

		if ($attributes = $db->loadColumn())
		{
			// delete all the attributes that belong to the removed products
			JModelVRE::getInstance('productattribute')->delete($attributes);
		}

		// load any assigned translation
		$q = $db->getQuery(true)
			->select($db->qn('id'))
			->from($db->qn('#__vikrestaurants_lang_section_product'))
			->where($db->qn('id_product') . ' IN (' . implode(',', $ids) . ')' );

		$db->setQuery($q);

		if ($languages = $db->loadColumn())
		{
			// delete all the translations that belong to the removed products
			JModelVRE::getInstance('langmenusproduct')->delete($languages);
		}

		return true;
	}

	/**
	 * Searches for the products that match the specified query.
	 *
	 * @param 	string  $search   The search string.
	 * @param 	array 	$options  An array of options:
	 *                            - start        int|null  the query offset;
	 *                            - limit        int|null  the query limit;
	 *
	 * @return 	array   A list of matching products.
	 * 
	 * @since   1.11
	 */
	public function search($search = '', array $options = [])
	{
		$options = new JRegistry($options);

		$db = JFactory::getDbo();

		// fetch list based on search key
		$q = $db->getQuery(true);

		$q->select($db->qn('p.id'));
		$q->select($db->qn('p.name'));
		$q->select($db->qn('p.price'));

		$q->select($db->qn('o.id', 'option_id'));
		$q->select($db->qn('o.name', 'option_name'));
		$q->select($db->qn('o.inc_price', 'option_price'));

		$q->from($db->qn('#__vikrestaurants_section_product', 'p'));
		$q->leftjoin($db->qn('#__vikrestaurants_section_product_option', 'o') . ' ON ' . $db->qn('o.id_product') . ' = ' . $db->qn('p.id'));

		$q->where(1);

		if ($search)
		{
			$q->andWhere([
				sprintf('CONCAT_WS(\' \', %s, %s) LIKE %s', $db->qn('p.name'), $db->qn('o.name'), $db->q("%$search%")),
				sprintf('CONCAT_WS(\' \', %s, %s) LIKE %s', $db->qn('o.name'), $db->qn('p.name'), $db->q("%$search%")),
			], 'OR');
		}

		$q->order($db->qn('p.ordering') . ' ASC');
		$q->order($db->qn('o.ordering') . ' ASC');

		$start = $options->get('start', 0);
		$limit = $options->get('limit', null);

		$db->setQuery($q, $start, $limit);
		$products = $db->loadObjectList();

		foreach ($products as $product)
		{
			$product->text = implode(' - ', array_filter([
				$product->name,
				$product->option_name,
			]));

			$product->totalPrice = (float) $product->price + (float) $product->option_price;
		}

		return $products;
	}
}
