<?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_Internal_Hooks
 *
 * Responsible for all the internal hooks that this plugin defines
 *
 * @license GPLv2
 * @copyright Hassine RIANI (https://upvimeo.com)
 * @since 1.1.0
 */
class UpVimeo_Internal_Hooks
{

	/**
	 * The api helper
	 * @var UpVimeo_Api_Helper
	 */
	protected $api = null;

	/**
	 * The settings helper
	 * @var UpVimeo_Settings_Helper
	 */
	protected $settings = null;

	/**
	 * The logger.
	 * @var UpVimeo_Logger|null
	 */
	protected $logger = null;

	/**
	 * The db.
	 * @var null
	 */
	protected $db = null;

	/**
	 * UpVimeo_Internal_Hooks constructor.
	 */
	public function __construct()
	{
		$this->init_utilities();
	}

	/**
	 * Enable custom extensions support
	 *
	 * @param $mimes
	 *
	 * @return mixed
	 */
	public function add_mimes($mimes)
	{
		foreach (upvimeo_get_allowed_mimes() as $key => $mime) {
			if (!isset($mimes[$key])) {
				$mimes[$key] = $mime;
			}
		}

		return $mimes;
	}

	/**
	 * Manually determine the default time limit of specific process
	 *
	 * @param $default
	 *
	 * @return int
	 * @since 1.4.0
	 *
	 */
	public function upload_process_default_time_limit($default)
	{
		$limit = (int) ini_get('max_execution_time');
		if ($limit === 0) {
			$default = 7200; // 2 hours.
		} elseif (($limit - 10) < 0) {
			$default = 30;
		} else {
			$default = $limit - 10;
		}

		return $default;
	}

	/**
	 * Hook after the attempt was created.
	 *
	 * @param  int  $attempt
	 * @param  string  $uri
	 * @param  array  $args
	 *
	 * @since 1.5.0
	 */

	public function frontend_after_attempt_created($attempt, $uri, $args)
	{
		$context = 'frontend';
		$logtag = $this->get_logtag($context);

		if (isset($args['process_hooks']) && !$args['process_hooks']) {
			$this->logger->log('No hooks processing required. (frontend)', $logtag);

			return;
		}

		/**
		 * Make sure we are on the right track.
		 */
		if (!isset($args['upvimeo_id'])) {
			$this->logger->log('No vimeo id found. Failed to execute post upload hooks. (frontend)', $logtag);

			return;
		}

		/*
		 * Singal start.
		 */
		$this->logger->log(sprintf('Processing hooks for %s', $uri), $logtag);

		/**
		 * Set embed privacy
		 */
		if ($this->api->supports_embed_privacy()) {
			$this->set_embed_privacy($uri, $context);
		}


		/**
		 * Set Folder
		 */
		$folder_uri = apply_filters('upvimeo_frontend_upload_folder', $this->settings->get('upvimeo_upload_folder', 'default'), $this->settings);
		$this->set_folder($uri, $folder_uri, $context);

		/**
		 * Set Presets
		 */
		if ($this->api->supports_embed_presets()) {
			$preset_uri = apply_filters('upvimeo_frontend_embed_preset', $this->settings->get('upvimeo_embed_preset', 'default'), $this->settings);
			$this->set_embed_preset($uri, $preset_uri, $context);
		}

		/**
		 * Set View privacy
		 */
		$view_privacy = isset($args['overrides']['view_privacy']) ? $args['overrides']['view_privacy'] : $this->get_view_privacy($context);
		if ($this->api->supports_view_privacy_option($view_privacy)) {
			$this->set_view_privacy($uri, $view_privacy, $context);
		}

		/**
		 * Create local video (Save the video in the database if enabled.)
		 */
		do_action('upvimeo_pro_save_frontend_uploads', $this, $args, $context);
	}


	/**
	 * Handle after upload hook in Admin area
	 *
	 * @param $args
	 *
	 * @since 1.7.0
	 */
	public function backend_after_upload($args)
	{

		$context = 'backend';
		$logtag = $this->get_logtag($context);

		/**
		 * Make sure we are on the right track.
		 */
		if (!isset($args['upvimeo_id'])) {
			$this->logger->log('No vimeo id found. Failed to execute post upload hooks. (backend)', $logtag);

			return;
		}

		/**
		 * Obtain some important data.
		 */
		$response = $args['upvimeo_id'];
		$uri = upvimeo_response_to_uri($response);

		/**
		 * Signal start
		 */
		$this->logger->log(sprintf('Processing hooks for %s', $uri), $logtag);

		/**
		 * Set embed privacy
		 */
		if ($this->api->supports_embed_privacy()) {
			$this->set_embed_privacy($uri, $context);
		}

		/**
		 * Set Folder
		 */
		$folder_uri = $this->settings->get('upvimeo_upload_folder', 'default');
		$this->set_folder($uri, $folder_uri, $context);

		/**
		 * Set Presets
		 */
		if ($this->api->supports_embed_presets()) {
			$preset_uri = $this->settings->get('upvimeo_embed_preset', 'default');
			$this->set_embed_preset($uri, $preset_uri, $context);
		}

		/**
		 * Set View privacy
		 */
		$view_privacy = isset($args['overrides']['view_privacy']) ? $args['overrides']['view_privacy'] : $this->get_view_privacy($context);
		if ($this->api->supports_view_privacy_option($view_privacy)) {
			$this->set_view_privacy($uri, $view_privacy, $context);
		}

		/**
		 * Create local video (Save the video in the database)
		 */
		$this->create_local_video($args, $context);

		/**
		 * Old deprecated hook
		 */
		do_action('UPVIMEO_after_upload', $uri, $this->api);
	}


	/**
	 * Init API Helper.
	 * @since 1.7.0
	 */
	protected function init_utilities()
	{
		if (is_null($this->api)) {
			$this->api = new UpVimeo_Api_Helper();
		}
		if (is_null($this->settings)) {
			$this->settings = new UpVimeo_Settings_Helper();
		}
		if (is_null($this->logger)) {
			$this->logger = new UpVimeo_Logger();
		}
		if (is_null($this->db)) {
			$this->db = new UpVimeo_Db_Helper();
		}
	}

	/**
	 * Returns the log tag.
	 *
	 * @param $context
	 *
	 * @return string
	 * @since 1.7.0
	 */
	protected function get_logtag($context)
	{
		if ($context === 'backend') {
			$tag = 'upvimeo-ADMIN-HOOKS';
		} elseif ($context === 'frontend') {
			$tag = 'upvimeo-FRONTEND-HOOKS';
		} else {
			$tag = 'upvimeo-INTERNAL-HOOKS';
		}

		return $tag;
	}

	/**
	 * Create local video
	 *
	 * @param $data
	 * @param $context
	 *
	 * @since 1.7.0
	 */
	protected function create_local_video($data, $context)
	{
		$logtag = $this->get_logtag($context);
		$id = isset($data['upvimeo_id']) ? $data['upvimeo_id'] : 0;
		$title = isset($data['upvimeo_title']) ? $data['upvimeo_title'] : '';
		$description = isset($data['upvimeo_description']) ? $data['upvimeo_description'] : '';
		$post_id = $this->db->create_local_video($title, $description, $id, 'frontend');
		$source = isset($data['source']) ? $data['source'] : array();

		if (!is_wp_error($post_id)) {
			/**
			 * Update meta
			 */
			update_post_meta($post_id, 'UPVIMEO_source', $source);
			if (isset($data['upvimeo_size']) && $data['upvimeo_size']) {
				update_post_meta($post_id, 'UPVIMEO_size', (int) $data['upvimeo_size']);
			}

			/**
			 * Set link to the Video. Note: For some videos Vimeo creates non-standard links.
			 * e.g View privacy: Those with link only.
			 */
			/**
			 * Set link to the Video. Note: For some videos Vimeo creates non-standard links.
			 * e.g View privacy: Those with link only.
			 */
			if (!empty($id)) {
				try {
					$fields = array('link');
					$fetch_thumbnail = (bool) $this->settings->get('upvimeo_exp_thumbs_support');
					if ($fetch_thumbnail) {
						$fields[] = 'pictures';
					}

					$response = $this->api->get_video_by_id($id, $fields);

					if (!empty($response['body']['link'])) {
						update_post_meta($post_id, 'UPVIMEO_link', $response['body']['link']);
					}

					if ($fetch_thumbnail && !empty($response['body']['pictures'])) {
						$this->generate_thumbnail($post_id, $response['body']['pictures']);
					}
				} catch (\Exception $e) {
				}
			}

			/**
			 * Set media library attachment source
			 */
			if (isset($data['source']['media_id'])) {
				update_post_meta($data['source']['media_id'], 'upvimeo', array(
					'upvimeo_id' => $id,
					'local_id' => $post_id,
				));
			}

			if (!empty($data['upvimeo_meta']) && is_array($data['upvimeo_meta'])) {
				foreach ($data['upvimeo_meta'] as $k => $v) {
					update_post_meta($post_id, $k, $v);
				}
			}

			$this->logger->log(sprintf('Local video #%s created', $post_id), $logtag);
		} else {
			$this->logger->log(sprintf('Failed to create local video (%s)', $post_id->get_error_message()), $logtag);
		}
	}

	/**
	 * Generate post thumbnail from Vimeo pictures
	 *
	 * @param int $post_id
	 * @param array $pictures
	 */
	protected function generate_thumbnail($post_id, $pictures)
	{
		if (empty($pictures['sizes'])) {
			return;
		}

		// Get largest size
		$sizes = $pictures['sizes'];
		// Vimeo returns sizes sorted by width, last one is usually the largest
		$largest = end($sizes);
		$url = isset($largest['link']) ? $largest['link'] : '';

		if (empty($url)) {
			return;
		}

		// Load required files for media_sideload_image if strictly needed,
		// though typically available in admin context.
		// But this might run in frontend context too.
		if (!function_exists('media_sideload_image')) {
			require_once(ABSPATH . 'wp-admin/includes/media.php');
			require_once(ABSPATH . 'wp-admin/includes/file.php');
			require_once(ABSPATH . 'wp-admin/includes/image.php');
		}

		$desc = "Vimeo Thumbnail for Video #$post_id";

		// Sideload the image
		$attachment_id = media_sideload_image($url, $post_id, $desc, 'id');

		if (!is_wp_error($attachment_id)) {
			set_post_thumbnail($post_id, $attachment_id);
			$this->logger->log(sprintf('Thumbnail generated for local video #%s', $post_id), 'upvimeo-INTERNAL');
		} else {
			$this->logger->log(sprintf('Failed to generate thumbnail for local video #%s. Error: %s', $post_id, $attachment_id->get_error_message()), 'upvimeo-INTERNAL');
		}
	}

	/**
	 * Set embed preset.
	 *
	 * @param $uri
	 * @param $preset_uri
	 * @param $context
	 *
	 * @since 1.7.0
	 */
	protected function set_embed_preset($uri, $preset_uri, $context)
	{
		if (empty($preset_uri) || 'default' === $preset_uri) {
			return;
		}
		$logtag = $this->get_logtag($context);
		try {
			$this->api->set_video_embed_preset($uri, $preset_uri);
			$this->logger->log('Embed preset set', $logtag);
		} catch (VimeoRequestException $e) {
			$this->logger->log(sprintf('Failed to set embed preset (%s)', $e->getMessage()), $logtag);
		}
	}

	/**
	 * Set folder.
	 *
	 * @param $uri
	 * @param $folder_uri
	 * @param $context
	 *
	 * @since 1.7.0
	 */
	protected function set_folder($uri, $folder_uri, $context)
	{

		if (empty($folder_uri) || 'default' === $folder_uri) {
			return;
		}

		$logtag = $this->get_logtag($context);

		try {
			$this->api->set_video_folder($uri, $folder_uri);
			$this->logger->log('Folder set', $logtag);
		} catch (VimeoRequestException $e) {
			$this->logger->log(sprintf('Failed to set folder (%s)', $e->getMessage()), $logtag);
		}
	}

	/**
	 * Set embed privacy.
	 *
	 * @param $uri
	 * @param $context
	 *
	 * @since 1.7.0
	 */
	protected function set_embed_privacy($uri, $context)
	{
		$logtag = $this->get_logtag($context);
		try {
			$whitelisted_domains = $this->settings->get_whitelisted_domains();

			// Fallback: If local settings are empty, pull from Vimeo account defaults directly to ensure Pro integration reflects Vimeo's global settings.
			if (empty($whitelisted_domains) && is_callable(array($this->api, 'get_whitelist_domains'))) {
				$whitelisted_domains = $this->api->get_whitelist_domains();
			}

			if (is_array($whitelisted_domains) && count($whitelisted_domains) > 0) {
				$this->api->set_embed_privacy($uri, 'whitelist');

				// PCP Compliant loop using wp_remote_request
				$access_token = $this->settings->get('upvimeo_access_token', '');
				if (!empty($access_token)) {
					$video_id = str_replace('/videos/', '', $uri);

					foreach ($whitelisted_domains as $domain) {
						if (empty($domain)) {
							continue;
						}

						$sanitized_domain = esc_url_raw(wp_strip_all_tags($domain));
						if (empty($sanitized_domain)) {
							$sanitized_domain = sanitize_text_field(wp_strip_all_tags($domain));
						} else {
							$parsed_host = wp_parse_url($sanitized_domain, PHP_URL_HOST);
							$sanitized_domain = !empty($parsed_host) ? $parsed_host : $sanitized_domain;
						}

						$sanitized_domain = sanitize_text_field(str_replace(array('http://', 'https://'), '', $sanitized_domain));

						if (empty($sanitized_domain)) {
							continue;
						}

						$request_url = 'https://api.vimeo.com/videos/' . urlencode($video_id) . '/privacy/domains/' . rawurlencode($sanitized_domain);

						$response = wp_safe_remote_request(
							$request_url,
							array(
								'method'  => 'PUT',
								'headers' => array(
									'Authorization' => 'Bearer ' . sanitize_text_field(wp_unslash($access_token)),
									'Accept'        => 'application/vnd.vimeo.*+json;version=3.4',
									'Content-Type'  => 'application/json',
								),
							)
						);

						if (is_wp_error($response)) {
							$domain_filtered = esc_html($sanitized_domain);
							$err_msg         = esc_html($response->get_error_message());
							$uri_filtered    = esc_html($uri);
							
							/* translators: 1: domain name, 2: video uri, 3: error message */
							$message_str = sprintf(esc_html__('Embed domain %1$s whitelist error for %2$s: %3$s', 'upvimeo-direct-uploads'), $domain_filtered, $uri_filtered, $err_msg);
							$this->logger->log($message_str, $logtag);
						} else {
							$response_code = wp_remote_retrieve_response_code($response);
							$domain_filtered = esc_html($sanitized_domain);
							$uri_filtered    = esc_html($uri);
							
							if ($response_code === 200 || $response_code === 204) {
								/* translators: 1: domain name, 2: video uri */
								$message_str = sprintf(esc_html__('Embed domain %1$s whitelisted for %2$s', 'upvimeo-direct-uploads'), $domain_filtered, $uri_filtered);
								$this->logger->log($message_str, $logtag);
							} else {
								// Ensure string cast or robust formatting for response code
								$status_str = esc_html((string)$response_code);
								
								/* translators: 1: domain name, 2: video uri, 3: status code */
								$message_str = sprintf(esc_html__('Failed to add whitelist domain %1$s for %2$s. Status: %3$s', 'upvimeo-direct-uploads'), $domain_filtered, $uri_filtered, $status_str);
								$this->logger->log($message_str, $logtag);
							}
						}
					}
				}
			}
		} catch (\Exception $e) {
			/* translators: 1: video uri, 2: error message */
			$this->logger->log(sprintf(esc_html__('Failed to set embed privacy for %1$s. Error: (%2$s)', 'upvimeo-direct-uploads'), $uri, $e->getMessage()), $logtag);
		}
	}

	/**
	 * Set view privacy
	 *
	 * @param $uri
	 * @param $privacy
	 * @param $context
	 */
	protected function set_view_privacy($uri, $privacy, $context)
	{

		$logtag = $this->get_logtag($context);

		if (!in_array($privacy, array('default', 'anybody'))) {
			$params['privacy'] = array('view' => $privacy);
			if ($this->api->can_edit()) {
				try {
					$this->api->edit($uri, $params);
					$this->logger->log(sprintf('View privacy set to %s for %s', $privacy, $uri), $logtag);
				} catch (VimeoRequestException $e) {
					$this->logger->log(sprintf('Failed to set view privacy %s for %s. Error: (%s)', $privacy, $uri, $e->getMessage()), $logtag);
				}
			} else {
				$this->logger->log(sprintf('Failed to set view privacy %s for %s. Unsupported on %s plan', $privacy, $uri, $this->api->get_plan(true)), $logtag);
			}
		}
	}

	/**
	 * Return view privacy.
	 *
	 * @param $context
	 *
	 * @return string|null
	 */
	protected function get_view_privacy($context)
	{
		if ($context === 'frontend') {
			$privacy = $this->settings->get_default_frontend_view_privacy();
			if (empty($privacy) || 'default' === $privacy) {
				$privacy = $this->settings->get_default_admin_view_privacy();
			}
		} elseif ($context === 'backend') {
			$privacy = $this->settings->get_default_admin_view_privacy();
		} else {
			$privacy = null;
		}

		return $privacy;
	}
	/**
	 * Whitelist UpVimeo fields in Elementor ACF URL Dynamic Tag.
	 *
	 * @param $options
	 * @param $control
	 * @param $tag
	 *
	 * @return array
	 */
	public function add_upvimeo_to_elementor_acf_url_tag($options, $control, $tag)
	{
		if (empty($tag['name']) || 'acf-url' !== $tag['name']) {
			return $options;
		}

		if (empty($control['name']) || 'key' !== $control['name']) {
			return $options;
		}

		if (!function_exists('acf_get_field_groups')) {
			return $options;
		}

		$acf_groups = acf_get_field_groups();

		foreach ($acf_groups as $acf_group) {
			$fields = acf_get_fields($acf_group['ID']);
			if ($fields) {
				foreach ($fields as $field) {
					if ($field['type'] === 'upvimeo') {
						$key = $field['key'] . ':' . $field['name'];
						$options[$key] = $field['label'];
					}
				}
			}
		}

		return $options;
	}
	/**
	 * Register Elementor Dynamic Tags.
	 *
	 * @param $dynamic_tags
	 */
	public function register_elementor_dynamic_tag($dynamic_tags)
	{
		$tag_class_path = UPVIMEO_PATH . 'pro/includes/integrations/elementor/class-upvimeo-elementor-tag.php';
		if (file_exists($tag_class_path)) {
			require_once $tag_class_path;
			if (class_exists('UpVimeo_Elementor_Tag')) {
				$dynamic_tags->register_tag('UpVimeo_Elementor_Tag');
			}
		}
	}
}
