<?php
namespace App\Cron\Job;

/**
 * Cleanup export data logs
 * @author Laura Condrea
 *
 * Deletes data from exportDataLogs and context files + export data files/xml
 * It should run once per week or per month, outside working hours
 *
 * @see DatabaseCleanupForCronJobLogs
 */
class DatabaseCleanupForExportData extends \App\Cron\Job\Utils\ShortRunningCronJobAbstract
{
	/**
	 * Cronjob category: {system|operational|financial|reporting}
	 * @var string
	 */
	protected $category = 'system';

	public function validateSettings(\App\Entity\Messages $msgs, array $fields)
	{

		// CHECK THAT THE CRON DOES NOT RUN MORE THAN ONCE PER DAY --------------------------------
		if ($fields['scheduleOptions']['type']!='scheduled') {
			$msgs->addWarningText('The cron can run only once per day or less.');
		}

	}

	/**
	 * The method returns the form which is needed for the current cronjob settings saving.
	 */
	public function getSettingsDialog(array $fields)
	{
		$form = parent::getSettingsDialog($fields);

		$_LANG = \App\Loader_Lang::getInstance()->getGlobal();
		$lang = \App\Loader_Lang::getInstance()->getSection("Admin/CronJobs/Management/DatabaseCleanupForExportData");

		$formContainer = \Engine\Form\Container\Fieldset::create($_LANG['Admin']['CronJobs']['Management']['formFields']['legend']);

		$form->addContainer($formContainer);

		$fld = new \Engine\Form\Input\Select($lang['options']['exportDataLogsPeriod'], 'exportDataLogsPeriod', $fields['settings']['exportDataLogsPeriod'], [
			'6m' =>   	$lang['options']['exportDataLogsPeriod_values_6m'],
			'1y' =>   	$lang['options']['exportDataLogsPeriod_values_1y'],
			'1.5y' =>   $lang['options']['exportDataLogsPeriod_values_1.5y'],
			'2y' =>   	$lang['options']['exportDataLogsPeriod_values_2y'],
			'2.5y' =>   $lang['options']['exportDataLogsPeriod_values_2.5y'],
			'3y' =>   	$lang['options']['exportDataLogsPeriod_values_3y'],
		]);

		$fld->showDefaultOption();
		$fld->setTooltip($lang['options']['exportDataLogsPeriodTooltip']);
		$formContainer->addElement($fld);

		$fld = new \Engine\Form\Input\Select($lang['options']['exportDataXmlsPeriod'], 'exportDataXmlsPeriod', $fields['settings']['exportDataXmlsPeriod'], [
			'1y' =>   	$lang['options']['exportDataXmlsPeriod_values_1y'],
			'2y' =>   	$lang['options']['exportDataXmlsPeriod_values_2y'],
			'3y' =>   	$lang['options']['exportDataXmlsPeriod_values_3y'],
			'4y' =>   	$lang['options']['exportDataXmlsPeriod_values_4y'],
			'5y' =>   	$lang['options']['exportDataXmlsPeriod_values_5y'],
			'6y' =>   	$lang['options']['exportDataXmlsPeriod_values_6y'],
			'7y' =>   	$lang['options']['exportDataXmlsPeriod_values_7y'],
			'8y' =>   	$lang['options']['exportDataXmlsPeriod_values_8y'],
		]);

		$fld->showDefaultOption();
		$fld->setTooltip($lang['options']['exportDataXmlsPeriodTooltip']);
		$formContainer->addElement($fld);

		$formContainer->groupInputsInArray("settings");

		return $form;
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Cron\Job\Utils\GeneralAbstract::executeJob()
	 */
	protected function executeJob()
	{
		///////////////////////////////////////////////////////////////////////////////////////////
		// EXPORT DATA LOGS + LOG CONTEXT FILES ///////////////////////////////////////////////////
		///////////////////////////////////////////////////////////////////////////////////////////

		// For each selected retention period, we ensure to keep full months of data
		$minAddTime = null;
		switch ($this->settings['exportDataLogsPeriod']) {
			case '':
				$minAddTime = null;
				break;
			case '6m':
				$minAddTime = \Engine\DateTime::create("-6 months")->format("Y-m-01 00:00:00");
				break;
			case '1y':
				$minAddTime = \Engine\DateTime::create("-12 months")->format("Y-m-01 00:00:00");
				break;
			case '1.5y':
				$minAddTime = \Engine\DateTime::create("-18 months")->format("Y-m-01 00:00:00");
				break;
			case '2y':
				$minAddTime = \Engine\DateTime::create("-24 months")->format("Y-m-01 00:00:00");
				break;
			case '2.5y':
				$minAddTime = \Engine\DateTime::create("-30 months")->format("Y-m-01 00:00:00");
				break;
			case '3y':
				$minAddTime = \Engine\DateTime::create("-36 months")->format("Y-m-01 00:00:00");
				break;

			default:
				throw new \Exception("Unknown exportDataLogsPeriod value: {$this->settings['errorLogsPeriod']}");
		}

		if ($minAddTime) {
			$this->logger->info(sprintf("Searching export data logs before %s", $minAddTime));
			$this->cleanupOldExportDataLogsFromDB($minAddTime);
			$this->cleanupOldExportDataLogsFromDisk($minAddTime);
		}
		else {
			$this->logger->idle("No export data logs period selected.");
		}

		///////////////////////////////////////////////////////////////////////////////////////////
		// EXPORT DATA XML FILES //////////////////////////////////////////////////////////////////
		///////////////////////////////////////////////////////////////////////////////////////////

		// For each selected retention period, we ensure to keep full months of data
		$minAddTime = null;
		switch ($this->settings['exportDataXmlsPeriod']) {
			case '':
				$minAddTime = null;
				break;
			case '1y':
				$minAddTime = \Engine\DateTime::create("-1 years")->format("Y-m-01 00:00:00");
				break;
			case '2y':
				$minAddTime = \Engine\DateTime::create("-2 years")->format("Y-m-01 00:00:00");
				break;
			case '3y':
				$minAddTime = \Engine\DateTime::create("-3 years")->format("Y-m-01 00:00:00");
				break;
			case '4y':
				$minAddTime = \Engine\DateTime::create("-4 years")->format("Y-m-01 00:00:00");
				break;
			case '5y':
				$minAddTime = \Engine\DateTime::create("-5 years")->format("Y-m-01 00:00:00");
				break;
			case '6y':
				$minAddTime = \Engine\DateTime::create("-6 years")->format("Y-m-01 00:00:00");
				break;
			case '7y':
				$minAddTime = \Engine\DateTime::create("-7 years")->format("Y-m-01 00:00:00");
				break;
			case '8y':
				$minAddTime = \Engine\DateTime::create("-8 years")->format("Y-m-01 00:00:00");
				break;

			default:
				throw new \Exception("Unknown exportDataXmlsPeriod value: {$this->settings['exportDataXmlsPeriod']}");
		}

		if ($minAddTime) {
			$this->logger->info(sprintf("Searching export data xml files before %s", $minAddTime));
			$this->cleanupOldExportDataXmlsFromDisk($minAddTime);
		}
		else {
			$this->logger->idle("No export data xml files period selected.");
		}
	}

	protected function cleanupOldExportDataLogsFromDB($minAddTime)
	{
		// get record count
		global $DBTExportData_logs;
		$db = \DB::getInstance();

		$errorLogsCount = $db->GetOne("SELECT COUNT(*) FROM `{$DBTExportData_logs}` WHERE `addTime`<?", [$minAddTime,]);
		if ($db->ErrorNo()) {
			throw new \DBException(sprintf("[%s] Cannot get records count. Error: %s", __METHOD__, $db->ErrorMsg()));
		}

		$errorLogsMinAddTime = $db->GetOne("SELECT MIN(`addTime`) FROM `{$DBTExportData_logs}` WHERE `addTime`<?", [$minAddTime,]);
		if ($db->ErrorNo()) {
			throw new \DBException(sprintf("[%s] Cannot select data. Error: %s", __METHOD__, $db->ErrorMsg()));
		}

		$errorLogsMinId = $db->GetOne("SELECT MIN(`id`) FROM `{$DBTExportData_logs}` WHERE `addTime`<?", [$minAddTime,]);
		if ($db->ErrorNo()) {
			throw new \DBException(sprintf("[%s] Cannot select data. Error: %s", __METHOD__, $db->ErrorMsg()));
		}

		if ($errorLogsCount) {
			$this->logger->info(sprintf("The exportDataLogs table contains %s records. First record is from %s (id: %s)", $errorLogsCount, $errorLogsMinAddTime, $errorLogsMinId));
		}

		$db->Execute("DELETE FROM `{$DBTExportData_logs}` WHERE `addTime`<?", [$minAddTime,]);
		if ($db->ErrorNo()) {
			throw new \DBException(sprintf("[%s] Cannot delete records. Error: %s", __METHOD__, $db->ErrorMsg()));
		}

		$deletedRecords = $db->Affected_Rows();
		$this->logger->success(sprintf("Deleted %s export data logs from the DB", $deletedRecords));
	}

	protected function cleanupOldExportDataLogsFromDisk($minAddTime)
	{
		$CFG = \Engine\Loader_Cfg::getInstance()->getGlobal();

		$exportDataLogOutputDir = $CFG['exportData']['logContextFiles'];
		if (!\Engine\Dir::check($exportDataLogOutputDir)) {
			$this->logger->error("Export data output folder doesn't exist or is not writable. I have nothing to delete");
			return;
		}

		$minAddTimeYm = \Engine\DateTime::createFromIsoDatetime($minAddTime)->format("Ym");

		$months = \Engine\Dir::getContents($exportDataLogOutputDir, 'dir');
		foreach ($months as $month) {
			if ($month < $minAddTimeYm) {
				$monthFolder = sprintf("%s/%s/", $exportDataLogOutputDir, $month);

				if (\Engine\Dir::delete($monthFolder)) {
					$this->logger->info(sprintf("Deleted log month folder '%s'", $month));
				}
				else {
					$this->logger->warning(sprintf("Cannot delete log month folder '%s'", $month));
				}
			}
		}
	}

	protected function cleanupOldExportDataXmlsFromDisk($minAddTime)
	{
		$CFG = \Engine\Loader_Cfg::getInstance()->getGlobal();

		$exportDataOutputDir = $CFG['exportData']['queuePath'];
		if (!\Engine\Dir::check($exportDataOutputDir)) {
			$this->logger->error("Export data output folder doesn't exist or is not writable. I have nothing to delete");
			return;
		}

		///////////////////////////////////////////////////////////////////////////////////////////
		// DELETE FILES FROM MAIN DIRECTORY ///////////////////////////////////////////////////////
		///////////////////////////////////////////////////////////////////////////////////////////
		$this->deleteFilesFromPath($exportDataOutputDir, "main", $minAddTime);

		///////////////////////////////////////////////////////////////////////////////////////////
		/// DELETE FILES FROM CONFIRMED FOLDER ////////////////////////////////////////////////////
		///////////////////////////////////////////////////////////////////////////////////////////
		$exportDataConfirmedDir = $exportDataOutputDir."confirmed/";
		if (!\Engine\Dir::check($exportDataConfirmedDir)) {
			$this->logger->info("Export data `confirmed` folder doesn't exist or is not writable. I have nothing to delete");
			return;
		}

		$minAddTimeYm = \Engine\DateTime::createFromIsoDatetime($minAddTime)->format("Ym");
		$months = \Engine\Dir::getContents($exportDataConfirmedDir, 'dir');
		foreach ($months as $month) {
			if ($month < $minAddTimeYm) {
				$monthFolder = sprintf("%s/%s/", $exportDataConfirmedDir, $month);

				if (\Engine\Dir::delete($monthFolder)) {
					$this->logger->info(sprintf("Deleted `confirmed` folder - '%s'", $month));
				}
				else {
					$this->logger->warning(sprintf("Cannot delete `confirmed` folder - '%s'", $month));
				}
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////
		/// DELETE FILES FROM FAILED FOLDER ///////////////////////////////////////////////////////
		///////////////////////////////////////////////////////////////////////////////////////////
		$this->deleteFilesFromPath($exportDataOutputDir."failed/", "failed", $minAddTime);

		///////////////////////////////////////////////////////////////////////////////////////////
		/// DELETE FILES FROM DELETED FOLDER //////////////////////////////////////////////////////
		///////////////////////////////////////////////////////////////////////////////////////////
		$this->deleteFilesFromPath($exportDataOutputDir."deleted/", "deleted", $minAddTime);
	}

	private function deleteFilesFromPath(string $dir, string $dirName, string $minAddTime)
	{
		if (!\Engine\Dir::check($dir)) {
			$this->logger->error(sprintf("Export data `%s` folder doesn't exist or is not writable. I have nothing to delete", $dirName));
			return;
		}

		$removedFiles = 0;
		$failedToDelete = 0;
		$files = \Engine\Dir::getContents($dir, 'file');
		foreach ($files as $file) {
			if (substr($file, 0,10) < $minAddTime) {
				$logPath = sprintf("%s/%s", $dir, $file);

				if (\Engine\File::delete($logPath)) {
					$removedFiles++;
				}
				else {
					$failedToDelete++;
				}
			}
		}

		$this->logger->success(sprintf("Deleted %s files in `%s` folder.", $removedFiles, $dirName));
		if ($failedToDelete) {
			$this->logger->warning(sprintf("Failed to delete %s files from `%s` folder.", $failedToDelete, $dirName));
		}
	}
}
