<?php

namespace XF\Cli\Command\Development;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use XF\Mvc\Entity\Entity;

class GenerateSchemaEntity extends Command
{
	use RequiresDevModeTrait;

	protected function configure()
	{
		$this
			->setName('xf-dev:generate-schema-entity')
			->setDescription('Generates schema code from an entity')
			->addArgument(
				'id',
				InputArgument::REQUIRED,
				'Identifier for the Entity (Prefix:Type format)'
			);
	}

	protected function execute(InputInterface $input, OutputInterface $output)
	{
		$id = $input->getArgument('id');
		if (!$id || !preg_match('#^[a-z0-9_\\\\]+:[a-z0-9_\\\\]+$#i', $id))
		{
			$output->writeln("Identifier in the form of Prefix:Type must be provided.");
			return 1;
		}

		try
		{
			$entity = \XF::em()->create($id);
		}
		catch (\Exception $e)
		{
			$class = \XF::stringToClass($id, '%s\Entity\%s');
			$output->writeln("Entity class for $id ($class) could not be created.");
			return 2;
		}

		$structure = $entity->structure();

		$table = $structure->table;
		$primaryKey = $structure->primaryKey;
		$columns = $structure->columns;

		$primaryKeySet = false;
		$columnStrings = [];

		foreach ($columns AS $columnName => $column)
		{
			$type = $this->resolveTypeDefaults($entity, $column['type'], $unsigned, $allowedDefault);

			$length = null;
			if (isset($column['maxLength']))
			{
				$length = $column['maxLength'];
			}
			else
			{
				if ($type == 'varchar')
				{
					$type = 'text';
					$allowedDefault = false;
				}
			}

			$values = null;
			if (isset($column['allowedValues']))
			{
				$type = 'enum';
				if (count($column['allowedValues']) > 1)
				{
					$values = '[\'' . implode('\', \'', $column['allowedValues']) . '\']';
				}
				else
				{
					$values = '\'' . $column['allowedValues'] . '\'';
				}

				$length = null;
			}

			$string = '$table->addColumn(\'' . $columnName . '\', \'' . $type . '\'' . ($length ? ', ' . $length : '') . ')';

			if ($values)
			{
				$string .= '->values(' . $values . ')';
			}

			if ($unsigned !== null)
			{
				if ($unsigned === false)
				{
					$string .= '->unsigned(false)';
				}
			}

			if (isset($column['nullable']))
			{
				$string .= '->nullable()';
			}

			if (isset($column['default']) && $allowedDefault)
			{
				if ($column['default'] === \XF::$time)
				{
					$default = 0;
				}
				else if (is_string($column['default']))
				{
					$default = '\'' . $column['default'] . '\'';
				}
				else if (is_bool($column['default']))
				{
					$default = ($column['default'] === true) ? 1 : 0;
				}
				else
				{
					$default = $column['default'];
				}
				$string .= '->setDefault(' . $default . ')';
			}

			if (isset($column['autoIncrement']))
			{
				$string .= '->autoIncrement()';
				$primaryKeySet = true;
			}

			$string .= ';';

			$columnStrings[] = $string;
		}

		$primaryKeyString = '';
		if (!$primaryKeySet && $primaryKey)
		{
			$primaryKeyString = "\n\t";
			if (is_array($primaryKey) && count($primaryKey) > 1)
			{
				$primaryKeyString .= '$table->addPrimaryKey([\'' . implode('\', \'', $primaryKey) . '\']);';
			}
			else
			{
				$primaryKeyString .= '$table->addPrimaryKey(\'' . $primaryKey . '\');';
			}
		}

		$columnOutput = implode("\n\t", $columnStrings);

		$sm = <<< FUNCTION
\$this->schemaManager()->createTable('$table', function(\\XF\Db\Schema\Create \$table)
{
	{$columnOutput}{$primaryKeyString}
});
FUNCTION;

		$output->writeln(["", $sm, ""]);

		return 0;
	}

	protected function resolveTypeDefaults(Entity $entity, $type, &$unsigned = null, &$allowedDefault = true)
	{
		$unsigned = null;
		$allowedDefault = true;

		switch ($type)
		{
			case $entity::INT:
				$unsigned = false;
				return 'int';

			case $entity::UINT:
				return 'int';

			case $entity::FLOAT:
				return 'float';

			case $entity::BOOL:
				return 'tinyint';

			case $entity::STR:
				return 'varchar';

			case $entity::BINARY:
				return 'varbinary';

			case $entity::SERIALIZED:
			case $entity::SERIALIZED_ARRAY:
			case $entity::JSON:
			case $entity::JSON_ARRAY:
			case $entity::LIST_LINES:
			case $entity::LIST_COMMA:
				$allowedDefault = false;
				return 'blob';
		}

		throw new \InvalidArgumentException('Could not infer type.');
	}
}