<?php

namespace XF;

use XF\Db\DuplicateKeyException;
use XF\Entity\User;
use XF\Mvc\Entity\Entity;
use XF\Repository\DraftRepository;

use function array_key_exists, array_slice, is_array;

class Draft implements \ArrayAccess
{
	/**
	 * @var \XF\Entity\Draft|null
	 */
	protected $draft;

	public function __construct(?\XF\Entity\Draft $draft = null)
	{
		$this->draft = $draft;
	}

	/**
	 * @return \XF\Entity\Draft|null
	 */
	public function getDraftEntity()
	{
		return $this->draft;
	}

	public function getDraftKey()
	{
		return $this->draft ? $this->draft->draft_key : null;
	}

	public function exists()
	{
		return $this->draft && $this->draft->exists();
	}

	public function save()
	{
		if ($this->draft)
		{
			try
			{
				return $this->draft->save();
			}
			catch (DuplicateKeyException $e)
			{
				return false;
			}
		}
		else
		{
			return false;
		}
	}

	public function delete()
	{
		if ($this->draft && $this->draft->exists())
		{
			return $this->draft->delete();
		}
		else
		{
			return false;
		}
	}

	public function __get($key)
	{
		return $this->offsetGet($key);
	}

	#[\ReturnTypeWillChange]
	public function offsetGet($key)
	{
		if (!$this->draft)
		{
			return null;
		}

		switch ($key)
		{
			case 'message':
			case 'extra_data':
				return $this->draft->getValue($key);

			default:
				$extraData = $this->draft->extra_data;
				return $extraData[$key] ?? null;
		}
	}

	public function __set($key, $value)
	{
		$this->offsetSet($key, $value);
	}

	public function offsetSet($key, $value): void
	{
		if (!$this->draft)
		{
			return;
		}

		switch ($key)
		{
			case 'message':
			case 'extra_data':
				$this->draft->set($key, $value);
				break;

			default:
				$this->draft->setExtraData($key, $value);
		}
	}

	public function set($key, $value)
	{
		$this->offsetSet($key, $value);
	}

	public function bulkSet(array $values)
	{
		foreach ($values AS $key => $value)
		{
			$this->offsetSet($key, $value);
		}
	}

	public function offsetExists($key): bool
	{
		if (!$this->draft)
		{
			return false;
		}

		switch ($key)
		{
			case 'message':
			case 'extra_data':
				return true;

			default:
				return array_key_exists($key, $this->draft->extra_data);
		}
	}

	public function offsetUnset($key): void
	{
		if (!$this->draft)
		{
			return;
		}

		switch ($key)
		{
			case 'message':
				$this->draft->message = '';
				break;

			case 'extra_data':
				$this->draft->extra_data = [];
				break;

			default:
				$this->draft->unsetExtraData($key);
		}
	}

	/**
	 * @param Entity $entity
	 * @param string $relationKey
	 * @param User|null $user
	 *
	 * @return Draft
	 */
	public static function createFromEntity(Entity $entity, $relationKey, ?User $user = null)
	{
		if (!$entity->isValidRelation($relationKey))
		{
			throw new \LogicException("Invalid draft relation key '{$relationKey}'");
		}

		$draftCondition = $entity->structure()->relations[$relationKey]['conditions'][0];
		if (!$draftCondition
			|| !is_array($draftCondition)
			|| $draftCondition[0] != 'draft_key'
			|| $draftCondition[1] != '='
		)
		{
			throw new \LogicException("Could not find expected draft_key condition; expected [draft_key, =, ...]");
		}

		$user = $user ?: \XF::visitor();
		if (!$user->user_id)
		{
			// guests can never have a draft
			$draft = null;
		}
		else
		{
			$relation = $entity->getRelation($relationKey);
			if (isset($relation[$user->user_id]))
			{
				$draft = $relation[$user->user_id];
			}
			else
			{
				$draftKey = '';
				foreach (array_slice($draftCondition, 2) AS $v)
				{
					if ($v && $v[0] == '$')
					{
						$v = $entity->getValue(substr($v, 1));
						if ($v === null)
						{
							return null;
						}
						$draftKey .= $v;
					}
					else
					{
						$draftKey .= $v;
					}
				}

				$draft = \XF::em()->instantiateEntity(\XF\Entity\Draft::class);
				$draft->user_id = $user->user_id;
				$draft->draft_key = $draftKey;
			}
		}

		$class = \XF::extendClass(Draft::class);
		return new $class($draft);
	}

	/**
	 * @param string $draftKey
	 * @param User|null $user
	 *
	 * @return Draft
	 */
	public static function createFromKey($draftKey, ?User $user = null)
	{
		$user = $user ?: \XF::visitor();

		$draft = \XF::repository(DraftRepository::class)->getDraftByKeyAndUser($draftKey, $user);
		if (!$draft && $user->user_id)
		{
			$draft = \XF::em()->instantiateEntity(\XF\Entity\Draft::class);
			$draft->user_id = $user->user_id;
			$draft->draft_key = $draftKey;
		}

		$class = \XF::extendClass(Draft::class);
		return new $class($draft);
	}
}
