<?php

/********************************************************************
 * Copyright (C) 2025 Hassine RIANI (https://upvimeo.com)
 *
 * This file is part of Upvimeo
 *
 * Upvimeo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * Upvimeo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Upvimeo. If not, see <https://www.gnu.org/licenses/>.
 **********************************************************************/

use Vimeify\Vimeo\Exceptions\VimeoRequestException;

/**
 * Class UpVimeo_Api_Helper
 *
 * Responsible for communicating with the Vimeo API
 *
 * @license GPLv2
 * @copyright Hassine RIANI (https://upvimeo.com)
 * @since 1.0.0
 */
class UpVimeo_Api_Helper extends UpVimeo_API_Base
{

	/**
	 * Upload quota by count
	 */
	const QUOTA_TYPE_VIDEOS_COUNT = 'video_count';

	/**
	 * Upload quota by size
	 */
	const QUOTA_TYPE_VIDEOS_SIZE = 'video_size';

	/**
	 * Cache key name when retrieving the folders
	 * @var string
	 */
	const CACHE_KEY_FOLDERS = 'upvimeo_account_folders';

	/**
	 * Cache key name when retrieving the folders
	 * @var string
	 */
	const CACHE_KEY_EMBED_PRESETS = 'upvimeo_account_embed_presets';

	/**
	 * Returns embed presets
	 *
	 * @return array
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 * @since 1.5.0
	 *
	 */
	private function _get_embed_presets()
	{
		$presets = array();
		if (is_null($this->api)) {
			return $presets;
		}
		$response = $this->api->request('/me/presets');
		$response = $this->prepare_response($response);
		if (isset($response['body']['data']) && is_array($response['body']['data'])) {
			foreach ($response['body']['data'] as $preset) {
				if (!isset($preset['uri'])) {
					continue;
				}
				$preset_uri = $preset['uri'];
				$presets[$preset_uri] = $preset['name'];
			}
		}

		return $presets;
	}

	/**
	 * Returns list of presets (With cache of 5 minutes)
	 *
	 * @param  bool  $cache
	 *
	 * @return array Returns preset_uri=>preset_name structure
	 * @since 1.5.0
	 *
	 */
	public function get_embed_presets($cache = true)
	{
		if ($cache) {
			$presets = get_transient(self::CACHE_KEY_EMBED_PRESETS);
			if (false === $presets) {
				$presets = array();
				try {
					$presets = $this->_get_embed_presets();
				} catch (\Exception $e) {
				}
				if (count($presets) > 0) {
					set_transient(self::CACHE_KEY_EMBED_PRESETS, $presets, 60 * 5);
				}
			}
		} else {
			try {
				$presets = $this->_get_embed_presets();
			} catch (\Exception $e) {
				$presets = array();
			}
		}

		return $presets;
	}

	/**
	 * Set video embed preset
	 *
	 * @param $video_uri
	 * @param $preset_uri
	 *
	 * @return array
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 * @since 1.5.0
	 *
	 */
	public function set_video_embed_preset($video_uri, $preset_uri)
	{
		$url = '/videos/' . upvimeo_uri_to_id($video_uri) . '/presets/' . upvimeo_embed_preset_uri_to_id($preset_uri);

		$response = $this->api->request($url, [], 'PUT');

		return $this->prepare_response($response);
	}

	/**
	 * Remove embed preset from Video
	 *
	 * @param $video_uri
	 * @param $preset_uri
	 *
	 * @return array
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 * @since 1.5.0
	 *
	 */
	public function remove_video_embed_preset($video_uri, $preset_uri)
	{
		$url = '/videos/' . upvimeo_uri_to_id($video_uri) . '/presets/' . upvimeo_embed_preset_uri_to_id($preset_uri);

		$response = $this->api->request($url, [], 'DELETE');

		return $this->prepare_response($response);
	}

	/**
	 * Returns videos
	 *
	 * @param  array  $query
	 *
	 * @return null|array
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 * @since 1.9.1
	 */
	public function get_my_videos($query = array())
	{

		$args = wp_parse_args($query, [
			'direction' => 'desc',
			'fields' => 'uri,name,description,duration,width,height,created_time,modified_time,privacy,pictures,link,stats,upload',
			'page' => 1,
			'per_page' => 20,
			'sort' => 'date',
			'query' => '',
		]);

		if (is_null($this->api)) {
			return null;
		}
		$response = $this->api->request('/me/videos', $args, 'GET');


		return $this->format_collection_response($response);
	}

	/**
	 * Returns folders
	 *
	 * @param  array  $query
	 *
	 * @return null|array
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 * @since 1.9.1
	 */
	public function get_folders_query($query = array())
	{

		$args = wp_parse_args($query, [
			'direction' => 'asc',
			'fields' => 'name,description,uri',
			'page' => 1,
			'per_page' => 100,
			'sort' => 'name',
			'query' => '',
		]);

		if (is_null($this->api)) {
			return null;
		}
		$response = $this->api->request('/me/projects', $args, 'GET');


		return $this->format_collection_response($response);
	}

	/**
	 * Returns list of folders (With cache of 5 minutes)
	 *
	 * @param  bool  $cache
	 *
	 * @return array Returns folder_uri=>folder_name structure
	 * @since 1.5.0
	 */
	public function get_folders($cache = true)
	{

		_deprecated_function(__FUNCTION__, '1.9.1', 'UpVimeo_Api_Helper::get_folders_query');

		$query = ['per_page' => 100];
		$folders = array();
		if ($cache) {
			$_folders = get_transient(self::CACHE_KEY_FOLDERS);
			if (false === $_folders) {
				try {
					$data = $this->get_folders_query($query);
					if (!empty($data['results'])) {
						foreach ($data['results'] as $result) {
							$folders[$result['uri']] = $result['name'];
						}
					}
				} catch (\Exception $e) {
				}
				if (count($folders) > 0) {
					set_transient(self::CACHE_KEY_FOLDERS, $folders, 60 * 5);
				}
			} else {
				$folders = $_folders;
			}
		} else {
			try {
				$data = $this->get_folders_query($query);
				if (!empty($data['results'])) {
					foreach ($data['results'] as $result) {
						$folders[$result['uri']] = $result['name'];
					}
				}
			} catch (\Exception $e) {
			}
		}

		return $folders;
	}

	/**
	 * Return folder form the API
	 *
	 * @param $uri
	 * @param  int  $cache_ttl  - In minutes
	 *
	 * @return array
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 */
	public function get_folder($uri, $cache_ttl = 60)
	{

		$parts = explode('/', $uri);
		$folder_id = end($parts);

		$result = [];
		if ($cache_ttl) {
			$cache_key = 'upvimeo_folder_' . $folder_id;
			$result = get_transient($cache_key);
			if (false === $result) {
				$response = $this->api->request('/me/projects/' . $folder_id, [], 'GET');
				$result = $this->format_object_response($response, false);
				set_transient($cache_key, $result, $cache_ttl * 60);
			}
		} else {
			$response = $this->api->request('/me/projects/' . $folder_id, [], 'GET');
			$result = $this->format_object_response($response, false);
		}


		return $result;
	}


	/**
	 * Returns the folder name from the API
	 *
	 * @param $uri
	 *
	 * @return string
	 * @since 1.5.0
	 */
	public function get_folder_name($uri)
	{
		try {
			$folder = $this->get_folder($uri);
		} catch (\Vimeify\Vimeo\Exceptions\VimeoRequestException $e) {
		}
		if (isset($folder['results']['name'])) {
			return $folder['results']['name'];
		} else {
			return __('Untitled', 'upvimeo-direct-uploads');
		}
	}

	/**
	 * Set video folder
	 *
	 * @param $video_uri
	 * @param $folder_uri
	 *
	 * @return array
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 * @since 1.5.0
	 *
	 */
	public function set_video_folder($video_uri, $folder_uri)
	{
		$url = untrailingslashit($folder_uri) . '/videos/' . upvimeo_uri_to_id($video_uri);

		$response = $this->api->request($url, [], 'PUT');

		return $this->prepare_response($response);
	}

	/**
	 * Remove video from folder
	 *
	 * @param $video_uri
	 * @param $folder_uri
	 *
	 * @return array
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 * @since 1.5.0
	 *
	 */
	public function remove_video_folder($video_uri, $folder_uri)
	{
		$url = untrailingslashit($folder_uri) . '/videos/' . upvimeo_uri_to_id($video_uri);

		$response = $this->api->request($url, [], 'DELETE');

		return $this->prepare_response($response);
	}

	/**
	 * Creates upload attempt
	 *
	 * @param $file_path
	 * @param  array  $params
	 *
	 * @return array
	 * @throws \Exception
	 * @since 1.1.0
	 *
	 */
	public function create_attempt($file_path, $params = array())
	{


		if (!is_file($file_path)) {
			throw new \Exception('Unable to locate file to upload.');
		}
		$file_size = filesize($file_path);
		$params['upload']['approach'] = 'tus';
		$params['upload']['size'] = $file_size;
		$uri = '/me/videos?fields=uri,upload';
		$params = apply_filters('UPVIMEO_before_create_api_video_params', $params, $file_path);
		$attempt = $this->api->request($uri, $params, 'POST');
		$attempt = $this->prepare_response($attempt);
		if ($attempt['status'] !== 200) {
			$attempt_error = !empty($attempt['body']['error']) ? ' [' . $attempt['body']['error'] . ']' : '';
			throw new \Exception('Unable to initiate an upload.' . esc_html($attempt_error));
		}

		return $attempt;
	}

	/**
	 * Creates upload ticket for client-side upload
	 *
	 * @param $size
	 * @param  array  $params
	 *
	 * @return array
	 * @throws \Exception
	 * @since 1.2.9
	 *
	 */
	public function create_upload_ticket($size, $params = array())
	{


		$params['upload']['approach'] = 'tus';
		$params['upload']['size'] = $size;
		$uri = '/me/videos?fields=uri,upload,link';
		$attempt = $this->api->request($uri, $params, 'POST');
		$attempt = $this->prepare_response($attempt);
		if ($attempt['status'] !== 200) {
			$attempt_error = !empty($attempt['body']['error']) ? ' [' . $attempt['body']['error'] . ']' : '';
			throw new \Exception('Unable to initiate an upload.' . esc_html($attempt_error));
		}

		return $attempt;
	}

	/**
	 * Update attempt
	 *
	 * @param $file_path
	 * @param $attempt
	 *
	 * @return mixed
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoUploadException
	 * @since 1.1.0
	 *
	 */
	public function finish_attempt($file_path, $attempt)
	{
		$size = filesize($file_path);

		$uri = $this->api->do_upload_tus($file_path, $size, $attempt);

		// Double check if the upload is complete.
		try {
			$check = $this->get($uri);
			$status = isset($check['body']['upload']['status']) ? $check['body']['upload']['status'] : 'unknown';

			if ($status === 'in_progress') {
				// Force completion
				if (isset($attempt['body']['upload']['upload_link'])) {
					$force_response = $this->api->request($attempt['body']['upload']['upload_link'], [], 'DELETE');
				}
			}
		} catch (\Exception $e) {
		}

		return $uri;
	}

	/**
	 * Edit specific video
	 *
	 * @param $uri
	 * @param $data
	 *
	 * @return array
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 * @since 1.0.0
	 *
	 */
	public function edit($uri, $data)
	{
		$response = $this->api->request($uri, $data, 'PATCH');

		return $this->prepare_response($response);
	}

	/**
	 * Return the current max quota
	 *
	 * @return int
	 * @since 1.2.0
	 *
	 */
	public function get_current_max_quota()
	{
		return isset($this->upload_quota['space']['max']) ? (int) $this->upload_quota['space']['max'] : 0;
	}

	/**
	 * Return the current max quota
	 *
	 * @return int
	 * @since 1.2.0
	 *
	 */
	public function get_current_used_quota()
	{
		return isset($this->upload_quota['space']['used']) ? (int) $this->upload_quota['space']['used'] : 0;
	}

	/**
	 * Return the current max quota
	 *
	 * @return int
	 * @since 1.2.0
	 *
	 */
	public function get_current_remaining_quota()
	{
		return isset($this->upload_quota['space']['free']) ? (int) $this->upload_quota['space']['free'] : 0;
	}

	/**
	 * Returns the current upload quota type
	 * @return mixed|string
	 */
	public function get_current_quota_type()
	{
		return isset($this->upload_quota['space']['unit']) ? $this->upload_quota['space']['unit'] : '';
	}

	/**
	 * Return the quota reset date
	 *
	 * @since 1.2.0
	 */
	public function get_quota_reset_date()
	{
		$date = false;
		if (isset($this->upload_quota['space']['showing']) && $this->upload_quota['space']['showing'] === 'periodic') {
			$date = isset($this->upload_quota['periodic']['reset_date']) ? $this->upload_quota['periodic']['reset_date'] : false;
		}

		return $date;
	}

	/**
	 * Returns the current plan
	 *
	 * @param  false  $formatted
	 *
	 * @return string
	 * @since 1.5.1
	 */
	public function get_plan($formatted = false)
	{
		$plan = $this->user_type;
		if (!$formatted) {
			return $plan;
		}
		if (empty($plan)) {
			return __('Unknown', 'upvimeo-direct-uploads');
		}
		$plan = ucwords(str_replace('_', ' ', $this->user_type));

		return sprintf('Vimeo %s', $plan);
	}

	/**
	 * Return the available preset choices for the settings
	 *
	 * @since 1.5.0
	 */
	public function get_embed_presets_options()
	{
		$presets = $this->get_embed_presets();
		if (!is_array($presets)) {
			$presets = array();
		}

		return array_merge(array('default' => 'Default (no preset)'), $presets);
	}

	/**
	 * Return the available folder choices for the settings
	 *
	 * @since 1.5.0
	 */
	public function get_folder_options()
	{
		$folders = $this->get_folders();
		if (!is_array($folders)) {
			$folders = array();
		}

		return array_merge(array('default' => 'Default (no folder)'), $folders);
	}

	/**
	 * Returns the collection response in the following format.
	 *
	 * @param $response
	 *
	 * @return array
	 * @since 1.7.0
	 */
	private function format_collection_response($response)
	{

		$response = $this->prepare_response($response);
		$total = isset($response['body']['total']) ? $response['body']['total'] : 0;
		$per_page = isset($response['body']['per_page']) ? $response['body']['per_page'] : 0;
		$page = isset($response['body']['page']) ? $response['body']['page'] : 0;
		if ($total > 0 && $per_page > 0) {
			$has_next = $total > $per_page ? $page < ($total / $per_page) : false;
		} else {
			$has_next = false;
		}

		return [
			'status' => isset($response['status']) ? $response['status'] : 500,
			'headers' => isset($response['headers']) ? $response['headers'] : [],
			'results' => isset($response['body']['data']) ? $response['body']['data'] : [],
			'paging' => [
				'total' => isset($response['body']['total']) ? $response['body']['total'] : 0,
				'page' => isset($response['body']['page']) ? $response['body']['page'] : 0,
				'per_page' => isset($response['body']['per_page']) ? $response['body']['per_page'] : 0,
				'has_next' => $has_next,
			],
		];
	}

	/**
	 * Returns the collection response in the following format.
	 *
	 * @param $response
	 * @param  bool  $headers
	 *
	 * @return array
	 * @since 1.7.0
	 */
	private function format_object_response($response, $headers = true)
	{
		$response = $this->prepare_response($response);

		return [
			'status' => isset($response['status']) ? $response['status'] : 500,
			'headers' => isset($response['headers']) && $headers ? $response['headers'] : [],
			'results' => isset($response['body']) ? $response['body'] : [],
		];
	}

	/**
	 * Returns whitelist domains
	 *
	 * @return array
	 * @throws \Vimeify\Vimeo\Exceptions\VimeoRequestException
	 * @since 1.9.1
	 */
	public function get_whitelist_domains()
	{
		if (is_null($this->api)) {
			return [];
		}
		$response = $this->api->request('/me/privacy/domains', [], 'GET');

		// Fallback Strategy: Discovery (HATEOAS)
		// If direct endpoints fail, ask the API where the domains resource is.
		if (isset($response['status']) && $response['status'] === 404) {

			// 1. Get current user with connection metadata
			$me = $this->api->request('/me', ['fields' => 'metadata.connections.privacy,metadata.connections.album_privacy'], 'GET');

			$discovered_uri = '';

			// 2. Look for privacy domains connection
			if (isset($me['body']['metadata']['connections']['privacy']['domains']['uri'])) {
				$discovered_uri = $me['body']['metadata']['connections']['privacy']['domains']['uri'];
			} elseif (isset($me['body']['metadata']['connections']['privacy']['uri'])) {
				// Sometimes it might just be under privacy? (Unlikely but possible)
			}

			if (!empty($discovered_uri)) {
				$response = $this->api->request($discovered_uri, [], 'GET');
			}
		}

		$response = $this->format_collection_response($response);

		$domains = [];
		if (!empty($response['results'])) {
			foreach ($response['results'] as $result) {
				if (isset($result['domain'])) {
					$domains[] = $result['domain'];
				}
			}
		}
		return $domains;
	}
}
