<?php

namespace XF\Cli\Command;

use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use XF\AddOn\AddOn;
use XF\Util\Hash;

class FileCheck extends AbstractCommand
{
	use JobRunnerTrait;

	protected function configure()
	{
		$this
			->setName('xf:file-check')
			->setDescription('Performs a file health check')
			->addOption(
				'addon',
				'a',
				InputOption::VALUE_REQUIRED,
				'Add-on to limit to checking (default: all)'
			)
			->addOption(
				'log-results',
				'l',
				InputOption::VALUE_NONE,
				'Log file health check results. This produces no command output and cannot be used with --addon option.'
			);
	}

	protected function execute(InputInterface $input, OutputInterface $output)
	{
		$logResults = $input->getOption('log-results');

		if ($logResults)
		{
			$fileCheck = \XF::em()->create(\XF\Entity\FileCheck::class);
			$fileCheck->save();

			$this->setupAndRunJob('fileCheck', \XF\Job\FileCheck::class, [
				'check_id' => $fileCheck->check_id,
			], $output);

			$output->writeln(['File checking of all add-ons complete. Check the admin control panel for results.']);
			return 0;
		}

		$addOnInfo = [];

		$ds = \XF::$DS;
		$addOnId = $input->getOption('addon');
		if ($addOnId == 'XF')
		{
			$addOnInfo['XF'] = [
				'title' => 'XenForo',
				'hash' => \XF::getAddOnDirectory() . $ds . 'XF' . $ds . 'hashes.json',
			];
		}
		else if ($addOnId)
		{
			/** @var AddOn $addOn */
			$addOn = \XF::app()->addOnManager()->getById($addOnId);
			if (!$addOn)
			{
				$output->writeln('<error>' . \XF::phrase('please_enter_valid_addon_id') . '</error>');
				return 1;
			}
			$addOnInfo[$addOnId] = [
				'title' => $addOn->title,
				'hash' => $addOn->getHashesPath(),
			];
		}
		else
		{
			$addOns = \XF::app()->addOnManager()->getAllAddOns();
			foreach ($addOns AS $addOnId => $addOn)
			{
				if (!$addOn->isInstalled())
				{
					continue;
				}
				$addOnInfo[$addOnId] = [
					'title' => $addOn->title,
					'hash' => $addOn->getHashesPath(),
				];
			}
			$addOnInfo['XF'] = [
				'title' => 'XenForo',
				'hash' => \XF::getAddOnDirectory() . $ds . 'XF' . $ds . 'hashes.json',
			];
		}

		$results = [
			'missing' => [],
			'inconsistent' => [],
			'total_missing' => 0,
			'total_inconsistent' => 0,
			'total_checked' => 0,
		];

		$rootPrefix = \XF::getRootDirectory() . $ds;

		$hashesMissing = [];

		foreach ($addOnInfo AS $addOnId => $info)
		{
			if (!file_exists($info['hash']))
			{
				$hashesMissing[$addOnId] = $info;
				continue;
			}

			$json = json_decode(file_get_contents($info['hash']), true);
			foreach ((array) $json AS $info => $hash)
			{
				$path = $rootPrefix . $info;

				$results['total_checked']++;
				if (!file_exists($path))
				{
					$results['missing'][$addOnId][] = $info;
					$results['total_missing']++;
				}
				else if (Hash::hashTextFile($path, 'sha256') !== $hash)
				{
					$results['inconsistent'][$addOnId][] = $info;
					$results['total_inconsistent']++;
				}
			}
		}

		if ($hashesMissing)
		{
			$skipMissingHashes = \XF::app()->config['development']['enabled'];

			$output->writeln('<error>' . \XF::phrase('following_add_ons_missing_necessary_files_for_health_checking') . '</error>');
			foreach ($hashesMissing AS $addOn)
			{
				$output->writeln("\t * $addOn[title]");
			}

			if ($skipMissingHashes)
			{
				$output->writeln(\XF::phrase('execution_has_been_allowed_to_continue')->render());
			}
			else
			{
				return 1;
			}
		}

		if (!$results['total_missing'] && !$results['total_inconsistent'])
		{
			if (!$hashesMissing)
			{
				$output->writeln('<info>' . \XF::phrase('file_health_check_okay', ['numFiles' => $results['total_checked']]) . '</info>');
			}

			return 0;
		}
		$table = new Table($output);

		foreach ($addOnInfo AS $addOnId => $info)
		{
			if (isset($results['missing'][$addOnId]) || isset($results['inconsistent'][$addOnId]))
			{
				$table->setHeaders([
					[new TableCell($info['title'], ['colspan' => 2])],
					['File path', 'Status'],
				]);

				if (isset($results['inconsistent'][$addOnId]))
				{
					foreach ($results['inconsistent'][$addOnId] AS $path)
					{
						$table->addRow([$path, 'Inconsistent']);
					}

					if (isset($results['missing'][$addOnId]))
					{
						$table->addRow(new TableSeparator());
					}
				}
				if (isset($results['missing'][$addOnId]))
				{
					foreach ($results['missing'][$addOnId] AS $path)
					{
						$table->addRow([$path, 'Missing']);
					}
				}
			}
		}

		$output->writeln('<error>' . \XF::phrase('check_completed_on_x_files', ['total_checked' => $results['total_checked']]) . '</error>');

		$table->render();

		return 1;
	}
}
