<?php
namespace Vidplex\Tables;

// If this file is called directly, abort.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Videos
 *
 * Manages video-related database operations for the plugin.
 *
 * Handles CRUD operations, caching, and retrieval of video records,
 * including filtering, counting, and association with channels.
 */
class Videos {
	/**
	 * Singleton instance of the class.
	 *
	 * @var self|null
	 */
	public static $instance;

	/**
	 * Database table name for storing videos.
	 *
	 * @var string
	 */
	private $table_name;

	/**
	 * Cache key prefix for video-related transient or object cache entries.
	 * Used to prevent cache key collisions across the application.
	 *
	 * @var string
	 */
	private $cache_prefix = 'vidplex_videos_cache_';


	/**
	 * Returns the singleton instance of the class.
	 *
	 * Ensures that only one instance of the class is created and reused
	 * throughout the request lifecycle.
	 *
	 * @return self
	 */
	public static function get_instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Class constructor.
	 *
	 * Initializes required properties and sets the database table name
	 * using the WordPress database prefix.
	 *
	 * @global wpdb $wpdb WordPress database abstraction object.
	 */
	private function __construct() {
		global $wpdb;
		$this->table_name = $wpdb->prefix . 'vidplex_yt_videos';
	}

	/**
	 * Deletes the cached video statistics transient.
	 *
	 * Clears any stored transient or object cache data related to
	 * video statistics to ensure fresh data is generated on next access.
	 *
	 * @return void
	 */
	private function delete_videos_stat_transient() {
		$vidplex_stats = get_transient( 'vidplex_stats' );
		if ( is_array( $vidplex_stats ) && isset( $vidplex_stats['total_videos'] ) ) {
			$settings = \Vidplex\Helpers::advanced_settings();

			unset( $vidplex_stats['total_videos'] );
			set_transient( 'vidplex_stats', $vidplex_stats, $settings['video_refresh_interval'] );
		}
	}

	/**
	 * Retrieves the total number of videos.
	 *
	 * Returns the total count of video records stored in the database.
	 * Cached data may be used internally to improve performance.
	 *
	 * @return int Total number of videos.
	 */
	public function get_active_videos_count() {
		$settings = \Vidplex\Helpers::advanced_settings();

		$vidplex_stats = get_transient( 'vidplex_stats' );

		if ( ! is_array( $vidplex_stats ) ) {
			$vidplex_stats = array();
		}

		if ( isset( $vidplex_stats['total_videos'] ) ) {
			return (int) $vidplex_stats['total_videos'];
		}

		global $wpdb;
		$count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$this->table_name} WHERE status='active' AND is_shorts=0" );

		$vidplex_stats['total_videos'] = $count;

		set_transient( 'vidplex_stats', $vidplex_stats, $settings['video_refresh_interval'] );

		return $count;
	}

	/**
	 * Retrieves the total number of videos for a specific channel.
	 *
	 * Counts and returns the number of video records associated
	 * with the given channel ID.
	 *
	 * @param string $channel_id Unique identifier of the channel.
	 * @param string $status Fetch videos based on their status, 'all' fetches existing videos discarding their status.
	 * @return int Total number of videos for the specified channel.
	 */
	public function get_videos_count_by_channel( $channel_id, $status = 'active' ) {
		switch ( $status ) {
			case 'active':
				$results = $this->get_videos_by_channel( $channel_id );

				if ( empty( $results ) ) {
					return '0';
				} else {
					return count( $results );
				}

			case 'all':
				$channel = \Vidplex\Store::get_instance()->channels->get_channel_by_id( $channel_id );

				global $wpdb;
				$count = (int) $wpdb->get_var(
					$wpdb->prepare(
						"SELECT COUNT(*) 
						FROM {$this->table_name} 
						WHERE channel_id = %d AND is_shorts=0",
						$channel[0]['id']
					)
				);
				return $count;

			default:
				return '0';
		}
	}

	/**
	 * Retrieves all videos for a specific channel.
	 *
	 * Fetches the list of video records associated with the given
	 * channel ID from the database.
	 *
	 * @param string|int $channel_id Unique identifier of the channel.
	 * @return array List of videos for the specified channel.
	 */
	public function get_videos_by_channel( $channel_id ) {
		$channel = \Vidplex\Store::get_instance()->channels->get_channel_by_id( $channel_id );

		$cached = get_transient( $this->cache_prefix . $channel[0]['id'] );

		if ( $cached !== false ) {
			return $cached;
		}

		$channel_id = isset( $channel[0]['id'] ) ? (int) $channel[0]['id'] : 0;

		if ( 0 === $channel_id ) {
			return array();
		}

		global $wpdb;

		$query = $wpdb->prepare(
			"SELECT * FROM {$this->table_name} WHERE channel_id = %d AND status='active' AND is_shorts=0 ORDER BY published_at DESC",
			$channel_id
		);

		$results = $wpdb->get_results( $query, ARRAY_A );

		$settings = \Vidplex\Helpers::advanced_settings();

		set_transient( $this->cache_prefix . $channel[0]['channel_id'], $results, $settings['video_refresh_interval'] );

		return $results;
	}

	/**
	 * Convert an ISO 8601 duration string to total seconds.
	 *
	 * Parses a YouTube-style ISO 8601 duration string (e.g., "PT1H2M10S",
	 * "PT45S", "PT10M30S") and converts it into the total number of seconds.
	 *
	 * This is commonly used when determining whether a video qualifies
	 * as a Short (≤ 60 seconds) or for general duration comparisons.
	 *
	 * @param string $duration ISO 8601 duration string returned by the
	 *                         YouTube Data API (contentDetails.duration).
	 *
	 * @return int Total duration in seconds.
	 */
	private function iso8601_to_seconds( $duration ) {
		preg_match_all( '/(\d+)([HMS])/', $duration, $matches, PREG_SET_ORDER );
		$seconds = 0;

		foreach ( $matches as $match ) {
			$value = (int) $match[1];
			switch ( $match[2] ) {
				case 'H':
					$seconds += $value * 3600;
					break;
				case 'M':
					$seconds += $value * 60;
					break;
				case 'S':
					$seconds += $value;
					break;
			}
		}
		return $seconds;
	}

	/**
	 * Filters and normalizes raw video data retrieved from the YouTube API.
	 *
	 * Processes the given videos array, extracts required properties,
	 * optionally enriches data using the provided API key, and returns
	 * a structured array ready for database insertion or further processing.
	 *
	 * @param array  $videos   Raw video items returned from the YouTube API.
	 * @param string $api_key  YouTube Data API key used for additional requests (if required).
	 *
	 * @return array Filtered and formatted list of video properties.
	 */
	private function filter_video_properties( $videos, $api_key ) {
		$filtered_list = array_map(
			function ( $video ) use ( $api_key ) {
				$url = "https://www.googleapis.com/youtube/v3/videos?part=statistics,contentDetails&key={$api_key}&id={$video['snippet']['resourceId']['videoId']}";

				$response = wp_remote_get( $url );

				$stats = array();

				$content_details = array();

				if ( ! is_wp_error( $response ) ) {
					$body = wp_remote_retrieve_body( $response );
					$data = json_decode( $body, true );

					$stats           = $data['items'][0]['statistics'];
					$content_details = $data['items'][0]['contentDetails'];
				}

				$is_shorts = 0;
				/*
				if ( isset( $content_details['duration'] ) ) {
					$total_seconds = $this->iso8601_to_seconds( $content_details['duration'] );
					$shorts_limit  = 180;
					$settings      = \Vidplex\Helpers::advanced_settings();

					if ( isset( $settings['long_shorts_as_video'] ) && filter_var( $settings['long_shorts_as_video'], FILTER_VALIDATE_BOOLEAN ) ) {
						$shorts_limit = 60;
					}

					if ( $total_seconds <= $shorts_limit ) {
						$is_shorts = 1;
					} else {
						$is_shorts = 0;
					}
				} */

				return array(
					'video_id'          => $video['snippet']['resourceId']['videoId'],
					'video_title'       => $video['snippet']['title'],
					'video_description' => $video['snippet']['description'],
					'published_at'      => $video['snippet']['publishedAt'],
					'statistics'        => wp_json_encode( $stats ),
					'is_shorts'         => $is_shorts,
				);
			},
			$videos
		);

		return $filtered_list;
	}

	/**
	 * Filter videos published after a specific date.
	 *
	 * Iterates through the provided video array and returns only the videos
	 * that were published more recently than the given last published date.
	 * This is typically used to detect newly uploaded videos during sync.
	 *
	 * Each video array item is expected to contain a 'published_at'
	 * (or equivalent) date field in a comparable datetime format.
	 *
	 * @param array  $videos               Array of video data arrays.
	 * @param string $last_published_date  Datetime string representing the
	 *                                      cutoff date. Videos published after
	 *                                      this date will be returned.
	 *
	 * @return array Filtered array containing only recently published videos.
	 */
	private function filter_recent_videos( $videos, $last_published_date ) {
		return array_filter(
			$videos,
			function ( $video ) use ( $last_published_date ) {
				if ( empty( $video['snippet']['publishedAt'] ) ) {
					return false;
				}

				return strtotime( $video['snippet']['publishedAt'] ) >= strtotime( $last_published_date );
			}
		);
	}

	/**
	 * Fetch videos from a channel's uploads playlist.
	 *
	 * Retrieves a batch of videos from the specified channel's uploads playlist
	 * using the YouTube Data API. The number of results returned can be limited
	 * using the $max_results parameter.
	 *
	 * This method is typically used for:
	 * - Fetching additional videos for pagination
	 * - Balancing shorts with regular videos
	 * - Retrieving recent uploads from a channel
	 *
	 * @param int    $channel_id  Internal channel ID used to locate the channel
	 *                            and its uploads playlist.
	 * @param string $api_key     YouTube Data API key used to authenticate the request.
	 * @param int    $max_results Optional. Maximum number of videos to retrieve.
	 *                            Default is 50.
	 *
	 * @return array|false Array of playlist video items on success,
	 *                     or false on failure.
	 */
	private function fetch_uploads_playlist( $channel_id, $api_key, $max_results = 50 ) {
		$channel = \Vidplex\Store::get_instance()->channels->get_channel_by_id( $channel_id );

		$url = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&key={$api_key}&playlistId={$channel[0]['channel_uploads_playlist']}&maxResults={$max_results}";

		if ( isset( $channel[0]['next_page_token'] ) ) {
			$url .= "&pageToken={$channel[0]['next_page_token']}";
		}

		$response = wp_remote_get( $url );

		if ( is_wp_error( $response ) ) {
			$error_message = $response->get_error_message();
			return wp_send_json_error( array( 'message' => 'Something went wrong!' . $error_message ) );
		}

		$body = wp_remote_retrieve_body( $response );
		$data = json_decode( $body, true );

		if ( ! isset( $data['items'] ) || empty( $data['items'] ) ) {
			return array();
		}

		// Check if next page token exists.
		if ( isset( $data['nextPageToken'] ) ) {
			\Vidplex\Store::get_instance()->channels->update(
				$channel_id,
				array(
					'next_page_token' => $data['nextPageToken'],
				)
			);
		}

		return $data['items'];
	}

	/**
	 * Balance shorts with regular videos for a channel.
	 *
	 * Ensures that if the current video collection contains YouTube Shorts,
	 * additional videos are fetched from the channel uploads playlist
	 * to balance the overall video set.
	 *
	 * The method:
	 * - Counts existing shorts in the provided video array.
	 * - Checks whether a next page token exists for further fetching.
	 * - Retrieves additional uploads if needed.
	 * - Merges new videos into the existing array until balance is achieved
	 *   or no more videos can be fetched.
	 *
	 * @param array  $videos     Array of video data arrays. Each video item must contain
	 *                           an 'is_shorts' key (1 for short, 0 for regular video).
	 * @param int    $channel_id Channel ID used to retrieve channel details and uploads.
	 * @param string $api_key    YouTube Data API key used for fetching additional videos.
	 *
	 * @return array Modified array of videos after balancing shorts with regular videos.
	 */
	private function balance_shorts_with_videos( $videos, $channel_id, $api_key ) {
		$channel = \Vidplex\Store::get_instance()->channels->get_channel_by_id( $channel_id );

		$shorts_count = count( array_filter( $videos, fn( $v ) => 1 === intval( $v['is_shorts'] ) ) );

		if ( ! isset( $channel[0]['next_page_token'] ) || empty( $channel[0]['next_page_token'] ) || $shorts_count <= 0 ) {
			return $videos;
		}

		$balanced = false;

		$max_loop = 3;
		$i        = 1;

		while ( ! $balanced ) {
			$uploads_playlist = $this->fetch_uploads_playlist( $channel_id, $api_key, $shorts_count );

			$new_videos = $this->filter_video_properties( $uploads_playlist, $api_key );

			$shorts_count = count( array_filter( $new_videos, fn( $v ) => 1 === intval( $v['is_shorts'] ) ) );

			$videos = array_merge( $videos, $new_videos );

			++$i;

			if ( $shorts_count <= 0 || $i >= $max_loop ) {
				$balanced = true;
			}
		}

		return $videos;
	}

	/**
	 * Refreshes and synchronizes videos for a specific YouTube channel.
	 *
	 * Fetches the latest uploads using the YouTube Data API, compares them
	 * with existing records in the database, inserts new videos, and updates
	 * modified entries if necessary.
	 *
	 * This method ensures the local video table remains in sync with
	 * the channel’s uploads playlist.
	 *
	 * @param string $channel_id Youtube channel ID.
	 * @param string $api_key    YouTube Data API key used to fetch channel videos.
	 *
	 * @return array $truncated_videos New videos to insert or empty array if new videos don't exist.
	 */
	private function refresh_channel_videos( $channel_id, $api_key ) {
		$settings = \Vidplex\Helpers::advanced_settings();

		$videos_count    = $this->get_videos_count_by_channel( $channel_id, 'all' );
		$remaining_quota = max( 0, $settings['max_videos_per_channel'] - $videos_count );

		$truncated_videos = array();

		$channel = \Vidplex\Store::get_instance()->channels->get_channel_by_id( $channel_id );

		global $wpdb;

		/* Refreshes the current video list */
		$query = $wpdb->prepare(
			"SELECT * FROM {$this->table_name} WHERE channel_id = %d ORDER BY published_at DESC",
			$channel[0]['id']
		);

		$all_videos_by_channel = $wpdb->get_results( $query, ARRAY_A );

		foreach ( $all_videos_by_channel as $index => $video ) {
			$url = "https://www.googleapis.com/youtube/v3/videos?part=snippet,statistics,contentDetails&key={$api_key}&id={$video['video_id']}";

			$response = wp_remote_get( $url );

			if ( ! is_wp_error( $response ) ) {
				$body = wp_remote_retrieve_body( $response );
				$data = json_decode( $body, true );

				if ( isset( $data['items'] ) && empty( $data['items'] ) ) {
					$video['status'] = 'removed';
					$this->update( $video['video_id'], $video, $channel[0]['id'] );
				} elseif ( isset( $data['items'] ) && ! empty( $data['items'] ) ) {
					$updated_data = $data['items'][0];

					$video['video_title']       = $updated_data['snippet']['title'];
					$video['video_description'] = $updated_data['snippet']['description'];
					$video['published_at']      = $updated_data['snippet']['publishedAt'];
					$video['statistics']        = wp_json_encode( $updated_data['statistics'] );
					$video['status']            = 'active';

					$is_shorts = 0;

					/* $total_seconds = $this->iso8601_to_seconds( $updated_data['contentDetails']['duration'] );
					$shorts_limit  = 180;
					if ( isset( $settings['long_shorts_as_video'] ) && filter_var( $settings['long_shorts_as_video'], FILTER_VALIDATE_BOOLEAN ) ) {
						$shorts_limit = 60;
					}

					if ( $total_seconds <= $shorts_limit ) {
						$is_shorts = 1;
					} else {
						$is_shorts = 0;
					} */

					$video['is_shorts'] = $is_shorts;

					$this->update( $video['video_id'], $video, $channel_id );
				}
			}
		}

		// Clear the video transient after refresh.
		delete_transient( $this->cache_prefix . $channel_id );

		/* Check for any recent uploads */
		$max_pages           = 3;
		$last_video          = $all_videos_by_channel[0];
		$last_published_date = $last_video['published_at'];

		$videos_count = $this->get_videos_count_by_channel( $channel_id );

		if ( $remaining_quota > 0 ) {

			$max_results = $settings['videos_per_fetch'];

			if ( $remaining_quota <= $max_results ) {
				$max_results = $remaining_quota;
			}

			$new_videos      = array();
			$next_page_token = null;
			for ( $i = 1;$i <= $max_pages;$i++ ) {
				$url = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&key={$api_key}&playlistId={$channel[0]['channel_uploads_playlist']}&maxResults={$max_results}";
				if ( ! is_null( $next_page_token ) && ! empty( $next_page_token ) ) {
					$url .= "&pageToken={$next_page_token}";
				}

				$response = wp_remote_get( $url );

				if ( is_wp_error( $response ) ) {
					$error_message = $response->get_error_message();
					return wp_send_json_error( array( 'message' => 'Something went wrong!' . $error_message ) );
				}

				$body = wp_remote_retrieve_body( $response );
				$data = json_decode( $body, true );

				if ( ! isset( $data['items'] ) || empty( $data['items'] ) ) {
					break;
				}

				// Check if next page token exists.
				if ( isset( $data['nextPageToken'] ) ) {
					$next_page_token = $data['nextPageToken'];
				}

				$new_uploads = $data['items'];
				if ( ! empty( $new_uploads ) ) {
					$new_videos = array_merge( $new_videos, $new_uploads );
				}

				$new_videos = $this->filter_recent_videos( $new_videos, $last_published_date );

				if ( count( $new_videos ) < $max_results ) {
					break;
				}
			}

			// Got recent uploads excluding the last published video in the list.
			if ( ! empty( $new_videos ) ) {
				$truncated_videos = array_values(
					array_filter(
						$new_videos,
						function ( $video ) use ( $last_video ) {
								return isset( $video['snippet']['resourceId']['videoId'] )
								&& $video['snippet']['resourceId']['videoId'] !== $last_video['video_id'];
						}
					)
				);
			}

			$max_results -= count( $truncated_videos );

			if ( $max_results < 0 ) {
				$excess_count = abs( $max_results );

				$sql = $wpdb->prepare(
					"DELETE FROM {$this->table_name} WHERE channel_id=%d ORDER BY published_at ASC LIMIT %d",
					$channel[0]['id'],
					$excess_count
				);

				$wpdb->query( $sql );
			} elseif ( $max_results > 0 ) {
				// Fetch next page videos from the uploads playlist.
				$next_page_results = $this->fetch_uploads_playlist( $channel_id, $api_key, $max_results );
				// Merge with $truncated_videos, which has recently published videos.
				$truncated_videos = array_merge( $truncated_videos, $next_page_results );
				// Filter the videos with necessary properties.
				$truncated_videos = $this->filter_video_properties( $truncated_videos, $api_key );
				// Balance shorts with videos.
				$truncated_videos = $this->balance_shorts_with_videos( $truncated_videos, $channel_id, $api_key );

				return $truncated_videos;
			}
		} elseif ( $remaining_quota <= 0 && ( $settings['max_videos_per_channel'] - $videos_count ) < 0 ) {
			// Mark the excess videos inactive. This occurs if the $settings['max_videos_per_channel'] is reduced.

			$excess_count = abs( $settings['max_videos_per_channel'] - $videos_count );

			$sql = $wpdb->prepare(
				"UPDATE {$this->table_name} AS t
				INNER JOIN (
					SELECT id
					FROM {$this->table_name}
					WHERE channel_id = %d
					ORDER BY published_at ASC
					LIMIT %d
				) AS sub ON t.id = sub.id
				SET t.status = %s",
				$channel[0]['id'],
				$excess_count,
				'inactive'
			);

			$wpdb->query( $sql );
		}

		return array();
	}

	/**
	 * Retrieves videos for a specific YouTube channel.
	 *
	 * Sends a request to the YouTube Data API using the channel's uploads
	 * playlist and returns the raw response data or a structured array
	 * of video items for further processing.
	 *
	 * This method is responsible only for fetching the data and does not
	 * handle database insertion or synchronization logic.
	 *
	 * @param string $channel_id Youtube channel ID.
	 * @param string $api_key    YouTube Data API key used to request video data.
	 *
	 * @return array|false Array of fetched video data on success, or false on failure.
	 */
	private function fetch_videos_by_channel( $channel_id, $api_key ) {
		$settings = \Vidplex\Helpers::advanced_settings();

		$max_results = $settings['videos_per_fetch'];
		if ( $settings['max_videos_per_channel'] < $max_results ) {
			$max_results = $settings['max_videos_per_channel'];
		}

		$uploads_playlist = $this->fetch_uploads_playlist( $channel_id, $api_key, $max_results );

		if ( empty( $uploads_playlist ) ) {
			return array();
		}

		// Format and filter the fetched data, and only extract the necessary data.
		$videos = $this->filter_video_properties( $uploads_playlist, $api_key );

		// $videos = $this->balance_shorts_with_videos( $videos, $channel_id, $api_key );

		return $videos;
	}

	/**
	 * Initiates synchronization of videos for a specific channel.
	 *
	 * Acts as the public entry point for triggering a video sync process.
	 * Validates the channel ID, retrieves required configuration (such as API key),
	 * and coordinates the refresh/update workflow for the channel's videos.
	 *
	 * Typically used by scheduled tasks (WP-Cron), manual triggers,
	 * or AJAX requests.
	 *
	 * @param string $channel_id Youtube channel ID.
	 * @return bool True on successful sync, false on failure.
	 */
	public function sync_channel_videos( $channel_id ) {
		$api_key = get_option( 'vidplex-api-key' );

		if ( empty( $api_key ) ) {
			wp_send_json_error( array( 'message' => 'Invalid API key.' ) );
		}

		$channel = \Vidplex\Store::get_instance()->channels->get_channel_by_id( $channel_id );

		$videos_count_by_channel = $this->get_videos_count_by_channel( $channel_id, 'all' );

		$videos = array();

		// Triggers first fetch for the channel.
		if ( empty( $channel ) || $videos_count_by_channel <= 0 ) {
			$videos = $this->fetch_videos_by_channel( $channel_id, $api_key );

			if ( empty( $videos ) ) {
				wp_send_json_error( array( 'message' => 'No videos found for this channel.' ) );
			}
		}

		// Triggers refresh for the channel. The channel is already set.
		if ( ! empty( $channel ) && $videos_count_by_channel > 0 ) {
			$videos = $this->refresh_channel_videos( $channel_id, $api_key );
		}

		if ( ! empty( $videos ) ) {
			$this->insert( $videos, $channel );
		}

		// Check for database storage limit per channel.
		$settings                = \Vidplex\Helpers::advanced_settings();
		$videos_count_by_channel = $this->get_videos_count_by_channel( $channel_id );
		if ( $videos_count_by_channel > $settings['max_videos_per_channel'] ) {
			return wp_send_json_error( array( 'message' => 'You’ve reached the maximum database storage limit. Increase the limit to continue adding videos.' ) );
		}

		// Clear the admin dashboard statistics transient.
		$this->delete_videos_stat_transient();

		// Update channel's sync datetime.
		\Vidplex\Store::get_instance()->channels->update(
			$channel_id,
			array(
				'last_sync_at' => gmdate( 'Y-m-d H:i:s' ),
			)
		);

		// Clear the video transient after insert.
		delete_transient( $this->cache_prefix . $channel_id );

		// Return success flag for successful video insertion.
		return true;
	}

	/**
	 * Inserts the list of videos.
	 *
	 * Fetches videos for the given channel ID and stores them
	 * in the database, updating existing records if necessary.
	 *
	 * @param array $videos Videos to store in the table.
	 * @param array $channel single associative array with index 0.
	 */
	public function insert( $videos, $channel ) {
		if ( empty( $videos ) || ! is_array( $videos ) || empty( $channel ) || ! is_array( $channel ) ) {
			return;
		}

		global $wpdb;
		$wpdb->hide_errors();

		foreach ( $videos as $video ) {
			$exists = $wpdb->get_var(
				$wpdb->prepare(
					"SELECT COUNT(*) FROM {$this->table_name} WHERE video_id = %s",
					$video['video_id']
				)
			);

			// Only insert into database if video doesn't exist.
			if ( ! $exists && isset( $channel[0]['id'] ) ) {
				$wpdb->query( 'START TRANSACTION' );

				$result = $wpdb->insert(
					$this->table_name,
					array(
						'channel_id'        => $channel[0]['id'], // make sure this exists.
						'video_id'          => $video['video_id'],
						'video_title'       => $video['video_title'],
						'video_description' => $video['video_description'],
						'published_at'      => $video['published_at'],
						'statistics'        => $video['statistics'],
						// 'is_shorts'         => $video['is_shorts'],
					),
					array( '%d', '%s', '%s', '%s', '%s', '%s' )
				);

				if ( false === $result || $wpdb->last_error ) {
					$wpdb->query( 'ROLLBACK' );
					continue;
				}

				$wpdb->query( 'COMMIT' );
			}
		}
	}

	/**
	 * Updates a specific video record for a channel.
	 *
	 * Modifies the video data for the given video ID and channel ID
	 * in the database.
	 *
	 * @param string|int $video_id   Unique identifier of the video to update.
	 * @param array      $data       Associative array of video data to update.
	 * @param int        $channel_id Internal database ID of the channel.
	 * @return bool True on successful update, false on failure.
	 */
	public function update( $video_id, $data, $channel_id ) {
		global $wpdb;

		$result = $wpdb->update( $this->table_name, $data, array( 'video_id' => $video_id ) );

		if ( false === $result || $wpdb->last_error ) {
			// There was a database error.
			return false;
		}

		// Clear the cache after insert.
		delete_transient( $this->cache_prefix . $channel_id );

		return true;
	}

	/**
	 * Removes all videos for a specific channel.
	 *
	 * Deletes all video records associated with the given channel ID
	 * from the database.
	 *
	 * @param string|int $channel_id Unique identifier of the channel.
	 * @return bool True on successful deletion, false on failure.
	 */
	public function delete( $channel_id ) {
		global $wpdb;

		$channel = \Vidplex\Store::get_instance()->channels->get_channel_by_id( $channel_id );

		$result = $wpdb->delete( $this->table_name, array( 'channel_id' => $channel[0]['id'] ) );

		if ( false === $result || $wpdb->last_error ) {
			return false;
		}

		delete_transient( $this->cache_prefix . $channel_id );

		$this->delete_videos_stat_transient();

		return true;
	}
}
