<?php
/**
 * Akeeba Backup Restoration Script
 *
 * @package   brs
 * @copyright Copyright (c)2025 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\BRS\Platform\Controller;

defined('_AKEEBA') or die();

use Akeeba\BRS\Framework\Mvc\Controller;
use Akeeba\BRS\Platform\Engine\TROMBETA\ErrorHandling\WarningException;
use Akeeba\BRS\Platform\Model\Configuration;
use Akeeba\BRS\Platform\Model\Replacedata as ReplacedataModel;
use Throwable;

/**
 * Controller for the Replace Data step on WordPress.
 *
 * @since  10.0
 */
class Replacedata extends Controller
{
	/**
	 * The main page.
	 *
	 * @return  void
	 * @since   10.0
	 */
	public function main(): void
	{
		$input = $this->getContainer()->get('input');

		/** @var ReplacedataModel $model */
		$model        = $this->getThisModel();
		$sameSiteURL  = $model->isSameSiteURL();
		$sameFileRoot = $model->isSameFilesystemRoot();

		// If we are restoring to the same URL and filesystem root (identical site) we don't need to replace any data
		if ($sameSiteURL && $sameFileRoot)
		{
			$this->setRedirect('index.php?view=finalise');

			return;
		}

		// Am I force reloading the data replacements?
		if ($input->getBool('force', false))
		{
			$session = $this->container->session;
			$session->set('dataReplacements', null);
		}

		parent::main();
	}

	/**
	 * Perform an AJAX operation (init, or step).
	 *
	 * @return  void
	 * @since   10.0
	 */
	public function ajax()
	{
		$input = $this->getContainer()->get('input');

		/** @var ReplacedataModel $model */
		$model   = $this->getThisModel();
		$method  = $input->getCmd('method', '');
		$session = $this->container->get('session');

		try
		{
			switch ($method)
			{
				case 'init':
					$model->setState('replaceFrom', trim($input->getRaw('replaceFrom', '')));
					$model->setState('replaceTo', trim($input->getRaw('replaceTo', '')));
					$model->setState('extraTables', $input->get('extraTables', [], 'raw'));
					// ⚠️ DO NOT CHANGE THE DEFAULT VALUE. Getting the state of a CB *MUST* use the default value 0.
					$model->setState('replaceguid', $input->getBool('replaceguid', 0));
					$model->setState('min_exec', $input->getInt('min_exec', 0));
					$model->setState('max_exec', $input->getInt('max_exec', 3));
					$model->setState('runtime_bias', $input->getInt('runtime_bias', 75));
					$model->setState('batchSize', $input->getInt('batchSize', 100));
					$model->setState('column_size', $input->getInt('column_size', 1048576));

					$engine = $model->init();
					break;

				case 'step':
					$engine = $model->step();
					break;
			}

			$session->saveData();

			$result = [
				'error'    => '',
				'msg'      => sprintf(
					'%s (%d)',
					$session->get('brs.replacements.currentTable', ''),
					$session->get('brs.replacements.currentOffset', '')
				),
				'more'     => $model->getState('more', true),
				'warnings' => $engine->getWarnings(),
			];

			// Perform finalization steps (file data replacement when we're done)
			if (!$result['more'])
			{
				$logger = $engine->getLogger();

				// First we need to update the multisite tables, if necessary.
				if ($model->isMultisite())
				{
					$logger->info('Updating multisite tables');
					$model->updateMultisiteTables();
				}
				$logger->info('Updating site options');
				$model->updateSiteOptions();
				$logger->info('Updating attachment GUIDs');
				$model->updateAttachmentGUIDs();
				$logger->info('Updating files');
				$model->updateFiles();
				$logger->info('Updating WP config file');
				$model->updateWPConfigFile();
			}
		}
		catch (Throwable $e)
		{
			$parts = explode(DIRECTORY_SEPARATOR, __DIR__);

			array_pop($parts);

			$installationParent = implode(DIRECTORY_SEPARATOR, $parts);

			$context = str_replace(
				$installationParent, '[SITE_ROOT]',
				'## ' . $e->getFile() . ':' . $e->getLine() . "\n" . $e->getTraceAsString()
			);
			$message = str_replace($installationParent, '[SITE_ROOT]', $e->getMessage());

			$result = [
				'error'         => $message,
				'msg'           => $e->getCode() . ': ' . $message,
				'more'          => false,
				'warnings'      => [],
				'error_context' => $context,
			];
		}

		@ob_clean();
		echo json_encode($result);

		$this->getContainer()->get('application')->close();
	}

	/**
	 * AJAX method to tell us if we need to perform data replacement.
	 *
	 * @return  void
	 * @since   10.0
	 */
	public function replaceneeded(): void
	{
		/** @var Configuration $config */
		$config = $this->getContainer()->get('mvcFactory')->model('Configuration');
		$result = true;

		/**
		 * When we initialised the Configuration model it read the `oldurl` from the database. However, at that
		 * point (in the "main" view) we had not already restored the database. Therefore, it was either unable to read
		 * anything, or it was reading false data from an existing database. As a resultm I need to reload that
		 * information and set it to the configuration object.
		 */
		$array = [];
		$config->addOptionsFromDatabase($array);

		if (isset($array['oldurl']))
		{
			$config->set('oldurl', $array['oldurl']);
			$config->saveToSession();
		}

		// These values are stored inside the session, after the setup step
		$old_url = $config->get('oldurl');
		$new_url = $config->get('siteurl');

		// If we are restoring to the same URL we don't need to replace any data
		if ($old_url == $new_url)
		{
			$result = false;
		}

		@ob_clean();
		echo json_encode($result);
	}
}