<?php

namespace XF\DevelopmentOutput;

use XF\Finder\ClassExtensionFinder;
use XF\Mvc\Entity\Entity;
use XF\Util\Json;

class ClassExtension extends AbstractHandler
{
	protected function getTypeDir()
	{
		return 'class_extensions';
	}

	public function export(Entity $extension)
	{
		if (!$this->isRelevant($extension))
		{
			return true;
		}

		$fileName = $this->getFileName($extension);

		$keys = [
			'from_class',
			'to_class',
			'execute_order',
			'active',
		];
		$json = $this->pullEntityKeys($extension, $keys);

		$this->queueExtensionCacheRebuild($extension->addon_id);

		return $this->developmentOutput->writeFile(
			$this->getTypeDir(),
			$extension->addon_id,
			$fileName,
			Json::jsonEncodePretty($json)
		);
	}

	public function delete(Entity $entity, $new = true)
	{
		$return = parent::delete($entity, $new);
		if ($return)
		{
			$addOnId = $new ? $entity->getValue('addon_id') : $entity->getExistingValue('addon_id');
			$this->queueExtensionCacheRebuild($addOnId);
		}

		return $return;
	}

	protected function getEntityForImport($name, $addOnId, $json, array $options)
	{
		/** @var \XF\Entity\ClassExtension $extension */
		$extension = \XF::em()->getFinder(ClassExtensionFinder::class)->where([
			'from_class' => $json['from_class'],
			'to_class' => $json['to_class'],
		])->fetchOne();
		if (!$extension)
		{
			$extension = \XF::em()->create(\XF\Entity\ClassExtension::class);
		}

		$extension = $this->prepareEntityForImport($extension, $options);

		return $extension;
	}

	public function import($name, $addOnId, $contents, array $metadata, array $options = [])
	{
		$json = json_decode($contents, true);

		$extension = $this->getEntityForImport($name, $addOnId, $json, $options);

		$extension->bulkSetIgnore($json);
		$extension->addon_id = $addOnId;
		$extension->save();
		// this will update the metadata itself

		$this->queueExtensionCacheRebuild($extension->addon_id);

		return $extension;
	}

	protected function getFileName(Entity $extension, $new = true)
	{
		$fromClass = $new ? $extension->getValue('from_class') : $extension->getExistingValue('from_class');
		$toClass = $new ? $extension->getValue('to_class') : $extension->getExistingValue('to_class');

		$fromClass = ltrim(preg_replace('#[^a-z0-9_-]#i', '-', $fromClass), '-');
		$toClass = ltrim(preg_replace('#[^a-z0-9_-]#i', '-', $toClass), '-');

		return "{$fromClass}_{$toClass}.json";
	}

	protected function queueExtensionCacheRebuild($addOnId)
	{
		\XF::runOnce('classExtensionCacheRebuild-' . $addOnId, function () use ($addOnId)
		{
			$this->rebuildExtensionCache($addOnId);
		});
	}

	public function rebuildExtensionCache($addOnId)
	{
		$fileName = 'extension_hint.php';
		$finalOutput = $this->getExtensionCacheFileValue($addOnId);

		if ($finalOutput)
		{
			$this->developmentOutput->writeSpecialFile($addOnId, $fileName, $finalOutput);
		}
		else
		{
			$this->developmentOutput->deleteSpecialFile($addOnId, $fileName);
		}
	}

	public function getExtensionCacheFileValue($addOnId)
	{
		$finder = \XF::finder(ClassExtensionFinder::class)
			->where('addon_id', $addOnId)
			->order(['from_class', 'to_class', 'execute_order']);
		$extensions = $finder->fetch();

		$grouped = [];

		foreach ($extensions AS $extension)
		{
			$parts = explode('\\', $extension->to_class);

			$class = 'XFCP_' . array_pop($parts);
			$namespace = implode('\\', $parts);

			$grouped[ltrim($namespace, '\\')][] = [
				'class' => $class,
				'from_class' => '\\' . ltrim($extension->from_class, '\\'),
			];
		}

		$output = '';

		foreach ($grouped AS $namespace => $extensionValues)
		{
			$output .= "namespace {$namespace}\n";
			$output .= "{\n";

			foreach ($extensionValues AS $extensionValue)
			{
				$output .= "\tclass {$extensionValue['class']} extends {$extensionValue['from_class']} {}\n";
			}

			$output .= "}\n";
			$output .= "\n";
		}

		$output = rtrim($output);

		return <<<EOF
<?php

// ################## THIS IS A GENERATED FILE ##################
// DO NOT EDIT DIRECTLY. EDIT THE CLASS EXTENSIONS IN THE CONTROL PANEL.

/**
 * @noinspection PhpIllegalPsrClassPathInspection
 * @noinspection PhpMultipleClassesDeclarationsInOneFile
 */

{$output}

EOF;
	}
}
