<?php

namespace XF\Repository;

use XF\Entity\BbCodeMediaSite;
use XF\Entity\Oembed;
use XF\Finder\BbCodeMediaSiteFinder;
use XF\Finder\OembedFinder;
use XF\Mvc\Entity\Repository;

class OembedRepository extends Repository
{
	/**
	 * @return OembedFinder
	 */
	public function findOembedLogsForList()
	{
		return $this->finder(OembedFinder::class)
			->with('BbCodeMediaSite')
			->setDefaultOrder('last_request_date', 'DESC');
	}

	/**
	 * @param $mediaSiteId
	 * @param $mediaId
	 *
	 * @return null|Oembed
	 */
	public function getOembed($mediaSiteId, $mediaId)
	{
		return $this->finder(OembedFinder::class)
			->with('BbCodeMediaSite')
			->where('media_site_id', $mediaSiteId)
			->where('media_id', $mediaId)
			->fetchOne();
	}

	/**
	 * @param BbCodeMediaSite|string $mediaSiteId Use the BbCodeMediaSite entity to avoid a query,
	 *                                                       otherwise use media_site_id
	 * @param string $mediaId
	 *
	 * @return string
	 */
	public function getOembedUrl($mediaSiteId, $mediaId)
	{
		if ($this->isSiteEntity($mediaSiteId))
		{
			$site = $mediaSiteId;
		}
		else
		{
			$site = $this->getMediaSite($mediaSiteId);
		}

		$url = $site->oembed_api_endpoint
		. (strpos($site->oembed_api_endpoint, '?') === false ? '?' : '&')
		. 'format=json&'
		. 'url=' . urlencode(str_replace('{$id}', $mediaId, $site->oembed_url_scheme));

		return $url;
	}

	public function getTotalActiveFetches($activeLength = 60)
	{
		return $this->db()->fetchOne("
			SELECT COUNT(*)
			FROM xf_oembed
			WHERE is_processing >= ?
		", time() - $activeLength);
	}

	public function getOembedFailure()
	{
		/** @var Oembed $oEmbed */
		$oEmbed = $this->em->create(Oembed::class);
		$oEmbed->setAsFailure();

		return $oEmbed;
	}

	public function getOembedFailureResponse($mediaSiteId, $mediaId, $error)
	{
		return json_encode([
			'provider' => $mediaSiteId,
			'id' => $mediaId,
			'xf-oembed-error' => $error,
		]);
	}

	public function logOembedRequest(Oembed $oEmbed)
	{
		$this->db()->query("
			UPDATE xf_oembed SET
				views = views + 1,
				last_request_date = ?
			WHERE oembed_id = ?
		", [\XF::$time, $oEmbed->oembed_id]);
	}

	public function logOembedReferrer(Oembed $oEmbed, $referrer)
	{
		if (!preg_match('#^https?://#i', $referrer))
		{
			return false;
		}

		$this->db()->insert('xf_oembed_referrer', [
			'oembed_id' => $oEmbed->oembed_id,
			'referrer_hash' => md5($referrer),
			'referrer_url' => $referrer,
			'hits' => 1,
			'first_date' => \XF::$time,
			'last_date' => \XF::$time,
		], false, 'hits = hits + 1, last_date = VALUES(last_date)');

		return true;
	}

	protected function getMediaSite($mediaSiteId)
	{
		return $this->finder(BbCodeMediaSiteFinder::class)
			->where('media_site_id', $mediaSiteId)
			->where('oembed_api_endpoint', '<>', '')
			->fetchOne();
	}

	public function findOembedMediaSitesForList()
	{
		return $this->finder(BbCodeMediaSiteFinder::class)
			->where('oembed_enabled', 1)
			->order('site_title');
	}

	/**
	 * Prunes expired oembed data from the file system
	 *
	 * @param integer|null $pruneDate
	 */
	public function pruneOembedCache($pruneDate = null)
	{
		if ($pruneDate === null)
		{
			if (!$this->options()->oEmbedCacheTTL)
			{
				return;
			}

			$pruneDate = \XF::$time - (86400 * $this->options()->oEmbedCacheTTL);
		}

		/** @var Oembed[] $images */
		$oEmbeds = $this->finder(OembedFinder::class)
			->where('fetch_date', '<', $pruneDate)
			->where('pruned', 0)
			->where('is_processing', 0)
			->fetch(2000);
		foreach ($oEmbeds AS $oEmbed)
		{
			$oEmbed->prune();
		}
	}

	/**
	 * Prunes unused oEmbed log entries.
	 *
	 * @param null|int $pruneDate
	 *
	 * @return int
	 */
	public function pruneOembedLogs($pruneDate = null)
	{
		if ($pruneDate === null)
		{
			$options = $this->options();

			if (!$options->oEmbedLogLength)
			{
				return 0;
			}
			if (!$options->oEmbedCacheTTL)
			{
				// don't prune if we are not expiring oEmbed data
				return 0;
			}

			$maxTtl = max($options->oEmbedLogLength, $options->oEmbedCacheTTL);
			$pruneDate = \XF::$time - (86400 * $maxTtl);
		}

		// we can only remove logs where we've pruned the data
		return $this->db()->delete(
			'xf_oembed',
			'pruned = 1 AND last_request_date < ?',
			$pruneDate
		);
	}

	public function pruneOembedReferrerLogs($pruneDate = null)
	{
		if ($pruneDate === null)
		{
			$options = $this->options();

			if (empty($options->oEmbedRequestReferrer['length']))
			{
				// we're keeping referrer data forever
				return 0;
			}

			$pruneDate = \XF::$time - (86400 * $options->oEmbedRequestReferrer['length']);
		}

		return $this->db()->delete(
			'xf_oembed_referrer',
			'last_date < ?',
			$pruneDate
		);
	}

	protected function isSiteEntity($thing)
	{
		return $thing instanceof BbCodeMediaSite;
	}
}
