<?php

namespace XF\Entity;

use XF\Finder\LanguageFinder;
use XF\Job\Atomic;
use XF\Mvc\Entity\AbstractCollection;
use XF\Mvc\Entity\Entity;
use XF\Mvc\Entity\Structure;
use XF\Repository\LanguageRepository;
use XF\Repository\StyleRepository;
use XF\Service\Language\RebuildService;
use XF\Util\File;

use function in_array;

/**
 * COLUMNS
 * @property int|null $language_id
 * @property int $parent_id
 * @property array $parent_list
 * @property string $title
 * @property string $date_format
 * @property string $date_short_format
 * @property string $date_short_recent_format
 * @property string $time_format
 * @property string $currency_format
 * @property string $decimal_point
 * @property string $thousands_separator
 * @property array|null $phrase_cache
 * @property string $language_code
 * @property string $text_direction
 * @property int $week_start
 * @property string $label_separator
 * @property string $comma_separator
 * @property string $ellipsis
 * @property string $parenthesis_open
 * @property string $parenthesis_close
 * @property bool $user_selectable
 *
 * RELATIONS
 * @property-read Language|null $Parent
 * @property-read AbstractCollection<Phrase> $Phrases
 */
class Language extends Entity
{
	public function canEdit()
	{
		if (!$this->language_id && !\XF::$developmentMode)
		{
			return false;
		}

		return true;
	}

	public function isMaster()
	{
		return !$this->language_id;
	}

	protected function verifyParentId($parentId)
	{
		if ($this->isUpdate() && $parentId)
		{
			$parent = $this->_em->find(Language::class, $parentId);
			if (!$parent || in_array($this->language_id, $parent->parent_list))
			{
				$this->error(\XF::phrase('please_select_valid_parent_language'), 'parent_id');
				return false;
			}
		}

		return true;
	}

	protected function verifyUserSelectable($value)
	{
		if (!$value)
		{
			$defaultLanguage = $this->app()->options()->defaultLanguageId;
			if ($this->language_id == $defaultLanguage)
			{
				$this->error(\XF::phrase('it_is_not_possible_to_prevent_users_from_selecting_default_language'), 'user_selectable');
				return false;
			}
		}

		return true;
	}

	protected function rebuildLanguageCache()
	{
		$repo = $this->getLanguageRepo();

		\XF::runOnce('languageCacheRebuild', function () use ($repo)
		{
			$repo->rebuildLanguageCache();
		});
	}

	protected function _postSave()
	{
		if ($this->isChanged('parent_id'))
		{
			$this->getRebuildLanguageService()->rebuildFullParentList();
			$this->getRebuildPhraseService()->rebuildFullPhraseMap();

			$this->enqueueRebuild();
		}

		$this->rebuildLanguageCache();

		$cssRelated = [
			'decimal_point',
			'thousands_separator',
			'text_direction',
			'label_separator',
			'comma_separator',
			'ellipsis',
			'parenthesis_open',
			'parenthesis_close',
		];

		if ($this->isChanged($cssRelated))
		{
			// this invalidates the CSS cache so changes such LTR/RTL and various separators take effect properly
			\XF::repository(StyleRepository::class)->updateAllStylesLastModifiedDateLater();
		}
	}

	protected function _preDelete()
	{
		$languageCount = $this->finder(LanguageFinder::class)->total();
		if ($languageCount <= 1)
		{
			$this->error(\XF::phrase('it_is_not_possible_to_delete_last_language'));
		}

		if ($this->language_id == $this->app()->options()->defaultLanguageId)
		{
			$this->error(\XF::phrase('it_is_not_possible_to_remove_default_language'));
		}
	}

	protected function _postDelete()
	{
		$id = $this->language_id;
		$db = $this->db();

		$db->delete('xf_phrase_map', 'language_id = ?', $id);
		$db->delete('xf_phrase_compiled', 'language_id = ?', $id);

		$hasChildren = (bool) $db->update('xf_language', ['parent_id' => $this->parent_id], 'parent_id = ?', $id);

		$db->update(
			'xf_user',
			['language_id' => $this->app()->options()->defaultLanguageId],
			"language_id = ?",
			$id
		);

		foreach ($this->Phrases AS $phrase)
		{
			/** @var Phrase $phrase */
			$phrase->setOption('recompile', false);
			$phrase->setOption('recompile_include', false);
			$phrase->setOption('rebuild_map', false);
			$phrase->delete();
		}

		$this->deletePhraseGroupCache();
		$this->deleteCompiledTemplates();

		if ($hasChildren)
		{
			$this->getRebuildLanguageService()->rebuildFullParentList();
			$this->getRebuildPhraseService()->rebuildFullPhraseMap();

			$this->enqueueRebuild();
		}

		$this->rebuildLanguageCache();
	}

	protected function enqueueRebuild()
	{
		$this->app()->jobManager()->enqueueUnique('languageRebuild', Atomic::class, [
			'execute' => ['XF:PhraseRebuild', 'XF:TemplateRebuild'],
		]);
	}

	protected function deletePhraseGroupCache()
	{
		$path = 'code-cache://phrase_groups/l' . $this->language_id;
		File::deleteAbstractedDirectory($path);
	}

	protected function deleteCompiledTemplates()
	{
		$path = 'code-cache://templates/l' . $this->language_id;
		File::deleteAbstractedDirectory($path);
	}

	public static function getStructure(Structure $structure)
	{
		$structure->table = 'xf_language';
		$structure->shortName = 'XF:Language';
		$structure->primaryKey = 'language_id';
		$structure->columns = [
			'language_id' => ['type' => self::UINT, 'autoIncrement' => true, 'nullable' => true],
			'parent_id' => ['type' => self::UINT, 'default' => 0],
			'parent_list' => ['type' => self::LIST_COMMA, 'maxLength' => 100, 'default' => []],
			'title' => ['type' => self::STR, 'maxLength' => 50,
				'required' => 'please_enter_valid_title',
			],
			'date_format' => ['type' => self::STR, 'maxLength' => 30,
				'required' => 'please_enter_valid_date_format',
			],
			'date_short_format' => ['type' => self::STR, 'maxLength' => 30,
				'required' => 'please_enter_valid_date_format',
			],
			'date_short_recent_format' => ['type' => self::STR, 'maxLength' => 30,
				'required' => 'please_enter_valid_date_format',
			],
			'time_format' => ['type' => self::STR, 'maxLength' => 15,
				'required' => 'please_enter_valid_time_format',
			],
			'currency_format' => ['type' => self::STR, 'maxLength' => 20,
				'required' => 'please_enter_valid_currency_format',
			],
			'decimal_point' => ['type' => self::STR, 'maxLength' => 1, 'default' => '.'],
			'thousands_separator' => ['type' => self::STR, 'maxLength' => 1, 'default' => ','],
			'phrase_cache' => ['type' => self::JSON_ARRAY, 'default' => []],
			'language_code' => ['type' => self::STR, 'maxLength' => 25, 'default' => ''],
			'text_direction' => ['type' => self::STR, 'default' => 'LTR',
				'allowedValues' => ['LTR', 'RTL'],
			],
			'week_start' => ['type' => self::UINT, 'max' => 6, 'default' => 0],
			'label_separator' => ['type' => self::STR, 'maxLength' => 15, 'default' => ':'],
			'comma_separator' => ['type' => self::STR, 'maxLength' => 15, 'default' => ', '],
			'ellipsis' => ['type' => self::STR, 'maxLength' => 15, 'default' => '...'],
			'parenthesis_open' => ['type' => self::STR, 'maxLength' => 15, 'default' => '('],
			'parenthesis_close' => ['type' => self::STR, 'maxLength' => 15, 'default' => ')'],
			'user_selectable' => ['type' => self::BOOL, 'default' => true],
		];
		$structure->getters = [];
		$structure->relations = [
			'Parent' => [
				'entity' => 'XF:Language',
				'type' => self::TO_ONE,
				'conditions' => [
					['language_id', '=', '$parent_id'],
				],
				'primary' => true,
			],
			'Phrases' => [
				'entity' => 'XF:Phrase',
				'type' => self::TO_MANY,
				'conditions' => 'language_id',
			],
		];

		return $structure;
	}

	/**
	 * @return RebuildService
	 */
	protected function getRebuildLanguageService()
	{
		return $this->app()->service(RebuildService::class);
	}

	/**
	 * @return \XF\Service\Phrase\RebuildService
	 */
	protected function getRebuildPhraseService()
	{
		return $this->app()->service(\XF\Service\Phrase\RebuildService::class);
	}

	/**
	 * @return LanguageRepository
	 */
	protected function getLanguageRepo()
	{
		return $this->_em->getRepository(LanguageRepository::class);
	}
}
