<?php

namespace App\Utils\Invoices;

/**
 * Groups the invoice rows based on various criteria.
 * @author Gabriel Grosu
 */
class GroupInvoiceRows
{

	/**
	 * The updating invoice entity.
	 * @var \Entity\Invoice
	 */
	private \Entity\Invoice $invoiceUpdate;

	/**
	 * Check and associate invoice entity (with update operation).
	 * @param \Entity\Invoice $invoiceUpdate
	 * @throws \Exception
	 */
	public function __construct(\Entity\Invoice $invoiceUpdate)
	{

		// check that it is inside a transaction
		if (!\DB::isInsideTransaction()) {
			throw new \Exception('You must be inside a transaction when using '.__CLASS__);
		}

		if ($invoiceUpdate->getOperation()!='update') {
			throw new \Exception('The invoice object must have the `update` operation attached when using'.__CLASS__);
		}

		// the rows attribute must not be set (so we create it now)
		if ($invoiceUpdate->getField('rows')) {
			throw new \Exception('You must not set the `rows` invoice class attribute when using ' . __CLASS__ . ' as it is automatically generated.');
		}

		$this->invoiceUpdate = $invoiceUpdate;

	}

	/**
	 * Forces all rows to be visible on the invoice (breaks all existing groups).
	 */
	public function resetRowsGrouping()
	{

		$idInvoice = $this->invoiceUpdate->getId();
		$invoiceSel = \Entity\Invoice::selectForUpdate($idInvoice);

		// reset all rows references
		$updateRows = [];
		foreach($invoiceSel->rowsList as $idRow=>$rowDetails) {
			$thisRowUpdateObj = \Entity\InvoiceRow::update($idRow);
			$thisRowUpdateObj->hiddenOnRowId = 0;
			if (!\Entity\InvoiceRow::selectForUpdate($idRow)->rowCurrencyDetailsBlocked) {
				$thisRowUpdateObj->currencyType = $rowDetails['currencyType'];
				$thisRowUpdateObj->currencyDate = $rowDetails['currencyDate'];
			}
			if ($rowDetails['finalizeAdvanceMode']) {
				$thisRowUpdateObj->finalizeAdvanceMode = $rowDetails['finalizeAdvanceMode'];
			}
			$updateRows[] = $thisRowUpdateObj;
		}
		$this->invoiceUpdate->rows = $updateRows;

	}

	/**
	 * Forces all rows to be visible on the invoice and also resets the rows order.
	 */
	public function resetRowsGroupingAndOrder()
	{

		$idInvoice = $this->invoiceUpdate->getId();
		$invoiceSel = \Entity\Invoice::selectForUpdate($idInvoice);

		$invoiceRows = $invoiceSel->rowsList;
		ksort($invoiceRows);

		foreach($invoiceRows as $idRow=>$rowDetails) {
			$thisRowUpdateObj = \Entity\InvoiceRow::update($idRow);
			$thisRowUpdateObj->hiddenOnRowId = 0;
			if (!\Entity\InvoiceRow::selectForUpdate($idRow)->rowCurrencyDetailsBlocked) {
				$thisRowUpdateObj->currencyType = $rowDetails['currencyType'];
				$thisRowUpdateObj->currencyDate = $rowDetails['currencyDate'];
			}
			if ($rowDetails['finalizeAdvanceMode']) {
				$thisRowUpdateObj->finalizeAdvanceMode = $rowDetails['finalizeAdvanceMode'];
			}
			$updateRows[] = $thisRowUpdateObj;
		}
		$this->invoiceUpdate->rows = $updateRows;

	}


	/**
	 * Groups the rows by VAT percent and quota.
	 */
	public function groupRowsByVatPercentAndQuota()
	{

		$idInvoice = $this->invoiceUpdate->getId();
		$invoiceSel = \Entity\Invoice::selectForUpdate($idInvoice);

		$updateRows = [];
		$groupedRowsDetails = [];
		foreach($invoiceSel->rowsList as $idRow=>$rowDetails) {

			$groupKey = $rowDetails['vatPercent'].'_'.$rowDetails['vatText'];
			// this is a new key - this becomes the parent row
			if (!$groupedRowsDetails[$groupKey]) {
				$groupedRowsDetails[$groupKey] = $idRow;
				$hiddenOnRowId = 0;
			}
			// this key already exists - this row is associated to its already stored parent
			else {
				$hiddenOnRowId = $groupedRowsDetails[$groupKey];
			}

			$thisRowUpdateObj = \Entity\InvoiceRow::update($idRow);
			$thisRowUpdateObj->hiddenOnRowId = $hiddenOnRowId;
			if (!\Entity\InvoiceRow::selectForUpdate($idRow)->rowCurrencyDetailsBlocked) {
				$thisRowUpdateObj->currencyDate = $rowDetails['currencyDate'];
				$thisRowUpdateObj->currencyType = $rowDetails['currencyType'];
			}
			if ($rowDetails['finalizeAdvanceMode']) {
				$thisRowUpdateObj->finalizeAdvanceMode = $rowDetails['finalizeAdvanceMode'];
			}
			$updateRows[] = $thisRowUpdateObj;
		}
		$this->invoiceUpdate->rows = $updateRows;

	}


	/**
	 * Groups by service type (serviceType/location/calculation/rowType).
	 */
	public function groupRowsByServiceType()
	{

		$idInvoice = $this->invoiceUpdate->getId();
		$invoiceSel = \Entity\Invoice::selectForUpdate($idInvoice);

		$updateRows = [];
		$groupedRowsDetails = [];
		foreach($invoiceSel->rowsList as $idRow=>$rowDetails) {

			$groupKey = $rowDetails['serviceCode'].'_'.$rowDetails['location'].'_'.$rowDetails['calculationType'].'_'.$rowDetails['rowType'].'_'.$rowDetails['vatPercent'].'_'.$rowDetails['vatText'];
			// this is a new key - this becomes the parent row
			if (!$groupedRowsDetails[$groupKey]) {
				$groupedRowsDetails[$groupKey] = $idRow;
				$hiddenOnRowId = 0;
			}
			// this key already exists - this row is associated to its already stored parent
			else {
				$hiddenOnRowId = $groupedRowsDetails[$groupKey];
			}

			$thisRowUpdateObj = \Entity\InvoiceRow::update($idRow);
			$thisRowUpdateObj->hiddenOnRowId = $hiddenOnRowId;
			if (!\Entity\InvoiceRow::selectForUpdate($idRow)->rowCurrencyDetailsBlocked) {
				$thisRowUpdateObj->currencyDate = $rowDetails['currencyDate'];
				$thisRowUpdateObj->currencyType = $rowDetails['currencyType'];
			}
			if ($rowDetails['finalizeAdvanceMode']) {
				$thisRowUpdateObj->finalizeAdvanceMode = $rowDetails['finalizeAdvanceMode'];
			}

			$updateRows[] = $thisRowUpdateObj;
		}
		$this->invoiceUpdate->rows = $updateRows;

	}

	/**
	 * Groups by service id (serviceId).
	 */
	public function groupRowsByServiceId()
	{

		$idInvoice = $this->invoiceUpdate->getId();
		$invoiceSel = \Entity\Invoice::selectForUpdate($idInvoice);

		$updateRows = [];
		$groupedRowsDetails = [];
		foreach($invoiceSel->rowsList as $idRow=>$rowDetails) {

			$groupKey = $rowDetails['idReservationService'].'_'.$rowDetails['vatPercent'].'_'.$rowDetails['vatText'];

			// this is a new key - this becomes the parent row
			if (!$groupedRowsDetails[$groupKey]) {
				$groupedRowsDetails[$groupKey] = $idRow;
				$hiddenOnRowId = 0;
			}
			// this key already exists - this row is associated to its already stored parent
			else {
				$hiddenOnRowId = $groupedRowsDetails[$groupKey];
			}

			$thisRowUpdateObj = \Entity\InvoiceRow::update($idRow);
			$thisRowUpdateObj->hiddenOnRowId = $hiddenOnRowId;
			if (!\Entity\InvoiceRow::selectForUpdate($idRow)->rowCurrencyDetailsBlocked) {
				$thisRowUpdateObj->currencyDate = $rowDetails['currencyDate'];
				$thisRowUpdateObj->currencyType = $rowDetails['currencyType'];
			}
			if ($rowDetails['finalizeAdvanceMode']) {
				$thisRowUpdateObj->finalizeAdvanceMode = $rowDetails['finalizeAdvanceMode'];
			}

			$updateRows[] = $thisRowUpdateObj;
		}
		$this->invoiceUpdate->rows = $updateRows;

	}

	/**
	 * Groups by service id and row type.
	 */
	public function groupRowsByServiceIdAndRowType()
	{

		$idInvoice = $this->invoiceUpdate->getId();
		$invoiceSel = \Entity\Invoice::selectForUpdate($idInvoice);

		$updateRows = [];
		$groupedRowsDetails = [];
		foreach($invoiceSel->rowsList as $idRow=>$rowDetails) {

			// the group key should include only service id (which determines type) and row type, but I added vatPercent and vatText just to be sure or for cases in which calculations configs are updated
			$groupKey = $rowDetails['idReservationService'].'_'.$rowDetails['rowType'].'_'.$rowDetails['vatPercent'].'_'.$rowDetails['vatText'];

			// this is a new key - this becomes the parent row
			if (!$groupedRowsDetails[$groupKey]) {
				$groupedRowsDetails[$groupKey] = $idRow;
				$hiddenOnRowId = 0;
			}
			// this key already exists - this row is associated to its already stored parent
			else {
				$hiddenOnRowId = $groupedRowsDetails[$groupKey];
			}

			$thisRowUpdateObj = \Entity\InvoiceRow::update($idRow);
			$thisRowUpdateObj->hiddenOnRowId = $hiddenOnRowId;
			if (!\Entity\InvoiceRow::selectForUpdate($idRow)->rowCurrencyDetailsBlocked) {
				$thisRowUpdateObj->currencyDate = $rowDetails['currencyDate'];
				$thisRowUpdateObj->currencyType = $rowDetails['currencyType'];
			}
			if ($rowDetails['finalizeAdvanceMode']) {
				$thisRowUpdateObj->finalizeAdvanceMode = $rowDetails['finalizeAdvanceMode'];
			}

			$updateRows[] = $thisRowUpdateObj;
		}
		$this->invoiceUpdate->rows = $updateRows;

	}

}
