<?php
/* Copyright (C) 2015   Jean-François Ferry     <jfefe@aternatik.fr>
 * Copyright (C) 2023 Deák Ferenc
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

use Luracast\Restler\RestException;

// dol_include_once('/booking/class/bookinglog.class.php');

require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_bbus.class.php';
require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_bbus_log.class.php';
require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/apiinvoicehelper.class.php';
require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/apiproductlisthelper.class.php';
require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/api_curl.class.php';
require_once DOL_DOCUMENT_ROOT . '/custom/bbus/class/basicservices.class.php';

/**
 * \file    booking/class/api_booking.class.php
 * \ingroup booking
 * \brief   File for API management of bookinglog.
 */

/**
 * API class for booking bookinglog
 *
 * @class  DolibarrApiAccess {@requires user,external}
 */
class BookingApi extends DolibarrApi
{
	use CurlApi;

	/**
	 * @var BookingLog $bookinglog {@type BookingLog}
	 */
	public $bookinglog;
	private User $user;

	/**
	 * Constructor
	 *
	 * @url     GET /
	 *
	 */
	public function __construct()
	{
		global $db, $user;
		$this->db = $db;
		$this->user = $user;
		// $this->bookinglog = new BookingLog($this->db);
	}

	//P4NR6zhWHage
	//!!!types:

	/**
	 * Get types of a bookable events
	 *
	 * Return an array with bookable events type list
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @url	GET types
	 *
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function types()
	{
		dol_include_once('/eventwizard/class/eventdetails.class.php');
		$EventDetails = new EventDetails($this->db);
		return $EventDetails->fields['type']['arrayofkeyval'];
		if (!DolibarrApiAccess::$user->rights->booking->types->read) {
			throw new RestException(401);
		}

		// dol_include_once('/booking/class/bookinglog.class.php');

		// $result = $this->bookinglog->fetch($id);
		// if (!$result) {
		// throw new RestException(404, 'BookingLog not found');
		// }

		// if (!DolibarrApi::_checkAccessToResource('bookinglog', $this->bookinglog->id, 'booking_bookinglog')) {
		// throw new RestException(401, 'Access to instance id='.$this->bookinglog->id.' of object not allowed for login '.DolibarrApiAccess::$user->login);
		// }

		// return $this->_cleanObjectDatas($this->bookinglog);
	}

	/**
	 * Get bookable events
	 *
	 * Return an array with bookable events list
	 *
	 * @return 	array|mixed data without useless information
	 * 
	 * @param 	int 		$onlyBookable 
	 * @param 	int 		$withDetails 
	 * @param 	int 		$withDates 
	 * @param 	int 		$withAviability
	 * @url	GET events
	 *
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function events($onlyBookable = 1, $withDetails = 0, $withDates = 0, $withAviability = 0)
	{
		global $db, $conf;
		// if (!DolibarrApiAccess::$user->rights->booking->events->read) {
		// throw new RestException(401);
		// }

		$obj_ret = [];

		// if (!DolibarrApiAccess::$user->rights->agenda->myactions->read) {
		// throw new RestException(401, "Insufficient rights to read events");
		// }

		$sql = "SELECT DISTINCT ed.rowid as id";
		if (!empty($conf->societe->enabled)) {
			// if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
			// $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
			// }
		}
		$sql .= " ,ed.label ";
		$sql .= " ,eld.label as from"; //... ??
		$sql .= " ,ela.label as to"; //...

		$sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm as t";
		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventdetails ed ON ed.rowid = t.fk_element";
		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventlocation eld ON eld.rowid = ed.fk_elventlocation_departure";
		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventlocation ela ON ela.rowid = ed.fk_elventlocation_arrival";
		$sql .= " WHERE code = 'AC_EVENT' ";
		$sql .= " AND t.elementtype = 'eventdetails@eventwizard'";
		if ($onlyBookable) {
			$sql .= " AND t.datep >  CURRENT_TIMESTAMP";
		}
		$result = $this->db->query($sql);

		if ($result) {
			$i = 0;
			// $num = $this->db->num_rows($result);
			// $min = min($num, ($limit <= 0 ? $num : $limit));
			while ($event = $this->db->fetch_object($result)) {
				if ($withDetails) {
					$event->details = $this->details($event->id);
				}
				if ($withDates) {
					//... $withAviability
					$event->dates = $this->dates($event->id);
				}
				$obj_ret[] = $event;
			}
		} else {
			throw new RestException(503, 'Error when retrieve Agenda Event list : ' . $this->db->lasterror());
		}
		if (!count($obj_ret)) {
			throw new RestException(404, 'No Agenda Event found');
		}
		return $obj_ret;
	}

	/**
	 * Get event dates
	 *
	 * Return an array with event dates list
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 		$id 
	 * @param 	int 		$onlyBookable 
	 * @param 	string 		$json 
	 * @url	GET dates
	 *
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function dates($id, $onlyBookable = 1, $json = null)
	{
		global $db, $conf;
		// if (!DolibarrApiAccess::$user->rights->booking->events->read) {
		// throw new RestException(401);
		// }

		$obj_ret = [];

		// if (!DolibarrApiAccess::$user->rights->agenda->myactions->read) {
		// throw new RestException(401, "Insufficient rights to read events");
		// }

		$sql = "SELECT t.id ";
		if (!empty($conf->societe->enabled)) {
			// if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
			// $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
			// }
		}
		$sql .= " ,ed.label, t.note ";
		$sql .= " ,t.datep as start, t.datep2 as end";
		$sql .= " ,eld.label as from"; //... ??
		$sql .= " ,ela.label as to"; //...

		$sql .= " FROM " . MAIN_DB_PREFIX . "actioncomm as t";
		// if (!empty($conf->societe->enabled)) {
		// if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
		// $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
		// }
		// }
		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventdetails ed ON ed.rowid = t.fk_element";
		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventlocation eld ON eld.rowid = ed.fk_elventlocation_departure";
		$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "eventwizard_eventlocation ela ON ela.rowid = ed.fk_elventlocation_arrival";
		// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."eventwizard_eventoption eo ON el.rowid = ed.fk_element";
		// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."eventwizard_eventproduct eo ON el.rowid = ed.fk_element";
		// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."eventwizard_ eo ON el.rowid = ed.fk_element";
		// $sql .= ' WHERE t.entity IN ('.getEntity('agenda').')';
		$sql .= " WHERE code = 'AC_EVENT' ";
		$sql .= " AND t.fk_element = " . (int) $this->db->escape($id);
		$sql .= " AND t.elementtype = 'eventdetails@eventwizard'";
		$sql .= " AND t.datep >  CURRENT_TIMESTAMP";
		// if (!empty($conf->societe->enabled)) {
		// if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socid) || $search_sale > 0) {
		// $sql .= " AND t.fk_soc = sc.fk_soc";
		// }
		// }
		// if ($user_ids) {
		// $sql .= " AND t.fk_user_action IN (".$this->db->sanitize($user_ids).")";
		// }
		// if ($socid > 0) {
		// $sql .= " AND t.fk_soc = ".((int) $socid);
		// }
		// Insert sale filter
		// if ($search_sale > 0) {
		// $sql .= " AND sc.fk_user = ".((int) $search_sale);
		// }
		// Add sql filters
		// if ($sqlfilters) {
		// $errormessage = '';
		// if (!DolibarrApi::_checkFilters($sqlfilters, $errormessage)) {
		// throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage);
		// }
		// $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
		// $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
		// }

		// $sql .= $this->db->order($sortfield, $sortorder);
		// if ($limit) {
		// if ($page < 0) {
		// $page = 0;
		// }
		// $offset = $limit * $page;

		// $sql .= $this->db->plimit($limit + 1, $offset);
		// }
		$result = $this->db->query($sql);

		if ($result) {
			$i = 0;
			$num = $this->db->num_rows($result);
			// $min = min($num, ($limit <= 0 ? $num : $limit));
			while ($i < $num) {
				$obj_ret[] = $this->db->fetch_object($result);
				$i++;
			}
		} else {
			throw new RestException(503, 'Error when retrieve Agenda Event list : ' . $this->db->lasterror());
		}
		if (!count($obj_ret)) {
			throw new RestException(404, 'No Agenda Event found');
		}
		return $obj_ret;
	}

	/**
	 * Post booking (draft)
	 *
	 * Return an array with draft bookings list
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$event
	 * @param 	array 	$details 
	 * @url	POST set
	 *
	 * @access protected
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function set($event, $details)
	{
		if (!DolibarrApiAccess::$user->rights->booking->events->write) {
			throw new RestException(401);
		}
	}

	/**
	 * Post booking validation
	 *
	 * Return an array with bookings list
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$event
	 * @param 	array 	$drafts 
	 * @url	POST validate
	 *
	 * @access protected
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function validate($event, $drafts)
	{
		if (!DolibarrApiAccess::$user->rights->booking->events->write) {
			throw new RestException(401);
		}
	}

	/**
	 * Get bookings list
	 *
	 * Return an array with the user's bookings list
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @url	POST bookings
	 *
	 * @access protected
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function bookings()
	{
		if (!DolibarrApiAccess::$user->rights->booking->events->write) {
			throw new RestException(401);
		}
	}

	/**
	 * Get cron to clean booking draftInvoices
	 *
	 * Return an array with cleaned list
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @url	GET cron
	 *
	 * @access protected
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function cron()
	{
		if (!DolibarrApiAccess::$user->rights->booking->events->write) {
			throw new RestException(401);
		}
	}

	/**
	 * Get details of a bookable event
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$id
	 * @url	GET details
	 *
	 * @access protected
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function details($id)
	{
		$details = [
			'tmp' => [],
			'product' => [],
			'min' => 0,
			'id_event' => $id,
			// 'variants' => []
		];
		$variants = [];
		dol_include_once('/eventwizard/class/eventdetails.class.php');
		$EventDetails = new EventDetails($this->db);
		if ($EventDetails->fetch($id)) {
			$sql = "SELECT ep.rowid ,ep.fk_eventdetails, ep.fk_product, ep.type, ep.amount, ep.qty ";
			$sql .= " , p.label, p.price "; //price	price_ttc	price_min	price_min_ttc
			$sql .= " FROM " . MAIN_DB_PREFIX . "eventwizard_eventproduct ep ";
			$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "product p ON p.rowid = ep.fk_product ";
			// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination ac ON ac.fk_product_parent = ep.fk_product ";
			// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."eventwizard_eventoption eo ON eo.rowid = ep.fk_eventoption ";
			// $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."eventwizard_eventdetails ed ON ed.rowid = ep.fk_eventdetails ";

			$sql .= " WHERE ep.fk_eventdetails = " . (int) $this->db->escape($id);
			// return $sql;
			$result = $this->db->query($sql);
			if ($result) {
				$variants_ids = [];
				while ($d = $this->db->fetch_object($result)) {
					$variants_ids[] = $d->fk_product;
					// if(is_null($d->fk_product_child)){}
					$d->variants = [];
					// $d->minP = (is_null($d->amount))?(float)$d->price:(float)$d->amount;
					$details['tmp']['' . $d->fk_product] = $d;
				}

				$sql2 = "SELECT p.rowid, p.label, p.price ";
				$sql2 .= ", ac.fk_product_parent, ac.fk_product_child, ac.variation_price, ac.variation_price_percentage "; //	variation_price	variation_price_percentage	variation_weight	variation_ref_ext	entity
				$sql2 .= "FROM " . MAIN_DB_PREFIX . "product_attribute_combination ac ";
				$sql2 .= " LEFT JOIN " . MAIN_DB_PREFIX . "product p ON ac.fk_product_child = p.rowid ";
				$sql2 .= " WHERE ac.fk_product_parent IN (" . implode(",", $variants_ids) . ") ";

				$result2 = $this->db->query($sql2);
				if ($result2) {
					while ($v = $this->db->fetch_object($result2)) {
						$d = $details['tmp']['' . $v->fk_product_parent];
						$d->variants[] = $this->product2out($v);
						if (!is_null($d->amount)) { //... - ??? %?
							$d->minP = (float) $d->amount;
						} elseif (!isset($d->minP)) {
							$d->minP = $v->price;
						} elseif ($d->minP > $v->price) {
							$d->minP = $v->price;
						}
						$details['tmp']['' . $v->fk_product_parent] = $d;
					}
				}
				foreach ($details['tmp'] as $k => $v) {
					$p = $this->product2out($v);
					$p["minP"] = (is_null($v->amount)) ? (!isset($d->minP)) ? (float) $v->price : (float) $v->minP : (float) $v->amount;
					$p["variants"] = $v->variants;
					$details['product'][] = $p;
					if ($p["type"] == 1) {
						$details['min'] += $p["minP"];
					}
				}
				unset($details['tmp']);
			} else {
				throw new RestException(503, 'Error when retrieve Agenda Event list : ' . $this->db->lasterror());
			}
			// $EventDetails->getLinesArray();
			// print_r($EventDetails->lines);
			return $details;

			//$variations
			// $variations = 

		}
	}

	protected function product2out($product)
	{
		return [
			"id" => $product->fk_product,
			"type" => $product->type, //1,2,3
			"fixed_price" => $product->amount,
			"max" => $product->qty,
			"label" => $product->label,
			"price" => $product->price,
		];
	}

	/**
	 * Get all products by type
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$type_id
	 * @param 	string 	$from_date
	 * @param 	string 	$to_date
	 * 
	 * @url	POST getallavailableproducts
	 *
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function getAllAvailableProducts(int $type_id, string $from_date, string $to_date)
	{
		$date = new DateTime($to_date);
		$date->modify('+1 day');
		$to_date = $date->format('Y-m-d');
		$TMPArray = [];
		$productsArray = [];
		$sql = "SELECT 
		pr.rowid
		from llx_actioncomm as ac 
		LEFT JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
		INNER JOIN llx_eventwizard_eventdetails as ed ON ed.rowid = ac.fk_element
		INNER JOIN llx_eventwizard_eventproduct as ep ON ep.fk_eventdetails = ed.rowid
		INNER JOIN llx_product as pr ON pr.rowid = ep.fk_product
		where ac.datep > '{$from_date} 00:00:00' AND ac.datep2 < '{$to_date} 00:00:00'
		AND ac.code = 'AC_EVENT'
		AND ed.type = {$type_id}
		AND ace.max_num - COALESCE(ace.participants, 0) > 1
		GROUP BY pr.rowid";
		$result = $this->db->query($sql);
		if ($this->db->num_rows($result) > 0) {
			while ($row = $this->db->fetch_object($result)) {
				$TMPArray[] = $row->rowid;
			}
			foreach ($TMPArray as $productItem) {
				$ApiProductListHelper = new ApiProductListHelper();
				$array = $ApiProductListHelper->list('t.ref', 'ASC', '', '', '', '', "(t.rowid:=:{$productItem})");
				$productsArray[] = $array[0];
			}
		}

		return $productsArray;
	}

	/**
	 * First step of the eventhandling
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$type_id			//selected evet from llx_event
	 * @param 	int 	$fk_event			//selected evet from llx_event
	 * @param 	int 	$reservations		//numbers of reservations
	 * @param 	int 	$product_id			//Product
	 * @param 	string	$sendId				//ID
	 * 
	 * @url	POST firsteventstep
	 * @access protected
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	public function firstEventStep(int $type_id, int $fk_event, int $reservations, int $product_id, string $sendId)
	{
		global $conf;
		ApiBbusLog::appLog("{$sendId}: firstEventStep");
		if (!DolibarrApiAccess::$user->rights->facture->creer) {
			ApiBbusLog::appLog("{$sendId} Insufficient rights");
			throw new RestException(401, 'Insufficient rights');
		}
		$this->createLog($sendId, $fk_event, $reservations, $product_id);


		$server_host = $this->getServerHost($type_id);
		if ($server_host == $conf->global->LOCAL_SERVER_HOST) {
			if ($this->noLocalEmptySpaces($fk_event, $reservations)) {
				throw new RestException(506, 'No available spaces');
			}
			ApiBbusLog::appLog("{$sendId}: firstEventStep: local");
			$createdPreOrderOBJ = $this->localcreatedpreorderobj($sendId, $fk_event, $reservations, $product_id);
		} else {
			ApiBbusLog::appLog("{$sendId}: firstEventStep: curl");
			$params = compact('fk_event', 'reservations');
			$postFields = json_encode($params);
			$noavailableSpaces = json_decode($this->curlRunner('bookingapi/curlnoLocalEmptySpaces', $postFields, 'POST', true))->result;
			if ($noavailableSpaces) {
				ApiBbusLog::appLog("{$sendId}: firstEventStep: No available spaces");
				throw new RestException(506, 'No available spaces');
			}
			$params = compact('sendId', 'fk_event', 'reservations', 'product_id');
			$postFields = json_encode($params);
			$createdPreOrderOBJ = $this->curlRunner('bookingapi/localcreatedpreorderobj', $postFields, 'POST', true);
		}
		ApiBbusLog::appLog("{$sendId}: " . json_encode($createdPreOrderOBJ));
		return $createdPreOrderOBJ;
	}

	/**
	 * Get all preorders
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	string 	sendId
	 * @param 	int 	fk_event
	 * @param 	int 	reservations
	 * @param 	int 	product_id
	 * 
	 * @url	POST localcreatedpreorderobj
	 * @access protected
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	function localcreatedpreorderobj($sendId, $fk_event, $reservations, $product_id)
	{
		global $user;
		ApiBbusLog::appLog("{$sendId}: localcreatedpreorderobj");

		$apiInvoiceHelper = new ApiInvoiceHelper;
		$createdPreOrderOBJ = [];
		for ($i = 1; $i <= $reservations; $i++) {
			dol_include_once('/comm/action/class/actioncomm.class.php');
			dol_include_once('/custom/booking/class/preorder.class.php');
			$apiInvoiceHelper->increaseParticipant($fk_event);

			$lines = $apiInvoiceHelper->getProductLines($product_id);
			foreach ($lines as $line) {
				$preOrderObj = new PreOrder($this->db);
				$preOrderObj->ref = $this->generateRandomString();
				$preOrderObj->fk_event = $fk_event;
				$preOrderObj->fk_product = $product_id;
				$result = $preOrderObj->create($user);
				if ($result > 0) {
					$createdPreOrderOBJ[] = [
						'id' => $preOrderObj->id,
						'ref' => $preOrderObj->ref,
					];
				} else {
					foreach ($preOrderObj->errors as $error) {
						ApiBbusLog::appLog("Error: " . $error);
					}
					ApiBbusLog::appLog("{$sendId}: Unsaved event: " . $fk_event);
					throw new RestException(401, 'Unsaved event: ' . $fk_event);
				}
			}
		}
		ApiBbusLog::appLog(json_encode($createdPreOrderOBJ));
		return $createdPreOrderOBJ;
	}

	function createLog($sendId, $fk_event, $reservations, $product_id)
	{
		/**
		 * LOG SECTION
		 */
		ApiBbusLog::eventLog("{$sendId} === NEW INVOICE ===");
		dol_syslog("{$sendId} === NEW INVOICE ===", LOG_INFO, 0);
		ApiBbusLog::eventLog("{$sendId} REQUEST: {$sendId}");
		dol_syslog("{$sendId} REQUEST: {$sendId}", LOG_INFO, 0);
		ApiBbusLog::eventLog("{$sendId} User: {$this->user->firstname} {$this->user->lastname} (ID: {$this->user->id})");
		dol_syslog("{$sendId} User: {$this->user->firstname} {$this->user->lastname} (ID: {$this->user->id})", LOG_INFO, 0);
		ApiBbusLog::eventLog("{$sendId} " . json_encode([
			'fk_event' => $fk_event,
			'reservations' => $reservations,
			'product_id' => $product_id,
			'sendId' => $sendId,
		]));
	}

	function generateRandomString($length = 10)
	{
		$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
		$charactersLength = strlen($characters);
		$randomString = '';
		for ($i = 0; $i < $length; $i++) {
			$randomString .= $characters[rand(0, $charactersLength - 1)];
		}

		return $randomString;
	}

	/**
	 * Save event data in Bookinghistory
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$fk_event			//selected evet from llx_event
	 * 
	 * @url	POST saveeventdata
	 * @access protected
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function saveEventData($fk_event, $fk_facture = null, $fk_eventproduct = null, $ref = null)
	{

		ApiBbusLog::appLog("saveEventData");

		dol_include_once('/custom/booking/class/bookinghistory.class.php');
		dol_include_once('/comm/action/class/actioncomm.class.php');
		$actionCommObj = new ActionComm($this->db);
		$sql = "SELECT fk_element, datep, datep2 FROM " . MAIN_DB_PREFIX . $actionCommObj->table_element . " WHERE id = {$fk_event}";
		ApiBbusLog::appLog("{$sql}");

		$result = $this->db->query($sql);
		if ($this->db->num_rows($result) > 0) {
			$row = $this->db->fetch_object($result);
			$BookingHistory = new BookingHistory($this->db);
			$BookingHistory->fk_event = $fk_event;
			$BookingHistory->fk_facture = $fk_facture;
			$BookingHistory->fk_event_detail = $row->fk_element;
			$BookingHistory->fk_eventproduct = $fk_eventproduct;
			$BookingHistory->date_start = strtotime($row->datep);
			$BookingHistory->date_end = strtotime($row->datep2);
			$BookingHistory->invoice_number = $ref;
			$result = $BookingHistory->create($this->user);
			if ($result > 0) {
				ApiBbusLog::appLog("saveEventData: OK {$BookingHistory->id}");
				return $BookingHistory->id;
			} else {
				foreach ($BookingHistory->errors as $error) {
					ApiBbusLog::appLog("saveEventData: {$error}");
				}
				ApiBbusLog::appLog("saveEventData: ERROR");
				throw new RestException(401, 'Unsaved');
			}
		} else {
			throw new RestException(401, 'No Event record');
		}
	}

	/**
	 * Validate and save invoice
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$type_id			Type_id
	 * @param 	string	$sendId				sendID
	 * @param  	array 	$payment			Invoice payment
	 * @param  	array 	$preorder			preorder
	 * @param  	array 	$invoice			Invoice
	 * @param  	string 	$cardPaymentLog		Card payment log data
	 * 
	 * @url	POST validateandsaveinvoice
	 * @access protected

	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	public function validateandsaveinvoice(int $type_id, string $sendId, array $payment, array $preorder, array $invoice, string $cardPaymentLog = '')
	{
		ApiBbusLog::appLog("validateandsaveinvoice");

		global $db, $conf;
		$server_host = $this->getServerHost($type_id);
		$createdInvoiceArray = $this->localValidateAndSaveInvoice($invoice, $preorder, $payment, $cardPaymentLog, $sendId, $server_host, $type_id);
		if ($server_host == $conf->global->LOCAL_SERVER_HOST) {
			ApiBbusLog::appLog("validateandsaveinvoice: local");
			ApiBbusLog::appLog(json_encode($preorder));
			foreach ($preorder as $order) {
				$sql = "SELECT fk_product, fk_event  FROM llx_booking_preorder WHERE rowid = {$order}";
				ApiBbusLog::appLog("{$sql}");
				$data = $db->query($sql);
				if ($db->num_rows($data) > 0) {
					while ($row = $db->fetch_object($data)) {
						ApiBbusLog::appLog("while");
						$bookingHistory = $this->saveEventData((int)$row->fk_event, $createdInvoiceArray[0]['invoice']['id'], null, $createdInvoiceArray[0]['invoice']['ref']);

						ApiBbusLog::appLog("______________________________________________________________________________");
						$this->updateBbticket($createdInvoiceArray[0]['invoice'], $bookingHistory);
						$this->deletePreorder($preorder);
					}
				}
			}
		} else {
			ApiBbusLog::appLog("validateandsaveinvoice: curl");
			/* #-------------- UPDATE BTICKET --------------
			ApiBbusLog::appLog("{$sendId}: Update bbticket with curl");
			$invoiceForCurl['id'] = $createdInvoiceArray[0]['invoice']['id'];
			$invoiceForCurl['ref'] = $createdInvoiceArray[0]['invoice']['ref'];
			$array['fk_booking_history'] = null;
			$array['invoice'] = $invoiceForCurl;
			$updateBBticketPostFields = json_encode($array);
			ApiBbusLog::appLog("{$updateBBticketPostFields}");
			$this->curlRunner('bookingapi/curlUpdateBbticket', $updateBBticketPostFields, 'POST', true); */
			foreach ($preorder as $rowid) {
				$params = compact('rowid');
				$postFields = json_encode($params);
				$preorderArray = $this->curlRunner('bookingapi/curllocalpreorderarray', $postFields, 'POST', true);
				$saveEventdataPostFields = '{"fk_event":"' . (int)$preorderArray->fk_event . '","ref":"' . $createdInvoiceArray[0]['invoice']['ref'] . '","fk_facture":"' . null . '","fk_eventproduct":"' . null . '"}';
				$bookingHistory = $this->curlRunner('bookingapi/saveEventData', $saveEventdataPostFields, 'POST', true);
				$array['fk_booking_history'] = $bookingHistory;
				$array['invoice'] = $createdInvoiceArray[0]['invoice'];
				$updateBBticketPostFields = json_encode($array);
				$this->curlRunner('bookingapi/curlUpdateBbticket', $updateBBticketPostFields, 'POST', true);
				ApiBbusLog::appLog("validateandsaveinvoice: {$postFields}");
				$this->curlRunner('bookingapi/curlDeletePreorder', $postFields, 'POST', true);
			}
		}
		ApiBbusLog::appLog("validateandsaveinvoice: END");

		return $createdInvoiceArray;
	}

	public function localValidateAndSaveInvoice($invoice, $preorder, $payment, $cardPaymentLog, $sendId, $server_host, $type_id)
	{
		ApiBbusLog::appLog("localValidateAndSaveInvoice");

		global $user, $db, $conf;
		$server_host_curl = false;
		$createdInvoiceData = [];
		$apiInvoiceHelper = new ApiInvoiceHelper;
		$bbusApi = new BBus();
		foreach ($preorder as $rowid) {
			$params = compact('rowid');
			$postFields = json_encode($params);
			if ($server_host == $conf->global->LOCAL_SERVER_HOST) {
				ApiBbusLog::appLog("localValidateAndSaveInvoice: local");
				$preorderArray = $this->localpreorderarray($rowid);
			} else {
				ApiBbusLog::appLog("localValidateAndSaveInvoice: curl");
				if ($type_id != 5 || $type_id != 3 || $type_id != 4) {
					$server_host_curl = true;
				}
				$preorderArray = $this->curlRunner('bookingapi/curllocalpreorderarray', $postFields, 'POST', true);
			}
			$lines = $apiInvoiceHelper->getProductLinesWithoutDiscount($preorderArray->fk_product);
			//$lines = $apiInvoiceHelper->createLinesForCrossShopping($preorderArray->fk_product);
			$createdInvoiceArray = $bbusApi->invoice($invoice, $lines, $payment, $cardPaymentLog = '', $sendId = '', $server_host_curl);
			$createdInvoiceData[] = $createdInvoiceArray;
		}
		ApiBbusLog::appLog("Visszaküld");
		$asd = json_encode($createdInvoiceData);
		ApiBbusLog::appLog("{$asd}");

		return $createdInvoiceData;
	}

	public function localpreorderarray(int $rowid)
	{
		global $user, $db;
		$result = [];
		$sql = "SELECT fk_product, fk_event  FROM llx_booking_preorder WHERE rowid = {$rowid}";
		$data = $db->query($sql);
		if ($db->num_rows($data) > 0) {
			while ($row = $db->fetch_object($data)) {
				$result = $row;
			}
		}
		return $result;
	}

	/**
	 * curllocalpreorderarray
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$rowid			
	 * 
	 * @url	POST curllocalpreorderarray
	 * @access protected

	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	public function curllocalpreorderarray(int $rowid)
	{
		global $user, $db;
		$result = [];
		$sql = "SELECT fk_product, fk_event  FROM llx_booking_preorder WHERE rowid = {$rowid}";
		$data = $db->query($sql);
		if ($db->num_rows($data) > 0) {
			while ($row = $db->fetch_object($data)) {
				$result = $row;
			}
		}
		return $result;
	}

	public function updateBbticket($invoice, $fk_booking_history = null)
	{
		ApiBbusLog::appLog("updateBbticket_________START");

		global $user, $conf;
		$sql = "SELECT rowid FROM llx_bbus_bbticket WHERE fk_facture = {$invoice['id']}";
		ApiBbusLog::appLog("{$sql}");
		$data = $this->db->query($sql);
		if (!empty($this->db->error())) {
			ApiBbusLog::appLog("{$this->db->error()}");
			throw new RestException(401, "{$this->db->error()}");
		}
		$addedsql = $fk_booking_history == null ? "" : " booking_history_id = {$fk_booking_history},";
		if ($this->db->num_rows($data) > 0) {
			while ($row = $this->db->fetch_object($data)) {
				$sql = "UPDATE llx_bbus_bbticket
					SET {$addedsql} invoice_number = '{$invoice['ref']}'
					WHERE rowid = {$row->rowid}";
				ApiBbusLog::appLog("{$sql}");
				$this->db->query($sql);
			}
		} else {
			ApiBbusLog::appLog("{$this->db->error()}");
			ApiBbusLog::appLog("api_booking ({$conf->global->LOCAL_SERVER_HOST}): updateBbticket error");
		}
	}

	/**
	 * curlupdateBbticket
	 *
	 * Return an array with details
	 *
	 *
	 * @param  	array 	$invoice					invoice
	 * 
	 * @url	POST curlupdateBbticket
	 * @access protected

	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	public function curlupdateBbticket(array $invoice, $fk_booking_history = null)
	{
		global $user;
		ApiBbusLog::appLog("curlupdateBbticket______START");
		$sql = "SELECT rowid FROM llx_bbus_bbticket WHERE invoice_number = '{$invoice['id']}' OR invoice_number = '{$invoice['ref']}'";
		ApiBbusLog::appLog("{$sql}");
		$data = $this->db->query($sql);
		if (!empty($this->db->error())) {
			ApiBbusLog::appLog("{$this->db->error()}");
			throw new RestException(401, "{$this->db->error()}");
		}
		if ($this->db->num_rows($data) > 0) {
			while ($row = $this->db->fetch_object($data)) {
				$booking_history_id = is_null($fk_booking_history) ? 'booking_history_id = null,' : 'booking_history_id = ' . $fk_booking_history . ',';
				$sql = "UPDATE llx_bbus_bbticket
					SET {$booking_history_id} invoice_number = '{$invoice['ref']}'
					WHERE rowid = {$row->rowid}";
				ApiBbusLog::appLog("{$sql}");
				$this->db->query($sql);
			}
		} else {
			ApiBbusLog::appLog("api_booking: curlupdateBbticket error");
			ApiBbusLog::appLog("{$this->db->error()}");
			throw new RestException(401, 'api_booking: curlupdateBbticket error');
		}
	}

	public function deletePreorder($preorder)
	{
		foreach ($preorder as $item) {
			$sql = "DELETE FROM llx_booking_preorder WHERE rowid = {$item}";
			$this->db->query($sql);
		}
	}

	/**
	 * curldeletePreorder
	 *
	 * Return an array with details
	 *
	 *
	 * @param  	array 	$preorder			preorder
	 * 
	 * @url	POST curldeletePreorder
	 * @access protected

	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	public function curldeletePreorder($preorder)
	{
		foreach ($preorder as $item) {
			$sql = "DELETE FROM llx_booking_preorder WHERE rowid = {$item}";
			$this->db->query($sql);
		}
	}

	public function eventErasuer()
	{
		global $user;
		$limitDate = date("Y-m-d H:i:s", dol_now() - 1200);
		$sql = "SELECT * FROM llx_booking_preorder WHERE date_creation < '{$limitDate}'";
		//print $sql;exit;
		$data = $this->db->query($sql);
		if ($this->db->num_rows($data) > 0) {
			while ($row = $this->db->fetch_object($data)) {
				//print_r($row);
				$actioncommObj = new ActionComm($this->db);
				$resultActionComm = $actioncommObj->fetch($row->fk_event);
				$actioncommObj->array_options['options_participants'] = $actioncommObj->array_options['options_participants'] - 1;
				$actioncommObj->update($user);
			}
			//exit;
			$sqlDelete = "DELETE FROM llx_booking_preorder WHERE date_creation < '{$limitDate}'";
			$dataDelete = $this->db->query($sqlDelete);
		}
	}

	/**
	 * Increase Participants
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$event_id			//rowid of evet from llx_event
	 * 
	 * @url	POST increaseparticipant
	 *
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function increaseParticipant(int $event_id)
	{
		dol_include_once('/comm/action/class/actioncomm.class.php');
		global $user;
		$actionComObj = new ActionComm($this->db);
		$actionComObj->fetch($event_id);
		$max_num = (int) $actionComObj->array_options['options_max_num'];
		$buffer = (int) $actionComObj->array_options['options_buffer'];
		$participants = (int) $actionComObj->array_options['options_participants'];
		$increasedParticipants = $participants + 1;
		if ($increasedParticipants > ($max_num + $buffer)) {
			return 'full';
		} elseif (($increasedParticipants > $max_num) && ($increasedParticipants <= ($max_num + $buffer))) {
			$actionComObj->array_options['options_participants'] = $increasedParticipants;
			$actionComObj->update($user);
			return 'Buffer';
		} else {
			$actionComObj->array_options['options_participants'] = $increasedParticipants;
			$actionComObj->update($user);
			return 'OK';
		}
	}

	/**
	 * Reduce Participants
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$event_id			//rowid of evet from llx_event
	 * 
	 * @url	POST reduceparticipant
	 *
	 * 
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function reduceParticipant(int $event_id)
	{
		global $user;
		dol_include_once('/comm/action/class/actioncomm.class.php');
		$actionComObj = new ActionComm($this->db);
		$actionComObj->fetch($event_id);
		$participants = (int) $actionComObj->array_options['options_participants'];
		$reducedParticipants = $participants - 1;
		$actionComObj->array_options['options_participants'] = $reducedParticipants < 0 ? 0 : $reducedParticipants;
		$actionComObj->update($user);
		return 'OK';
	}

	/**
	 * Create invoice
	 * 
	 * @param  array 	$invoice			Invoice data
	 * @param  array 	$lines				Invoice lines
	 * @param  array 	$payment			Invoice payment
	 * @param  string 	$eventid			Event ID
	 * @param  string 	$cardPaymentLog		Card payment log data
	 * 
	 * @access protected
	 * @return array|mixed Data without useless information
	 * 
	 * @url POST /invoice
	 */
	public function invoice(array $invoice, array $lines, array $payment, string $cardPaymentLog = '', string $eventid = '', string $sendId = '')
	{
		if (empty($sendId)) {
			$sendId = substr(md5(rand()), 0, 8);
		}

		/**
		 * LOG SECTION
		 */
		ApiBbusLog::appLog("{$sendId} === NEW INVOICE ===");
		dol_syslog("{$sendId} === NEW INVOICE ===", LOG_INFO, 0);
		ApiBbusLog::appLog("{$sendId} REQUEST: {$sendId}");
		dol_syslog("{$sendId} REQUEST: {$sendId}", LOG_INFO, 0);
		ApiBbusLog::appLog("{$sendId} User: {$this->user->firstname} {$this->user->lastname} (ID: {$this->user->id})");
		dol_syslog("{$sendId} User: {$this->user->firstname} {$this->user->lastname} (ID: {$this->user->id})", LOG_INFO, 0);
		ApiBbusLog::appLog("{$sendId} " . json_encode([
			'invoice' => $invoice,
			'lines' => $lines,
			'payment' => $payment,
			'eventid' => $eventid,
			'cardPaymentLog' => $cardPaymentLog
		]));

		/**
		 * CHECK RIGHTS
		 */
		if (!DolibarrApiAccess::$user->rights->facture->creer) {
			ApiBbusLog::appLog("{$sendId} Insufficient rights");
			throw new RestException(401, 'Insufficient rights');
		}


		$apiInvoiceHelper = new ApiInvoiceHelper;
		/**
		 * handle invoice basic data
		 */
		$invoiceObj = $apiInvoiceHelper->createInvoice($invoice, $sendId);
		/**
		 * handle invoice line(s)
		 */
		$products = [];

		//print $eventid;exit;

		foreach ($lines as $line) {
			$invoiceObj = $apiInvoiceHelper->addLineToInvoice($invoiceObj, $line, $sendId);
			$this->increaseParticipant((int) $eventid);
			$product = $apiInvoiceHelper->loadProductToResult($line);
			if (!empty($product)) {
				$products[] = $product;
			}
		}

		$invoiceObj->fetch_lines();

		foreach ($products as &$row) {
			foreach ($invoiceObj->lines as $line) {
				if ($line->product_ref == $row['ref']) {
					//$row['price'] = $line->total_ttc;
					$row['price'] = $line->multicurrency_total_ttc;
					$row['total_tva'] = $line->total_tva;
					//$row['total_tva'] = $line->multicurrency_total_tva;
				}
			}
		}

		return [
			'sendId' => $sendId,
			'invoice' => [
				'id' => $invoiceObj->id,
				'ref' => $invoiceObj->ref,
				'total' => $invoiceObj->multicurrency_total_ttc
			],
			'products' => $products,
		];
	}

	/**
	 * Get all free spaces on a selected date
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	type_id
	 * @param 	string 	date_from
	 * @param 	string 	date_to
	 * @param 	int 	product_id
	 * @param 	int 	participant_number
	 * 
	 * @url	POST getavailablespaces
	 *
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function getAvailableSpaces(int $type_id, string $date_from, string $date_to, int $product_id, int $participant_number)
	{
		global $conf;
		$server_host = $this->getServerHost($type_id);
		if ($server_host == $conf->global->LOCAL_SERVER_HOST) {
			ApiBbusLog::appLog("getAvailableSpaces: local");
			return $this->localAvailablePlaces($date_from, $date_to, $product_id, $participant_number);
		} else {
			ApiBbusLog::appLog("getAvailableSpaces: curl");
			$params = compact('date_from', 'date_to', 'product_id', 'participant_number');
			$postFields = json_encode($params);
			return $this->curlRunner('bookingapi/localAvailablePlaces', $postFields, 'POST', true);
		}
	}

	/* 	private function getServerHost($type_id)
	{
		$basicServices = new BasicServices($this->db);
		$resultBS = $basicServices->fetch($type_id);
		return $basicServices->server_host;
	} */

	/**
	 * Get all free spaces on a selected date
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	string 	date_from
	 * @param 	string 	date_to
	 * @param 	int 	product_id
	 * @param 	int 	participant_number
	 * 
	 * @url	POST localAvailablePlaces
	 *
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function localAvailablePlaces($date_from, $date_to, $product_id, $participant_number)
	{
		ApiBbusLog::appLog("localAvailablePlaces");
		$array = [];
		$date = new DateTime($date_to);
		$date->modify('+1 day');
		$date_to = $date->format('Y-m-d');
		$date_from = $this->getCutOffTimeDate($product_id);
		$sql = "SELECT 
		ac.id, 
		ac.datep, 
		ace.max_num, 
		ace.participants,
		ace.buffer
		FROM llx_actioncomm as ac 
		LEFT JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
		INNER JOIN llx_eventwizard_eventdetails as ed ON ac.fk_element = ed.rowid
		INNER JOIN llx_eventwizard_eventproduct as ep ON ep.fk_eventdetails = ac.fk_element
		WHERE ac.code = 'AC_EVENT'
		AND ep.fk_product = {$product_id}
		AND ac.datep > '{$date_from}' 
		AND ac.datep2 < '{$date_to} 00:00:00'
		ORDER BY ac.datep ASC";
		$result = $this->db->query($sql);
		while ($row = $this->db->fetch_object($result)) {
			$max_num = $row->max_num;
			$buffer = $row->buffer;
			$participants = $row->participants;
			$date = strtotime($row->datep);
			$element['event_id'] = $row->id;
			//$element['date'] = date("Y-m-d", $date);
			//$element['time'] = date("H:i", $date);
			$element['participants'] = is_null($participants) ? 0 : (int) $participants;
			$element['max_num'] = $max_num;
			$element['buffer'] = $buffer;
			if ($max_num > $participants) {
				if ($max_num + $buffer > $participants + $participant_number) {
					$array[date("Y-m-d", $date)][date("H:i", $date)] = $element;
				}
			}
		}
		return $array;
	}

	/**
	 * localCheckAvailablePlaces
	 *	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	product_id
	 * 
	 * @url	POST localCheckAvailablePlaces
	 *
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 */
	public function localCheckAvailablePlaces($product_id)
	{
		ApiBbusLog::appLog("localCheckAvailablePlaces");
		$date_from = $this->getCutOffTimeDate($product_id);
		$sql = "SELECT 
		ac.id, 
		ac.datep, 
		ace.max_num, 
		ace.participants,
		ace.buffer
		FROM llx_actioncomm as ac 
		LEFT JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
		INNER JOIN llx_eventwizard_eventdetails as ed ON ac.fk_element = ed.rowid
		INNER JOIN llx_eventwizard_eventproduct as ep ON ep.fk_eventdetails = ac.fk_element
		WHERE ac.code = 'AC_EVENT'
		AND ep.fk_product = {$product_id}
		AND ac.datep > '{$date_from}'
		ORDER BY ac.datep ASC";
		$result = $this->db->query($sql);
		return $this->db->num_rows($result) > 0;
	}

	private function getCutOffTimeDate($product_id)
	{
		$date = date("Y-m-d H:i:s");
		//ApiBbusLog::appLog("{$date}");
		$sql = "SELECT cut_off_time_app FROM llx_product_extrafields WHERE fk_object = {$product_id}";
		if ($result = $this->db->query($sql)) {
			if ($this->db->num_rows($result) > 0) {
				while ($row = $this->db->fetch_object($result)) {
					$cutOffTime = $row->cut_off_time_app;
				}
				$dateTimestamp = strtotime($date);
				$date = date("Y-m-d H:i:s", $dateTimestamp + $cutOffTime * 60);
			}
		}
		//ApiBbusLog::appLog("{$date}");
		return $date;
	}

	private function noLocalEmptySpaces($event_id, $reservations)
	{
		$sql = "SELECT ace.max_num, ace.buffer, ace.participants FROM llx_actioncomm as ac
        INNER JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
        WHERE ac.id = {$event_id}";
		$result = $this->db->query($sql);
		if ($this->db->num_rows($result) == 0) {
			return true;
		}
		while ($row = $this->db->fetch_object($result)) {
			if ($row->participants >= $row->max_num) {
				return true;
			}
			if ($row->participants + $reservations > $row->max_num + $row->buffer) {
				return true;
			}
		}
		return false;
	}

	/**
	 * curlnoLocalEmptySpaces
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	int 	$fk_event			
	 * @param 	int 	$reservations		
	 * 
	 * @url	POST curlnoLocalEmptySpaces
	 * @access protected
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	public function curlnoLocalEmptySpaces($fk_event, $reservations)
	{
		ApiBbusLog::appLog("curlnoLocalEmptySpaces");

		$sql = "SELECT ace.max_num, ace.buffer, ace.participants FROM llx_actioncomm as ac
        INNER JOIN llx_actioncomm_extrafields as ace ON ace.fk_object = ac.id
        WHERE ac.id = {$fk_event}";
		$result = $this->db->query($sql);
		if ($this->db->num_rows($result) == 0) {
			return true;
		}
		while ($row = $this->db->fetch_object($result)) {
			if ($row->participants >= $row->max_num) {
				return true;
			}
			if ($row->participants + $reservations > $row->max_num + $row->buffer) {
				return true;
			}
		}
		return false;
	}

	/**
	 * curlcreatedpreorderobj
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	string 	$sendId			
	 * @param 	int 	$fk_event			
	 * @param 	int 	$reservations		
	 * @param 	int 	$product_id			
	 * 
	 * @url	POST curlcreatedpreorderobj
	 * @access protected
	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	function curlcreatedpreorderobj($sendId, $fk_event, $reservations, $product_id)
	{
		ApiBbusLog::appLog("curlcreatedpreorderobj");
		$apiInvoiceHelper = new ApiInvoiceHelper;
		$createdPreOrderOBJ = [];
		for ($i = 1; $i <= $reservations; $i++) {
			dol_include_once('/comm/action/class/actioncomm.class.php');
			dol_include_once('/custom/booking/class/preorder.class.php');
			$apiInvoiceHelper->increaseParticipant($fk_event);

			$lines = $apiInvoiceHelper->getProductLines($product_id);
			foreach ($lines as $line) {
				$preOrderObj = new PreOrder($this->db);
				$preOrderObj->ref = $this->generateRandomString();
				$preOrderObj->fk_event = $fk_event;
				$preOrderObj->fk_product = $product_id;
				$result = $preOrderObj->create($this->user);
				if ($result > 0) {
					$createdPreOrderOBJ[] = [
						'id' => $preOrderObj->id,
						'ref' => $preOrderObj->ref,
					];
				} else {
					ApiBbusLog::appLog(json_encode(['error' => $preOrderObj->errors]));
					ApiBbusLog::appLog("Unsaved event: " . $fk_event);
					throw new RestException(401, 'Unsaved event: ' . $fk_event);
				}
			}
		}
		ApiBbusLog::appLog(json_encode($createdPreOrderOBJ));
		return $createdPreOrderOBJ;
	}

	/**
	 * Create BBticket record
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	string		$product_id
	 * @param 	string		$datec
	 * @param 	string		$facture_id
	 * 
	 * @url	POST createbbticket
	 * @access protected

	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	public function createbbticket(string $product_id, string $datec, string $facture_id)
	{
		global $db, $conf;
		$sql = "SELECT validperiod, occasions FROM " . $this->db->prefix() . "product_extrafields WHERE fk_object = " . $product_id;
		$result = $db->query($sql);
		while ($sqlDataResult = $db->fetch_array($result)) {
			$validperiod = $sqlDataResult['validperiod'] ? $sqlDataResult['validperiod'] : 366;
			$occasions = $sqlDataResult['occasions'];
		}
		$ticket = new BbTicket($db);
		$ticket->fk_facture = null;
		$ticket->bundle_id = $this->getProductAssociationPere($product_id);
		$ticket->usable_occasions = $occasions;
		$ticket->usage = '0';
		$ticket->available_at = $this->getAvailableAtDate($datec, $validperiod);
		$ticket->ticket_id = $product_id;
		$ticket->fk_settlements_group_id = $conf->global->CURL_GROUP_ID;
		$ticket->invoice_number = $facture_id;
		if ($ticket->create($this->user) == -1) {
			dol_syslog("Nem sikerult a ticketek mentese");
		}
		return true;
	}

	private function getProductAssociationPere($product_id)
    {
		$pere = $product_id;
        $sql = "SELECT fk_product_pere FROM " . $this->db->prefix() . "product_association WHERE fk_product_fils = " . $product_id;
        $result = $this->db->query($sql);
        if ($this->db->num_rows($result) > 0) {
            while ($sqlDataResult = $this->db->fetch_object($result)) {
                return $sqlDataResult->fk_product_pere;
            }
        }
		return $pere;
    }
	private function getAvailableAtDate($date, $validperiod)
	{
		$available_at = date('Y-m-d H:i:s', strtotime($date . ' +' . $validperiod . ' days'));
		return $available_at;
	}

	/**
	 * Get printed factures refs
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	string		$from
	 * @param 	string		$to
	 * @param 	string		$factures
	 * 
	 * @url	POST getPrintedFacturesRefs
	 

	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	public function getPrintedFacturesRefs(string $from, string $to, string $factures = "")
	{
		$refs = "";
		if ($factures != "") {
			global $db, $conf;
			$curluser_id = $conf->global->CURL_USER_ID;
			$sql = "SELECT bbt.invoice_number 
				FROM llx_bbus_bbticket as bbt 
				INNER JOIN llx_bbus_bbticketinvoiceprinting as bbtip ON bbtip.ticket_id = bbt.rowid 
				WHERE bbt.fk_user_creat = {$curluser_id} AND bbt.invoice_number IN ({$factures}) AND bbt.date_creation BETWEEN '{$from}' AND '{$to}' ORDER BY bbt.rowid DESC";
			$result = $db->query($sql);
			if ($db->num_rows($result) > 0) {
				while ($row = $db->fetch_object($result)) {
					if ($refs == "") {
						$refs .= "'" . $row->invoice_number . "'";
					} else {
						$refs .= ",'" . $row->invoice_number . "'";
					}
				}
			}
		}
		return $refs;
	}

	/**
	 * Get printed factures refs in array
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	string		$from
	 * @param 	string		$to
	 * 
	 * @url	POST getPrintedFacturesRefsArray
	 

	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	public function getPrintedFacturesRefsArray(string $from, string $to, string $factures = '')
	{
		$refs = [];
		if ($factures !== '') {
			global $db, $conf;
			$curluser_id = $conf->global->CURL_USER_ID;
			$sql = "SELECT bbt.invoice_number 
				FROM llx_bbus_bbticket as bbt 
				INNER JOIN llx_bbus_bbticketinvoiceprinting as bbtip ON bbtip.ticket_id = bbt.rowid 
				WHERE bbt.fk_user_creat = {$curluser_id} AND bbt.invoice_number IN ({$factures}) AND bbt.date_creation BETWEEN '{$from}' AND '{$to}' ORDER BY bbt.rowid DESC";
			$result = $db->query($sql);
			if ($db->num_rows($result) > 0) {
				while ($row = $db->fetch_object($result)) {
					$refs[] = $row->invoice_number;
				}
			}
		}
		return $refs;
	}

	/**
	 * Get number of a facture's printer count
	 *
	 * Return an array with details
	 *
	 * @return 	array|mixed data without useless information
	 *
	 * @param 	string		$invoice_number
	 * 
	 * @url	POST getFacturesNumberOfPrints
	 

	 * @throws RestException 401 Not allowed
	 * @throws RestException 404 Not found
	 * @throws RestException 506 No available spaces
	 * 
	 */
	public function getFacturesNumberOfPrints(string $invoice_number)
	{
		$number = 0;
		global $db;
		$sql = "SELECT tip.fk_facture, tip.product_id, count(tip.product_id)  FROM llx_bbus_bbticketinvoiceprinting as tip
        WHERE tip.invoice_number = '{$invoice_number}'
        GROUP BY tip.fk_facture, tip.product_id";
		$result = $db->query($sql);
		if ($db->num_rows($result) > 0) {
			while ($row = $db->fetch_object($result)) {
				$array[$row->fk_facture][$row->product_id] = $row->count;
			}
			$firstKey = array_key_first($array);
			$firstValue = $array[$firstKey];
			$number = reset($firstValue);
		}
		return $number;
	}
}
