<?php

namespace App\Command;

use Pimcore\Console\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;

class GeneratorFormatCodeCommand extends AbstractCommand
{
	protected function configure()
	{
		$this
			->setName('generator:format-code')
			->setDescription('Formats the code using php-cs-fixer')
			->addOption(
				'dry-run', 'd',
				InputOption::VALUE_NONE,
				'Only shows which files would have been modified'
			);
	}

	protected function execute(InputInterface $input, OutputInterface $output)
	{
		$folders = [
			'app/config/pimcore',
			'install-profiles',
			'src',
		];
		$processedFiles = [];
		$projectPath = realpath(__DIR__.'/../../../');
		foreach ($folders as $folder) {
			$process = new Process(sprintf(
				'cd %s && php-cs-fixer fix %s -n --rules=\'%s\' --using-cache=no%s',
				$projectPath,
				$folder,
				'{"@PSR2":true,"@Symfony":true,"array_syntax":{"syntax":"short"},"indentation_type":false}',
				($input->getOption('dry-run')) ? ' --dry-run' : ''
			));
			try {
				if ($input->getOption('dry-run')) {
					$process->run();
					$exitCode = $process->getExitCode();
					if (0 != $exitCode && 8 != $exitCode) {
						throw new ProcessFailedException($process);
					}
				} else {
					$process->mustRun();
				}
				$rows = explode("\n", $process->getOutput());
				foreach ($rows as $i => $row) {
					if (false !== stristr($row, ')')) {
						$tmp = explode(' ', trim($row));
						end($tmp);
						$path = str_replace('//', '/', current($tmp));
						$processedFile = realpath($projectPath.'/'.$folder.'/'.$path);
						if (false === $processedFile) {
							$projectPathParent = realpath(__DIR__.'/../../../../');
							$processedFile = realpath($projectPathParent.'/'.$path);
						}
						if (false === $processedFile) {
							throw new \RuntimeException('Can\'t parse dry-run output in row: '.$row);
						}
						$processedFiles[] = $processedFile;
					}
				}
			} catch (\Exception $e) {
				$output->writeln('<error>'.$e->getMessage().'</error>');
				exit;
			}
		}
		if ($input->getOption('dry-run')) {
			$output->writeln("These files would be changed:\n".implode("\n", $processedFiles));
		} else {
			//replace spaces with tabs
			foreach ($processedFiles as $filePath) {
				$fileContents = file_get_contents($filePath);
				$fileRows = explode("\n", $fileContents);
				foreach ($fileRows as $i => $row) {
					$row = preg_replace("/\G\t/", '    ', $row);
					$fileRows[$i] = preg_replace("/\G {4}/", "\t", $row);
				}
				$fileContents2 = implode("\n", $fileRows);
				$handle = fopen($filePath, 'w');
				fwrite($handle, $fileContents2);
				fclose($handle);
			}
			// utilize git status to find out what really changed
			$process = new Process(sprintf(
				'cd %s && git status --porcelain',
				$projectPath
			));
			$process->mustRun();
			$rows = explode("\n", $process->getOutput());
			$gitChangedFiles = [];
			foreach ($rows as $i => $row) {
				$tmp = explode(' ', trim($row));
				end($tmp);
				$gitChangedFiles[] = realpath($projectPath.'/'.current($tmp));
			}
			$changedFiles = [];
			foreach ($processedFiles as $processedFile) {
				if (in_array($processedFile, $gitChangedFiles)) {
					$changedFiles[] = $processedFile;
				}
			}
			if ($changedFiles) {
				$output->writeln("These files have been changed:\n".implode("\n", $changedFiles));
			} else {
				$output->writeln('Nothing changed.');
			}
		}
	}
}
