<?php

namespace BricksExtras;

// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class BricksExtrasMedia {
    
    /**
     * Initialize the waveform handler
     */
    public function init_waveform_handler() {
        add_action('wp_ajax_save_waveform_data', array($this, 'ajax_save_waveform_data'));
        add_action('wp_ajax_get_waveform_data', array($this, 'ajax_get_waveform_data'));
        add_action('wp_ajax_nopriv_get_waveform_data', array($this, 'ajax_get_waveform_data'));
    }
    
    /**
	 * Save waveform data to a JSON file
	 * 
	 * @param string $audio_url URL of the audio file
	 * @param array $waveform_data Array of waveform peaks
	 * @param string $player_id Optional player ID
	 * @return array Response with success/error status and message
	 */
	private static function save_waveform_data($audio_url, $waveform_data, $player_id = '') {
		
		// Validate inputs
		if (empty($audio_url) || empty($waveform_data)) {
			return array(
				'success' => false,
				'message' => 'Missing required data'
			);
		}
		
		// Validate waveform data structure
		if (!self::is_valid_waveform_data($waveform_data)) {
			return array(
				'success' => false,
				'message' => 'Invalid waveform data structure'
			);
		}

		// Strip URL fragments (anything after #) to ensure same audio files share waveform data
		$clean_audio_url = strtok($audio_url, '#');
		
		// Create a unique filename based on the URL only (not player ID)
		// This allows sharing waveform data between players with the same audio
		$url_hash = md5($clean_audio_url);
		$filename = sanitize_file_name($url_hash . '.json');
		
		// Get the upload directory
		$upload_dir = wp_upload_dir();
		$waveform_dir = $upload_dir['basedir'] . '/bricksextras/waveforms';
		
		// Create directory if it doesn't exist
		if (!file_exists($waveform_dir)) {
			wp_mkdir_p($waveform_dir);
		}
		
		if (!file_exists($waveform_dir . '/index.php')) {
			file_put_contents($waveform_dir . '/index.php', '<?php // Silence is golden');
		}
		
		// Get additional data from POST if available
		$full_duration = isset($_POST['full_duration']) ? floatval($_POST['full_duration']) : null;
		$data_resolution = isset($_POST['data_resolution']) ? intval($_POST['data_resolution']) : 2;
		
		// Create the data to save
		$data_to_save = array(
			'waveform_data' => $waveform_data, // Full audio waveform data
			'full_duration' => $full_duration,
			'data_resolution' => $data_resolution,
			'timestamp' => time() 
		); 
		
		// Save the file
		$file_path = $waveform_dir . '/' . $filename;
		
		$json_data = json_encode($data_to_save);
		
		// Check file size limit (50KB)
		$max_size = 51200; // 50KB in bytes
		if (strlen($json_data) > $max_size) {
			return array(
				'success' => false,
				'message' => 'Waveform data exceeds maximum allowed size (50KB)'
			);
		} 
		
		$saved = file_put_contents($file_path, $json_data);
		
		if ($saved) {
			return array(
				'success' => true,
				'message' => 'Waveform data saved successfully',
				'file' => $filename,
				'url' => $upload_dir['baseurl'] . '/bricksextras/waveforms/' . $filename,
				'file_size' => strlen($json_data)
			);
		} else {
			return array(
				'success' => false,
				'message' => 'Failed to save waveform data'
			);
		}
	}

	/**
	 * Validate waveform data structure
	 *
	 * @param array $data The waveform data to validate
	 * @return bool True if data is valid, false otherwise
	 */
	public static function is_valid_waveform_data($data) {
		// Check if data is an array
		if (!is_array($data)) {
			return false;
		}

		$allowed_keys = array('scale', 'min', 'max');
		if (count(array_diff(array_keys($data), $allowed_keys)) > 0) {
			return false;
		}
		
		// Check required properties
		if (!isset($data['scale']) || !is_numeric($data['scale'])) {
			return false;
		}
		
		if (!is_int($data['scale']) || $data['scale'] < 1 || $data['scale'] > 10000) {
			return false;
		}
		
		// Check min/max arrays
		if (!isset($data['min']) || !isset($data['max']) || !is_array($data['min']) || !is_array($data['max'])) {
			return false;
		}
		
		// Check that arrays have same length and are not empty
		if (empty($data['min']) || count($data['min']) !== count($data['max'])) {
			return false;
		}
		
		if (count($data['min']) > 3000 || count($data['max']) > 3000) {
			return false;
		}
		
		foreach ($data['min'] as $val) {
			if (!is_int($val) || $val < -1000 || $val > 1000) {
				return false;
			}
		}
		
		foreach ($data['max'] as $val) {
			if (!is_int($val) || $val < -1000 || $val > 1000) {
				return false;
			}
		}

		
		
		return true;
	}

	/**
	 * Get saved audio waveform data
	 *
	 * @param string $audio_url URL of the audio file
	 * @param string $player_id Optional player identifier to make the cache file unique
	 * @return array Waveform data or error message
	 */
	private static function get_waveform_data($audio_url, $player_id = '') {
		// Validate input
		if (empty($audio_url)) {
			return array(
				'success' => false,
				'message' => 'Missing audio URL'
			);
		}
		
		// Strip URL fragments (anything after #) to ensure same audio files share waveform data
		$clean_audio_url = strtok($audio_url, '#');
		
		// Create the filename based on the URL only (not player ID)
		// This allows sharing waveform data between players with the same audio
		$url_hash = md5($clean_audio_url);
		$filename = $url_hash . '.json';
		
		// Get the upload directory
		$upload_dir = wp_upload_dir();
		$file_path = $upload_dir['basedir'] . '/bricksextras/waveforms/' . $filename;
		
		// Check if file exists
		if (file_exists($file_path)) {
			$file_size = filesize($file_path);
			if ($file_size === false || $file_size > 51200) {
				return array(
					'success' => false,
					'message' => 'Waveform data not found'
				);
			} 
			
			$file_content = file_get_contents($file_path);
			$waveform_data = json_decode($file_content, true);
			
			// Validate the waveform data structure
			if ($waveform_data === null) {
				return array(
					'success' => false,
					'message' => 'Invalid JSON format in waveform data file'
				);
			}
			
			// Check if we have the waveform_data property and validate it
			if (!isset($waveform_data['waveform_data']) || !self::is_valid_waveform_data($waveform_data['waveform_data'])) {
				return array(
					'success' => false,
					'message' => 'Invalid waveform data structure'
				);
			}
			
			return array(
				'success' => true,
				'data' => $waveform_data
			);
		} else {
			return array(
				'success' => false,
				'message' => 'Waveform data not found'
			);
		}
	}

	

	/**
	 * AJAX handler for saving waveform data
	 */
	public static function ajax_save_waveform_data() {
		// Verify nonce for security
		check_ajax_referer('media-nonce', 'security');
		
		if (!current_user_can('edit_posts')) {
			wp_send_json_error('Authentication required');
		}
		
		$user_id = get_current_user_id();
		$rate_limit_key = 'bricksextras_waveform_rate_limit_' . $user_id;
		$current_count = get_transient($rate_limit_key);
		
		if ($current_count === false) {
			set_transient($rate_limit_key, 1, 60);
		} elseif ($current_count >= 20) {
			wp_die();
		} else {
			set_transient($rate_limit_key, $current_count + 1, 60);
		} 
		
		$audio_url = isset($_POST['audio_url']) ? esc_url_raw(wp_unslash($_POST['audio_url'])) : '';
		if (empty($audio_url)) {
			wp_send_json_error('Missing audio URL');
		}

		$player_id = Helpers::get_post_value('player_id');
		
		// Get waveform data from POST
		$waveform_data = isset($_POST['waveform_data']) ? wp_unslash($_POST['waveform_data']) : '';
		
		if (empty($waveform_data) || substr($waveform_data, 0, 1) !== '{' || substr($waveform_data, -1) !== '}') {
			wp_send_json_error('Invalid waveform data format');
			return;
		}
		
		// Decode the JSON string to get the waveform peaks array
		$waveform_peaks = null;
		if (!empty($waveform_data)) {
			$waveform_peaks = json_decode($waveform_data, true);
			
			// Check if JSON decode was successful
			if ($waveform_peaks === null) {
				wp_send_json_error('Invalid JSON format in waveform data');
				return;
			}
			
			// Validate the waveform data structure
			if (!self::is_valid_waveform_data($waveform_peaks)) {
				wp_send_json_error('Invalid waveform data structure');
				return;
			}

		} else {
			wp_send_json_error('Empty waveform data');
			return;
		}
		
		// Save the file
		$result = self::save_waveform_data($audio_url, $waveform_peaks, $player_id);
		
		if ($result['success']) {
			wp_send_json_success($result);
		} else {
			wp_send_json_error($result['message']);
		}
	}

	/**
	 * AJAX handler for retrieving waveform data
	 */
	public static function ajax_get_waveform_data() {
		// Verify nonce for security
		check_ajax_referer('media-nonce', 'security');
		
		$audio_url = isset($_GET['audio_url']) ? esc_url_raw(wp_unslash($_GET['audio_url'])) : '';
		$player_id = Helpers::get_get_value('player_id');
		
		$result = self::get_waveform_data($audio_url, $player_id);
		
		if ($result['success']) {
			wp_send_json_success($result['data']);
		} else {
			wp_send_json_error($result['message']);
		}
	}

	/**
	 * Get or create cached poster for YouTube/Vimeo videos
	 * checks cache first, fetches if needed
	 * 
	 * @param string $video_url The video URL (YouTube or Vimeo)
	 * @return array|false Array with 'url', 'width', 'height', 'provider', 'video_id' or false on failure
	 */
	public static function get_video_poster($video_url) {
		if (empty($video_url)) {
			return false;
		}

		// Parse the video URL to get provider and ID
		$video_info = self::parse_video_url($video_url);
		if (!$video_info) {
			return false;
		}
		
		$video_id = $video_info['video_id'];
		$hash = $video_info['hash'] ?? null; // For unlisted Vimeo videos 

		// Validate video_id
		if (!preg_match('/^[a-zA-Z0-9_-]{6,20}$/', $video_id)) {
			return false;
		}
		
		if ($hash !== null && !preg_match('/^[a-zA-Z0-9]{6,20}$/', $hash)) {
			return false;
		}

		$provider = ($video_info['provider'] === 'youtube') ? 'youtube' : 'vimeo';

		// Get upload directory
		$upload_dir = wp_upload_dir();
		if (!empty($upload_dir['error'])) {
			return false;
		}
		
		$cache_base_dir = $upload_dir['basedir'] . '/bricksextras/posters';
		$cache_base_url = $upload_dir['baseurl'] . '/bricksextras/posters';
		
		// Video-specific directory
		$video_dir = $cache_base_dir . '/' . $video_id;
		$cache_file = $video_dir . '/poster-cache.json';

		// Check if we have cached data
		if (file_exists($cache_file)) {
			$cache_filesize = filesize($cache_file);
			if ($cache_filesize === false || $cache_filesize > 10240) { 
				wp_delete_file($cache_file);
			} else { 
				$cached_data = json_decode(file_get_contents($cache_file), true, 3);
				
				// Check for JSON errors
				if (json_last_error() !== JSON_ERROR_NONE) {
					wp_delete_file($cache_file);
				} else if (is_array($cached_data) && isset($cached_data['poster_file'])) {
					$poster_file = $cached_data['poster_file'];
					
					if (!preg_match('/^poster\.(jpg|jpeg|png|webp)$/', $poster_file)) {
						return false;
					}
					
					$poster_path = $video_dir . '/' . $poster_file;
					if (file_exists($poster_path)) {

						// Return cached poster URL
						return array(
							'url' => esc_url($cache_base_url . '/' . $video_id . '/' . $poster_file),
							'provider' => $provider,
						);
					}
				} 
			}
		}

		// Not cached or invalid cache - fetch from provider
		$result = self::fetch_and_cache_poster($provider, $video_id, $hash);
		
		return $result;
	} 

	/**
	 * Fetch poster from provider and cache it locally
	 * 
	 * @param string $provider 'youtube' or 'vimeo'
	 * @param string $video_id The video ID
	 * @param string|null $hash Optional hash for unlisted Vimeo videos
	 * @return array|false Array with poster info or false on failure
	 */
	private static function fetch_and_cache_poster($provider, $video_id, $hash = null) {

		$video_id = sanitize_file_name($video_id);
		if (!preg_match('/^[a-zA-Z0-9_-]{6,20}$/', $video_id)) {
			return false;
		}

		$rate_limit_key = 'bricksextras_poster_fetch_' . md5($video_id);
		$fetch_count = get_transient($rate_limit_key);
		
		if ($fetch_count !== false && $fetch_count >= 10) {
			return false;
		}
		
		set_transient($rate_limit_key, ($fetch_count === false ? 1 : $fetch_count + 1), 60);
		
		// Get upload directory
		$upload_dir = wp_upload_dir();
		if (!empty($upload_dir['error'])) {
			return false;
		}
		
		$cache_base_dir = $upload_dir['basedir'] . '/bricksextras/posters';
		$cache_base_url = $upload_dir['baseurl'] . '/bricksextras/posters';
		
		// Create base directory if needed
		if (!file_exists($cache_base_dir)) { 
			if (!wp_mkdir_p($cache_base_dir)) {
				return false;
			}
			file_put_contents($cache_base_dir . '/index.php', '<?php // Silence is golden');
		}

		// Create video-specific directory
		$video_dir = $cache_base_dir . '/' . $video_id;
		if (!file_exists($video_dir)) {
			if (!wp_mkdir_p($video_dir)) {
				return false;
			}
			file_put_contents($video_dir . '/index.php', '<?php // Silence is golden');
		}

		$poster_url = null;
		$extension = 'jpg';

		// Fetch poster URL based on provider
		if ($provider === 'youtube') {
			
			$sizes = [
				'maxresdefault' => ['width' => 1280, 'height' => 720],
				'sddefault' => ['width' => 640, 'height' => 480],
				'hqdefault' => ['width' => 480, 'height' => 360]
			];
			
			foreach ($sizes as $size => $dimensions) {
				foreach (['webp', 'jpg'] as $format) {
					$path_prefix = ($format === 'webp') ? 'vi_webp' : 'vi';
					$test_url = "https://i.ytimg.com/{$path_prefix}/{$video_id}/{$size}.{$format}";
					
					// Test if this URL is available
					$test_response = wp_safe_remote_head($test_url, array('timeout' => 10));
					
					if (!is_wp_error($test_response) && wp_remote_retrieve_response_code($test_response) === 200) {
						$poster_url = $test_url;
						$extension = $format;
						break 2; // Break out of both loops
					}
				}
			}
			
			// If no poster found, return false
			if (!$poster_url) {
				return false;
			}
			
		} elseif ($provider === 'vimeo') {
			// Use oEmbed API to get thumbnail
			$oembed_url = "https://vimeo.com/api/oembed.json?url=https://vimeo.com/{$video_id}";
			if ($hash) {
				$oembed_url .= "/{$hash}";
			}
			
			$response = wp_safe_remote_get($oembed_url, array('timeout' => 10));
			
			if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
				return false;
			}
			
			$data = json_decode(wp_remote_retrieve_body($response), true);
			
			if (empty($data['thumbnail_url'])) {
				return false;
			} 
			
			// Parse thumbnail ID from Vimeo CDN
			if (preg_match('/^https:\/\/i\.vimeocdn\.com\/video\/([0-9]+-[a-zA-Z0-9]+(?:-d)?)_/', $data['thumbnail_url'], $matches)) {
				$thumbnail_id = $matches[1];
				
				// Get high-res webp version
				$poster_url = "https://i.vimeocdn.com/video/{$thumbnail_id}_1920x1080.webp";
				$extension = 'webp';
			}
			
		} else {
			return false;
		}

		if (!$poster_url) {
			return false;
		}

		// Download the poster
		$poster_file = 'poster.' . $extension;
		$poster_path = $video_dir . '/' . $poster_file;
		
		$response = wp_safe_remote_get($poster_url, array(
			'timeout' => 10,
			'stream' => true,
			'filename' => $poster_path
		));
		
		if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
			return false;
		}

		// Verify the downloaded file
		if (!file_exists($poster_path)) {
			return false;
		}
		
		// Check file size (max 1MB)
		$filesize = filesize($poster_path);
		if ($filesize === false || $filesize > 1 * 1024 * 1024) {
			wp_delete_file($poster_path);
			return false;
		} 

		// Get actual image dimensions
		$image_info = wp_getimagesize($poster_path);
		if (!$image_info) {
			wp_delete_file($poster_path);
			return false;
		}
		
		// Validate MIME type
		$allowed_mime_types = array('image/jpeg', 'image/png', 'image/webp');
		if (!in_array($image_info['mime'], $allowed_mime_types, true)) {
			wp_delete_file($poster_path);
			return false;
		}

		// Save cache data 
		$cache_data = array(
			'provider' => $provider, /* 'youtube' or 'vimeo' */
			'poster_file' => $poster_file, 
		); 
		
		// Encode JSON with error checking
		$json_data = json_encode($cache_data, JSON_PRETTY_PRINT);
		if ($json_data === false) {
			wp_delete_file($poster_path);
			return false;
		}
		
		// write to temp file, then rename
		$temp_file = $video_dir . '/poster-cache.tmp';
		$bytes_written = file_put_contents($temp_file, $json_data);
		
		if ($bytes_written === false) {
			wp_delete_file($temp_file);
			wp_delete_file($poster_path);
			return false;
		}
		
		// rename
		if (!rename($temp_file, $video_dir . '/poster-cache.json')) {
			wp_delete_file($temp_file);
			wp_delete_file($poster_path);
			return false;
		}

		return array(
			'url' => esc_url($cache_base_url . '/' . $video_id . '/' . $poster_file),
			'provider' => $provider,
		); 
	}

	/**
	 * Parse video URL to extract provider and video ID 
	 * Supports YouTube and Vimeo URL formats
	 * 
	 * @param string $url The video URL
	 * @return array|false Array with 'provider', 'video_id', and optional 'hash' or false
	 */
	private static function parse_video_url($url) {

		// YouTube patterns (supports all Vidstack formats) 
		$youtube_patterns = array(
			'/^(?:https?:\/\/)?youtube\/([a-zA-Z0-9_-]{6,20})$/',  // youtube/_cMxraX_5RE or http://youtube/_cMxraX_5RE
			'/(?:youtube\.com|youtube-nocookie\.com)\/watch\?v=([a-zA-Z0-9_-]{6,20})/',  // watch?v= (both domains)
			'/youtu\.be\/([a-zA-Z0-9_-]{6,20})/',  // youtu.be short links
			'/(?:youtube\.com|youtube-nocookie\.com)\/embed\/([a-zA-Z0-9_-]{6,20})/',  // embed (both domains)
			'/(?:youtube\.com|youtube-nocookie\.com)\/shorts\/([a-zA-Z0-9_-]{6,20})/'  // shorts (both domains)
		);
		
		foreach ($youtube_patterns as $pattern) {
			if (preg_match($pattern, $url, $matches)) {
				return array(
					'provider' => 'youtube',
					'video_id' => $matches[1]
				);
			}
		}

		// Vimeo patterns (supports all Vidstack formats)
		$vimeo_patterns = array(
			'/^(?:https?:\/\/)?vimeo\/([0-9]{6,20})$/',  // vimeo/640499893 or http://vimeo/640499893
			'/(?:vimeo\.com\/|player\.vimeo\.com\/video\/)([0-9]{6,20})(?:\/([a-zA-Z0-9]{6,20}))?/'  // URLs with optional hash
		); 
		
		foreach ($vimeo_patterns as $pattern) {
			if (preg_match($pattern, $url, $matches)) {
				$result = array(
					'provider' => 'vimeo',
					'video_id' => $matches[1]
				);
				
				// Check for hash (unlisted videos)
				if (isset($matches[2])) {
					$result['hash'] = $matches[2];
				}
				
				return $result;
			}
		}

		return false;
	}

}
