<?php

namespace XF\Cli\Command\Development;

use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use XF\Cli\Command\AbstractCommand;
use XF\Repository\AddOnRepository;

use function count;

abstract class AbstractImportCommand extends AbstractCommand
{
	use RequiresDevModeTrait;

	// [command, name, entity, dir]
	abstract protected function getContentTypeDetails();

	abstract protected function getTitleIdMap($typeDir, $addOnId);

	public function importData($typeDir, $fileName, $path, $content, $addOnId, array $metadata)
	{
		$shortName = $this->getContentTypeDetails()['entity'];
		$id = preg_replace('/\.json$/', '', $fileName);
		\XF::app()->developmentOutput()->import($shortName, $id, $addOnId, $content, $metadata, [
			'import' => true,
		]);
		return $id;
	}

	protected function configure()
	{
		$contentType = $this->getContentTypeDetails();

		$this
			->setName("xf-dev:import-$contentType[command]")
			->setDescription("Imports $contentType[name] from development files")
			->addOption(
				'addon',
				null,
				InputOption::VALUE_REQUIRED,
				'Add-on to limit to importing (default: all)'
			);
	}

	protected function execute(InputInterface $input, OutputInterface $output)
	{
		$this->executeType($this->getContentTypeDetails(), $input, $output);

		return 0;
	}

	protected function executeType(array $contentType, InputInterface $input, OutputInterface $output)
	{
		$onlyAddOn = $input->getOption('addon');
		$start = microtime(true);

		$output->writeln("Importing $contentType[name]...");

		$db = \XF::db();
		$db->logQueries(false);
		$db->beginTransaction();

		$this->importDataForType(
			$contentType['dir'],
			$contentType['entity'],
			$onlyAddOn,
			$output
		);

		$this->afterExecuteType($contentType, $input, $output);

		$db->commit();

		\XF::triggerRunOnce();

		$output->writeln(sprintf(ucfirst($contentType['name']) . " imported. (%.02fs)", microtime(true) - $start));
	}

	protected function importDataForType($typeDir, $entity, $onlyAddOn, OutputInterface $output)
	{
		$devOutput = \XF::app()->developmentOutput();
		$allMetadata = $devOutput->getTypeMetadata($typeDir);
		$addOns = $devOutput->getAvailableTypeFilesByAddOn($typeDir);
		$installedAddOns = \XF::repository(AddOnRepository::class)->getInstalledAddOnData();

		foreach ($addOns AS $addOnId => $files)
		{
			if ($onlyAddOn && $onlyAddOn != $addOnId)
			{
				continue;
			}

			if (!isset($installedAddOns[$addOnId]))
			{
				$output->writeln("\tSkipping $addOnId - not installed.");
				continue;
			}

			$output->writeln("\tImporting $addOnId...");

			$metadata = $allMetadata[$addOnId] ?? [];
			$map = $this->importDataForAddOn($output, $typeDir, $files, $addOnId, $metadata);
			$this->deleteRemaining($typeDir, $map, $entity);

			$output->writeln("\tImported $addOnId.");
			$output->writeln("");
		}
	}

	protected function importDataForAddOn(OutputInterface $output, $typeDir, array $files, $addOnId, array $typeMetadata)
	{
		$map = $this->getTitleIdMap($typeDir, $addOnId);

		$printName = ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE);

		if (!$printName && $files)
		{
			$progress = new ProgressBar($output, count($files));
			$progress->start();
		}
		else
		{
			$progress = null;
		}

		foreach ($files AS $fileName => $path)
		{
			if ($printName)
			{
				$output->writeln("\t$fileName");
			}
			else if ($progress)
			{
				$progress->advance();
			}

			$metadata = $typeMetadata[$fileName] ?? [];
			$content = file_get_contents($path);

			$title = $this->importData($typeDir, $fileName, $path, $content, $addOnId, $metadata);

			unset($map[$title]);
		}

		if ($progress)
		{
			$progress->finish();
			$output->writeln("");
		}

		return $map;
	}

	protected function deleteRemaining($typeDir, array $map, $entity)
	{
		if ($map)
		{
			$old = \XF::em()->findByIds($entity, $map);
			foreach ($old AS $entity)
			{
				$entity->delete();
			}
		}
	}

	protected function afterExecuteType(array $contentType, InputInterface $input, OutputInterface $output)
	{

	}
}
