<?php

namespace XF\Entity;

use XF\Mvc\Entity\Entity;
use XF\Mvc\Entity\Structure;
use XF\Util\File;

/**
 * COLUMNS
 * @property int|null $oembed_id
 * @property string $media_site_id
 * @property string $media_id
 * @property string $media_hash
 * @property string|null $title
 * @property int $fetch_date
 * @property int $first_request_date
 * @property int $last_request_date
 * @property int $views
 * @property bool $pruned
 * @property int $is_processing
 * @property int $failed_date
 * @property int $fail_count
 *
 * GETTERS
 * @property-read string $url
 *
 * RELATIONS
 * @property-read BbCodeMediaSite|null $BbCodeMediaSite
 * @property-read \XF\Mvc\Entity\AbstractCollection<\XF\Entity\OembedReferrer> $Referrers
 */
class Oembed extends Entity
{
	public const FAILURE = 1;

	protected $failure = null;

	public function getAbstractedJsonPath()
	{
		return sprintf(
			'internal-data://oembed_cache/%d/%d-%s.json',
			floor($this->oembed_id / 1000),
			$this->oembed_id,
			$this->media_hash
		);
	}

	public function isValid()
	{
		if ($this->pruned)
		{
			return false;
		}

		return $this->app()->fs()->has($this->getAbstractedJsonPath());
	}

	public function setAsFailure()
	{
		$this->failure = self::FAILURE;
		$this->setReadOnly(true);
	}

	public function isFailure()
	{
		return $this->failure ? true : false;
	}

	public function isRefreshRequired()
	{
		$filePath = $this->getAbstractedJsonPath();
		$fs = $this->app()->fs();

		if ($this->is_processing && \XF::$time - $this->is_processing < 5)
		{
			if ($fs->has($filePath))
			{
				return false;
			}

			$maxSleep = 5 - (\XF::$time - $this->is_processing);
			for ($i = 0; $i < $maxSleep; $i++)
			{
				if ($fs->has($filePath))
				{
					return false;
				}
			}
		}

		if ($this->failed_date && $this->fail_count)
		{
			return $this->isFailureRefreshRequired();
		}

		if ($this->pruned)
		{
			return true;
		}

		$ttl = $this->app()->options()->oEmbedCacheTTL;
		if ($ttl && $this->fetch_date < \XF::$time - $ttl * 86400)
		{
			return true;
		}

		if (!$fs->has($filePath))
		{
			return true;
		}

		$refresh = $this->app()->options()->oEmbedCacheRefresh;
		if ($refresh && !$this->fail_count && $this->fetch_date < \XF::$time - $refresh * 86400)
		{
			return true;
		}

		return false;
	}

	/**
	 * @return string
	 */
	public function getUrl()
	{
		return str_replace('{$id}', $this->media_id, $this->BbCodeMediaSite->oembed_url_scheme);
	}

	public function isFailureRefreshRequired()
	{
		if (!$this->failed_date || !$this->fail_count)
		{
			return false;
		}

		switch ($this->fail_count)
		{
			case 1: $delay = 30; break; // 30 seconds
			case 2: $delay = 2.5 * 60; break; // 2.5 minutes
			case 3: $delay = 10 * 60; break; // 10 minutes
			case 4: $delay = 1800; break; // 30 mins
			case 5: $delay = 2 * 3600; break; // 2 hours

			default:
				$delay = ($this->fail_count - 5) * 86400; // 1, 2, 3... days
		}

		return \XF::$time >= ($this->failed_date + $delay);
	}

	public function prune()
	{
		$this->pruned = true;
		$this->save();

		File::deleteFromAbstractedPath($this->getAbstractedJsonPath());

		return true;
	}

	protected function _preSave()
	{
		if ($this->isChanged('media_site_id') || $this->isChanged('media_id'))
		{
			$this->media_hash = md5($this->media_site_id . $this->media_id);
		}
	}

	public static function getStructure(Structure $structure)
	{
		$structure->table = 'xf_oembed';
		$structure->shortName = 'XF:Oembed';
		$structure->primaryKey = 'oembed_id';
		$structure->columns = [
			'oembed_id' => ['type' => self::UINT, 'nullable' => true, 'autoIncrement' => true],
			'media_site_id' => ['type' => self::STR, 'maxLength' => 25, 'required' => true],
			'media_id' => ['type' => self::STR, 'maxLength' => 250, 'required' => true],
			'media_hash' => ['type' => self::STR, 'maxLength' => 32, 'required' => true],
			'title' => ['type' => self::STR, 'nullable' => true , 'default' => null],
			'fetch_date' => ['type' => self::UINT, 'default' => 0],
			'first_request_date' => ['type' => self::UINT, 'default' => \XF::$time],
			'last_request_date' => ['type' => self::UINT, 'default' => \XF::$time],
			'views' => ['type' => self::UINT, 'default' => 0],
			'pruned' => ['type' => self::BOOL, 'default' => false],
			'is_processing' => ['type' => self::UINT, 'default' => 0],
			'failed_date' => ['type' => self::UINT, 'default' => 0],
			'fail_count' => ['type' => self::UINT, 'default' => 0],
		];
		$structure->getters = ['url' => true];
		$structure->relations = [
			'BbCodeMediaSite' => [
				'entity' => 'XF:BbCodeMediaSite',
				'type' => self::TO_ONE,
				'conditions' => 'media_site_id',
				'primary' => true,
			],
			'Referrers' => [
				'entity' => 'XF:OembedReferrer',
				'type' => self::TO_MANY,
				'conditions' => 'oembed_id',
				'order' => ['last_date', 'DESC'],
			],
		];

		return $structure;
	}
}
