<?php
/**
 * Video Data Model
 *
 * Handles database operations for training videos
 *
 * @package    Karate_Club_Manager
 * @subpackage Karate_Club_Manager/includes/classes
 * @since      1.0.27
 */

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

/**
 * Class MACM_Video
 *
 * Manages training video data and operations
 *
 * @since 1.0.27
 */
class MACM_Video {

	/**
	 * Get table name
	 *
	 * @since 1.0.27
	 * @return string Table name with prefix.
	 */
	private static function get_table_name() {
		global $wpdb;
		return $wpdb->prefix . 'macm_training_videos';
	}

	/**
	 * Clear all video caches
	 *
	 * @since 1.0.206
	 * @return void
	 */
	private static function clear_cache() {
		wp_cache_delete( 'macm_videos_all', 'macm' );
		wp_cache_delete( 'macm_videos_count', 'macm' );
	}

	/**
	 * Get all videos
	 *
	 * @since 1.0.27
	 * @param array $args Query arguments.
	 * @return array Array of video objects.
	 */
	public static function get_all( $args = array() ) {
		global $wpdb;
		$table = self::get_table_name();

		$defaults = array(
			'status'  => 'published',
			'orderby' => 'display_order',
			'order'   => 'ASC',
		);

		$args = wp_parse_args( $args, $defaults );

		// Validate order direction.
		$order = in_array( strtoupper( $args['order'] ), array( 'ASC', 'DESC' ), true ) ? strtoupper( $args['order'] ) : 'ASC';

		// Validate orderby field.
		$allowed_orderby = array( 'display_order', 'title', 'created_at', 'updated_at', 'status' );
		$orderby_field   = in_array( $args['orderby'], $allowed_orderby, true ) ? $args['orderby'] : 'display_order';

		// Generate cache key based on args.
		$cache_key = 'macm_videos_' . md5( wp_json_encode( $args ) );
		$cached    = wp_cache_get( $cache_key, 'macm' );
		if ( false !== $cached ) {
			return $cached;
		}

		// $order is validated against whitelist (ASC/DESC only) on line 68.
		// Using explicit conditionals to avoid string concatenation.
		if ( ! empty( $args['status'] ) ) {
			if ( 'ASC' === $order ) {
				$results = $wpdb->get_results(
					$wpdb->prepare(
						'SELECT * FROM %i WHERE status = %s ORDER BY %i ASC',
						$table,
						$args['status'],
						$orderby_field
					)
				);
			} else {
				$results = $wpdb->get_results(
					$wpdb->prepare(
						'SELECT * FROM %i WHERE status = %s ORDER BY %i DESC',
						$table,
						$args['status'],
						$orderby_field
					)
				);
			}
		} elseif ( 'ASC' === $order ) {
				$results = $wpdb->get_results(
					$wpdb->prepare(
						'SELECT * FROM %i ORDER BY %i ASC',
						$table,
						$orderby_field
					)
				);
		} else {
			$results = $wpdb->get_results(
				$wpdb->prepare(
					'SELECT * FROM %i ORDER BY %i DESC',
					$table,
					$orderby_field
				)
			);
		}

		wp_cache_set( $cache_key, $results, 'macm', HOUR_IN_SECONDS );
		return $results;
	}

	/**
	 * Get video by ID
	 *
	 * @since 1.0.27
	 * @param int $video_id Video ID.
	 * @return object|null Video object or null if not found.
	 */
	public static function get( $video_id ) {
		global $wpdb;
		$table = self::get_table_name();

		$cache_key = 'macm_video_' . $video_id;
		$cached    = wp_cache_get( $cache_key, 'macm' );
		if ( false !== $cached ) {
			return $cached;
		}

		$result = $wpdb->get_row(
			$wpdb->prepare(
				'SELECT * FROM %i WHERE id = %d',
				$table,
				$video_id
			)
		);

		if ( $result ) {
			wp_cache_set( $cache_key, $result, 'macm', HOUR_IN_SECONDS );
		}

		return $result;
	}

	/**
	 * Create new video
	 *
	 * @since 1.0.27
	 * @param array $data Video data.
	 * @return int|false Video ID on success, false on failure.
	 */
	public static function create( $data ) {
		global $wpdb;
		$table = self::get_table_name();

		$defaults = array(
			'title'         => '',
			'description'   => '',
			'video_url'     => '',
			'video_type'    => 'youtube',
			'duration'      => '',
			'thumbnail_id'  => null,
			'belt_level'    => '',
			'category'      => '',
			'difficulty'    => '',
			'instructor_id' => null,
			'display_order' => 0,
			'status'        => 'published',
			'created_at'    => current_time( 'mysql' ),
			'updated_at'    => current_time( 'mysql' ),
		);

		$data = wp_parse_args( $data, $defaults );

		// Validate required fields.
		if ( empty( $data['title'] ) || empty( $data['video_url'] ) ) {
			return false;
		}

		$result = $wpdb->insert(
			$table,
			array(
				'title'         => sanitize_text_field( $data['title'] ?? '' ),
				'description'   => wp_kses_post( $data['description'] ?? '' ),
				'video_url'     => $data['video_url'] ? esc_url_raw( $data['video_url'] ) : '',
				'video_type'    => sanitize_text_field( $data['video_type'] ?? 'youtube' ),
				'duration'      => sanitize_text_field( $data['duration'] ?? '' ),
				'thumbnail_id'  => absint( $data['thumbnail_id'] ?? 0 ),
				'belt_level'    => sanitize_text_field( $data['belt_level'] ?? '' ),
				'category'      => sanitize_text_field( $data['category'] ?? '' ),
				'difficulty'    => sanitize_text_field( $data['difficulty'] ?? '' ),
				'instructor_id' => ! empty( $data['instructor_id'] ) ? absint( $data['instructor_id'] ) : null,
				'display_order' => absint( $data['display_order'] ?? 0 ),
				'status'        => sanitize_text_field( $data['status'] ?? 'published' ),
				'created_at'    => $data['created_at'],
				'updated_at'    => $data['updated_at'],
			),
			array( '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%d', '%d', '%s', '%s', '%s' )
		);

		if ( $result ) {
			self::clear_cache();
			return $wpdb->insert_id;
		}

		return false;
	}

	/**
	 * Update video
	 *
	 * @since 1.0.27
	 * @param int   $video_id Video ID.
	 * @param array $data Video data.
	 * @return bool True on success, false on failure.
	 */
	public static function update( $video_id, $data ) {
		global $wpdb;
		$table = self::get_table_name();

		// Add updated_at timestamp.
		$data['updated_at'] = current_time( 'mysql' );

		$update_data = array();
		$format      = array();

		// Only update provided fields.
		$allowed_fields = array(
			'title'         => '%s',
			'description'   => '%s',
			'video_url'     => '%s',
			'video_type'    => '%s',
			'duration'      => '%s',
			'thumbnail_id'  => '%d',
			'belt_level'    => '%s',
			'category'      => '%s',
			'difficulty'    => '%s',
			'instructor_id' => '%d',
			'display_order' => '%d',
			'status'        => '%s',
			'updated_at'    => '%s',
		);

		foreach ( $allowed_fields as $field => $field_format ) {
			if ( isset( $data[ $field ] ) ) {
				// Sanitize based on field - ensure we never pass null to sanitization functions.
				if ( 'title' === $field || 'video_type' === $field || 'duration' === $field || 'status' === $field || 'belt_level' === $field || 'category' === $field || 'difficulty' === $field ) {
					$update_data[ $field ] = sanitize_text_field( $data[ $field ] ?? '' );
				} elseif ( 'description' === $field ) {
					$update_data[ $field ] = wp_kses_post( $data[ $field ] ?? '' );
				} elseif ( 'video_url' === $field ) {
					$update_data[ $field ] = $data[ $field ] ? esc_url_raw( $data[ $field ] ) : '';
				} elseif ( 'thumbnail_id' === $field || 'display_order' === $field ) {
					$update_data[ $field ] = absint( $data[ $field ] ?? 0 );
				} elseif ( 'instructor_id' === $field ) {
					$update_data[ $field ] = ! empty( $data[ $field ] ) ? absint( $data[ $field ] ) : null;
				} else {
					$update_data[ $field ] = $data[ $field ];
				}
				$format[] = $field_format;
			}
		}

		if ( empty( $update_data ) ) {
			return false;
		}

		$result = $wpdb->update(
			$table,
			$update_data,
			array( 'id' => absint( $video_id ) ),
			$format,
			array( '%d' )
		);

		if ( false !== $result ) {
			self::clear_cache();
			wp_cache_delete( 'macm_video_' . $video_id, 'macm' );
		}

		return false !== $result;
	}

	/**
	 * Delete video
	 *
	 * @since 1.0.27
	 * @param int $video_id Video ID.
	 * @return bool True on success, false on failure.
	 */
	public static function delete( $video_id ) {
		global $wpdb;
		$table = self::get_table_name();

		$result = $wpdb->delete(
			$table,
			array( 'id' => absint( $video_id ) ),
			array( '%d' )
		);

		if ( false !== $result && $result > 0 ) {
			self::clear_cache();
			wp_cache_delete( 'macm_video_' . $video_id, 'macm' );
		}

		return false !== $result && $result > 0;
	}

	/**
	 * Get video count
	 *
	 * @since 1.0.27
	 * @param string $status Optional. Filter by status.
	 * @return int Video count.
	 */
	public static function get_count( $status = '' ) {
		global $wpdb;
		$table = self::get_table_name();

		$cache_key = 'macm_videos_count_' . ( $status ? sanitize_key( $status ) : 'all' );
		$cached    = wp_cache_get( $cache_key, 'macm' );
		if ( false !== $cached ) {
			return (int) $cached;
		}

		if ( ! empty( $status ) ) {
			$count = (int) $wpdb->get_var(
				$wpdb->prepare(
					'SELECT COUNT(*) FROM %i WHERE status = %s',
					$table,
					$status
				)
			);
		} else {
			$count = (int) $wpdb->get_var(
				$wpdb->prepare(
					'SELECT COUNT(*) FROM %i',
					$table
				)
			);
		}

		wp_cache_set( $cache_key, $count, 'macm', HOUR_IN_SECONDS );
		return $count;
	}

	/**
	 * Get embed code for video
	 *
	 * @since 1.0.27
	 * @param object $video Video object.
	 * @return string HTML embed code.
	 */
	public static function get_embed_code( $video ) {
		if ( ! $video || empty( $video->video_url ) ) {
			return '';
		}

		$embed_code = '';

		switch ( $video->video_type ) {
			case 'youtube':
				$video_id = self::extract_youtube_id( $video->video_url );
				if ( $video_id ) {
					$embed_code = sprintf(
						'<iframe width="100%%" height="360" src="https://www.youtube.com/embed/%s" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
						esc_attr( $video_id )
					);
				}
				break;

			case 'vimeo':
				$video_id = self::extract_vimeo_id( $video->video_url );
				if ( $video_id ) {
					$embed_code = sprintf(
						'<iframe src="https://player.vimeo.com/video/%s" width="100%%" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>',
						esc_attr( $video_id )
					);
				}
				break;

			case 'direct':
				$embed_code = sprintf(
					'<video width="100%%" height="360" controls><source src="%s" type="video/mp4">Your browser does not support the video tag.</video>',
					esc_url( $video->video_url )
				);
				break;

			case 'embed':
				// For custom embed codes, sanitize but allow iframe.
				$embed_code = wp_kses(
					$video->video_url,
					array(
						'iframe' => array(
							'src'             => array(),
							'width'           => array(),
							'height'          => array(),
							'frameborder'     => array(),
							'allow'           => array(),
							'allowfullscreen' => array(),
						),
					)
				);
				break;
		}

		return $embed_code;
	}

	/**
	 * Extract YouTube video ID from URL
	 *
	 * @since 1.0.27
	 * @param string $url YouTube URL.
	 * @return string|false Video ID or false.
	 */
	private static function extract_youtube_id( $url ) {
		$patterns = array(
			'/youtube\.com\/watch\?v=([^&]+)/',
			'/youtube\.com\/embed\/([^?]+)/',
			'/youtu\.be\/([^?]+)/',
		);

		foreach ( $patterns as $pattern ) {
			if ( preg_match( $pattern, $url, $matches ) ) {
				return $matches[1];
			}
		}

		return false;
	}

	/**
	 * Extract Vimeo video ID from URL
	 *
	 * @since 1.0.27
	 * @param string $url Vimeo URL.
	 * @return string|false Video ID or false.
	 */
	private static function extract_vimeo_id( $url ) {
		if ( preg_match( '/vimeo\.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/', $url, $matches ) ) {
			return $matches[1];
		}

		return false;
	}

	/**
	 * Get video thumbnail URL
	 *
	 * @since 1.0.27
	 * @param object $video Video object.
	 * @param string $size Image size.
	 * @return string|false Thumbnail URL or false.
	 */
	public static function get_thumbnail_url( $video, $size = 'medium' ) {
		if ( ! $video ) {
			return false;
		}

		// If custom thumbnail is set, use it.
		if ( ! empty( $video->thumbnail_id ) ) {
			$thumbnail = wp_get_attachment_image_url( $video->thumbnail_id, $size );
			if ( $thumbnail ) {
				return $thumbnail;
			}
		}

		// Otherwise, try to get thumbnail from video service.
		switch ( $video->video_type ) {
			case 'youtube':
				$video_id = self::extract_youtube_id( $video->video_url );
				if ( $video_id ) {
					return 'https://img.youtube.com/vi/' . $video_id . '/mqdefault.jpg';
				}
				break;

			case 'vimeo':
				// Vimeo requires API call for thumbnail, skip for now.
				// Could be implemented with transient caching.
				break;
		}

		return false;
	}
}
