<?php

// TODO: loop records-ul care se face aici sa il putem integra in loop records-ul de la fiecare sursa in parte sau loopRecords sa fie pasul/etapa lui standard pt fiecare raport; atentie nu toate rapoartele au nevoie de loopRecords

// TODO: la coloanele custom, sa pot sa indic tipul - eg. date, int, float, string(X), text

// TODO: bug: daca am o coloana custom care depinde de alta coloana custom, si asta din urma nu e selectata/vizibila - nu se include in raport si nu stie sa o determine nici pe aceasta din urma

// TODO: [notImportant] the key 't' - means the total is calculated for that column ; not yet implemented in the refactored code.


// skip this step if no custom columns were set
if (!$columnsFormula) {
	return true;
}

// the report table primary key
$tablePrimaryKey=str_replace('`', '', $CFG['reportsManagement']['adminReport']['tablePrimaryKey'][$reportSource]);


///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// LOOP THROUGH EACH REPORT RECORD AND RESOLVE THE FORMULA ////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

// get records count
$recordsCountSQL="SELECT COUNT(*) FROM `$reportTable`";
$recordsCount=$db->GetOne($recordsCountSQL);
// check sql
if ($db->ErrorNo()) killReport("Could not get records count. SQL: ".$recordsCountSQL);

///////////////////////////////////////////////////////////////////////////////////////////////////
// LOOP THROUGH EACH RECORD TO FILL IN CUSTOM COLUMNS /////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
$groupSize=1000;
for($startGroup=0;$startGroup<$recordsCount;$startGroup+=$groupSize) {

	// refresh report status
	refreshReportStatus('customColumns', 'populateCustomColumns', $startGroup/$recordsCount*100);

	// GET RECORDS TO BE UPDATED //////////////////////////////////////////////////////////////////
	$recordsListSQL = "SELECT * FROM `$reportTable` WHERE 1 LIMIT ".$startGroup.", ".$groupSize;
	$recordsList=$db->GetAll($recordsListSQL);

	// check SQL
	if ($db->ErrorNo()) killReport("Could not run SQL in report. SQL: ".$recordsListSQL);

	// UPDATE RECORDS ONE BY ONE //////////////////////////////////////////////////////////////////
	foreach($recordsList as $thisRow) {

		$updateRecordArr = [];

		// a list of price columns used in formulas (each new custom column will be added in the list)
		$custColsPriceColumns=$priceColumns;

		foreach($columnsFormula as $custCol=>$colDet) {

			$thisColValue='';

			switch($colDet['type']) {

				// MATH COLUMNS ///////////////////////////////////////////////////////////////////
				case 'math': {

					// get the current formula
					$formula=$colDet['formula'];

					foreach($custColsPriceColumns as $col) {
						$formula=str_replace('%'.$col.'%', '('.(Float)$thisRow[$col].')', $formula);
					}

					// there should not be any "%" symbols
					if (strpos($formula, '%')!==false) {
						killReport("Could not execute math custom column formula; formula=`{$formula}` / column=`{$custCol}` on record id '{$thisRow[$tablePrimaryKey]}'. This looks like a formula depending on other custom columns - check the columns ordering both when they are defined and in the `columns order` step in admin reports.");
					}

					$thisColValue=0;
					try {
						if (eval("\$thisColValue=$formula;")!==null) {
							killReport("Could not execute math custom column formula update; formula=`{$formula}` / column=`{$custCol}` on record id '{$thisRow[$tablePrimaryKey]}'.");
						}
					}
					catch(\DivisionByZeroError $e) {
						// do nothing, also do not stop report execution
					}
					break;
				}


				// ARRAY VALUE CUSTOM COLUMN //////////////////////////////////////////////////////
				case 'arrayValue': {

					// if these remarks exist
					if ($thisRow[$colDet['arrSrc']]) {

						$arrVals=string2array($thisRow[$colDet['arrSrc']]);
						if (!$arrVals) {
							$arrVals = @unserialize($thisRow[$colDet['arrSrc']]);
						}
						if (!$arrVals) {
							$arrVals = json_decode($thisRow[$colDet['arrSrc']], true);
						}

						// if some values extracted...
						if ($arrVals) {

							// apply all keys sequentially
							$elemOk = true; // indicates that the seauence of keys exists correctly
							foreach (explode('.', $colDet['arrKey']) as $key) {
								if (!is_array($arrVals) or !array_key_exists($key, $arrVals)) {
									$elemOk = false;
									break;
								}
								$arrVals = $arrVals[$key];
							}

							// load the final value (if ok and scalar value)
							if ($elemOk and is_scalar($arrVals)) {
								$thisColValue = $arrVals;
							}

						}

					}

					break;
				}


				// REMARK VALUE CUSTOM COLUMN /////////////////////////////////////////////////////
				case 'remark': {

					// if these remarks exist
					if ($thisRow[$colDet['rmSrc']]) {

						$rmVals=string2array($thisRow[$colDet['rmSrc']]);
						if (!$rmVals) {
							$rmVals = @unserialize($thisRow[$colDet['rmSrc']]);
						}
						if (!$rmVals) {
							$rmVals = json_decode($thisRow[$colDet['rmSrc']], true);
						}

						// if rm vals exist and the needed key is set - get that value
						if ($rmVals and array_key_exists($colDet['rmVar'], $rmVals)) {
							$thisColValue=$rmVals[$colDet['rmVar']];
						}

					}

					break;
				}

				// CORE ENTITY CUSTOM COLUMN //////////////////////////////////////////////////////
				case 'entity': {

					if ($thisRow[$colDet['idEnt']]) {

						$class='\Entity\\'.$colDet['entType'];

						// instantiate entity object
						$entObj=$class::select($thisRow[$colDet['idEnt']]);

						// get the attributes
						$attributes=explode('.', $colDet['attr']);

						// execute the attribute one by one
						$cellValue=$entObj;
						foreach($attributes as $attr) {
							// check if the current object is still an Entity extension
							if ( !($cellValue instanceof \App\Entity\SelectAbstract) ) {
								killReport("The entity custom column is not correctly configured(1): class={$class}, attrs:{$colDet['attr']}");
							}
							try {
								$cellValue=$cellValue->$attr;
							} catch (\Exception $e) {
								killReport("The entity custom column is not correctly configured(2); class={$class}, attrs:{$colDet['attr']}, failedAttr:{$attr}");
							}
						}
						// if, in the end, we still have an object, kill the report
						if (is_object($cellValue)) {
							killReport("The entity custom column is not correctly configured(3) - the last attribute represents an object not a scalar value: class={$class}, attrs:{$colDet['attr']}");
						}

						$thisColValue=$cellValue;
					}

					break;
				}

				// RD ENTITY CUSTOM COLUMN ////////////////////////////////////////////////////////
				case 'entityValue': { // TODO: apparently the entity id is not validated - you can add anything as the column "id"

					if ($thisRow[$colDet['idEnt']]) {

						$class = "\\App\\ReportsData\\EntityFields\\".$colDet['entType'];

						$entObj = new $class($thisRow[$colDet['idEnt']]);

						// get the attributes
						$attributes=explode('.', $colDet['attr']);

						// execute the attribute one by one
						$cellValue = $entObj;
						foreach($attributes as $attr) {

							// check if the current object is still an Entity extension
							if ( !($cellValue instanceof \App\ReportsData\EntityFields\GeneralAbstract) ) {
								killReport("The entity custom column is not correctly configured(1): class={$class}, attrs:{$colDet['attr']}");
							}
							try {
								$cellValue = $cellValue->getValue($attr);
							} catch (\Exception $e) {
								killReport("The entity custom column is not correctly configured(2); class={$class}, attrs:{$colDet['attr']}, failedAttr:{$attr}. Error message: {$e->getMessage()}");
							}
						}
						// if, in the end, we still have an object, kill the report
						if (is_object($cellValue)) {
							killReport("The entity custom column is not correctly configured(3) - the last attribute represents an object not a scalar value: class={$class}, attrs:{$colDet['attr']}");
						}

						$thisColValue = $cellValue;

					}

					break;
				}

				default: {
					killReport("Invalid custom column formula type: {$colDet['type']}.");
				}

			}

			// add this new custom column to price cols - it may be used in the future
			$custColsPriceColumns[]=$custCol;

			// update the $updateRecordArr and also the current record values
			$thisRow[$custCol]=$thisColValue; // so this can be used in other formulas
			if (strlen($thisColValue)>0) {
				$updateRecordArr[]="`{$custCol}`='{$thisColValue}'";
			}

		}


		// UPDATE THE CURRENT RECORD //////////////////////////////////////////////////////////////
		if ($updateRecordArr) {
			$updateReportRowSQL="UPDATE `$reportTable` SET ".implode(', ', $updateRecordArr)." WHERE `$tablePrimaryKey`='{$thisRow[$tablePrimaryKey]}'";
			if (!$db->Execute($updateReportRowSQL)) {
				killReport("Could not execute custom column formula update. SQL: ".$updateReportRowSQL);
			}
		}


	}

	// force that all those entity caches are stored in local memory (some columns still use the formulas on old Entity)
	\App\Entity\Cache::getInstance()->flush();
	\App\Entity\Cache::getInstance()->flushInternal();

}

// clean all RD cache
\App\ReportsData\EntityFields\Cache::getInstance()->flushAll();
