<?php

declare(strict_types=1);

namespace ProudNerds\Helpers;

use ProudNerds\Video\ImageExtractor;
use ProudNerds\Video\QueueManager;
use ProudNerds\Video\Tasks\VideoTask;

/**
 * Helper class for working with local videos.
 */
class VideoLocal
{
    /**
     * Extends the context with video related data
     * This method can be used in blocks to extend the context with video related data
     *
     * @param object $context The context object that will be extended
     * @param int|null $videoId The ID of the video
     * @param int $screenshotLocation The percentage of the video to extract the screenshot from
     * @return void
     */
    public static function extendContextWithVideoData(object $context, ?int $videoId = null, ?int $screenshotLocation = null): void
    {
        if (!method_exists($context, 'set') && !method_exists($context, 'get')) {
            // If the context does not support setting or getting values, we cannot extend it
            return;
        }

        if ($videoId === null) {
            // Try to get the video Id from context if not provided
            $videoId = $context->get('video') ?: null;
        }

        if ($screenshotLocation === null) {
            // Try to get the screenshot location from context if not provided
            $screenshotLocation = (int)$context->get('screenshot_location') ?: 50;
        }

        // Set admin status
        $context->set('is_admin', is_admin());

        // Check if ffmpeg is installed and usable
        $execAvailable = function_exists('exec') && !in_array('exec', explode(',', ini_get('disable_functions')));

        $context->set('ffmpeg_available', $execAvailable && exec('which ffmpeg') !== '');

        // Check if cron has been running in the last 5 minutes
        $context->set('ffmpeg_queue_setup', (get_option('ffmpeg_queue_last_run', 0) > time() - 300));

        if (!$videoId) {
            $context->set(
                'screenshot',
                'data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjIwMHB4IiB3aWR0aD0iMjAwcHgiIHZlcnNpb249IjEuMSIgaWQ9IkNhcGFfMSIgeG1sbnM9'
                . 'Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmlld0JveD0iLTE1'
                . 'MC44IC0xNTAuOCA4MDQuMjYgODA0LjI2IiB4bWw6c3BhY2U9InByZXNlcnZlIiBmaWxsPSIjMDAwMDAwIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9r'
                . 'ZS13aWR0aD0iMS4wMDUzMjgiIHRyYW5zZm9ybT0icm90YXRlKDApIj48ZyBpZD0iU1ZHUmVwb19iZ0NhcnJpZXIiIHN0cm9rZS13aWR0aD0iMCI+'
                . 'PC9nPjxnIGlkPSJTVkdSZXBvX3RyYWNlckNhcnJpZXIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ry'
                . 'b2tlPSIjQ0NDQ0NDIiBzdHJva2Utd2lkdGg9IjEuMDA1MzI4Ij48L2c+PGcgaWQ9IlNWR1JlcG9faWNvbkNhcnJpZXIiPiA8Zz4gPGc+IDxnPiA8'
                . 'cGF0aCBzdHlsZT0iZmlsbDojZmZmZmZmOyIgZD0iTTQxMy4zODIsMjExLjk2NWgtNjAuMjI6di05Ni40MjFIMFYzODcuMTJoMzUzLjE1N3YtNzcu'
                . 'MThoNjAuMTYxbDg5LjM0Niw3NC4yMjVWMTM3LjY1NCBMNDEzLjM4MiwyMTEuOTY1eiBNMTc3LjM1NSwzNTQuNzQyYy00OC43NzIsMC04OS42OTEt'
                . 'MzMuMzctMTAxLjQyNi03OC40OTZINTIuMDcybDM1LjctMzUuNzQzbDM1LjY3OCwzNS43NDNIOTkuODk0IGMxMS4wNjYsMzIuMjI3LDQxLjQ4MSw1'
                . 'NS40MTUsNzcuNDM5LDU1LjQxNWMzOC4yNjcsMCw3MC4yMzQtMjYuMjA4LDc5LjMxNi02MS41NjNoMjMuNzQ5IEMyNzAuODQzLDMxOC4zMDksMjI4'
                . 'LjMyNywzNTQuNzQyLDE3Ny4zNTUsMzU0Ljc0MnogTTI2Ni43MDEsMjU4Ljk5bC0zNS42NzgtMzUuNzIxaDIzLjUxMiBjLTExLjAwMS0zMi4yMjct'
                . 'NDEuNTAyLTU1LjM5NC03Ny40MTgtNTUuMzk0Yy0zOC4yNDUsMC03MC4yMzQsMjYuMTg3LTc5LjI1MSw2MS41ODVINzQuMTM5IGM5LjQ0OC00OC4y'
                . 'MTEsNTEuOTIxLTg0LjYyMiwxMDIuOTM2LTg0LjYyMmM0OC43NSwwLDg5LjY0OCwzMy4zMjcsMTAxLjQyNiw3OC40MzFoMjMuODU3TDI2Ni43MDEs'
                . 'MjU4Ljk5eiI+PC9wYXRoPiA8L2c+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+'
                . 'IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPGc+IDwvZz4gPC9nPiA8'
                . 'L2c+PC9zdmc+'
            );
        }

        $conversionLocation = self::getConversionLocation($videoId);

        // Create the folder if it doesn't exist
        if (!file_exists($conversionLocation)) {
            mkdir($conversionLocation, 0755, true);
        }

        // Check if a screenshot exists, if not create it
        $context->set('screenshot', self::getScreenshotUrl($videoId, $conversionLocation, $screenshotLocation));

        // Get the video URLs
        $videoUrls = self::getVideoUrls($conversionLocation, $videoId);
        $context->set('video_urls', $videoUrls);

        // Make the original file available in the context
        // This ensures we always have a video URL, even if conversion failed
        $originalVideoUrl = wp_get_attachment_url($videoId);

        $context->set('original_video_url', $originalVideoUrl);
        $context->set('original_video_mime', get_post_mime_type($videoId));

        // On admin page, setup the video conversion task
        if (is_admin() && $execAvailable && count($videoUrls) === 0) {
            // Check if file is already transcoded
            self::addVideoTask($videoId, $conversionLocation, $screenshotLocation);
        }
    }

    /**
     * Get the conversion location for a video.
     *
     * @param int $videoId The ID of the video.
     * @return string The path to the conversion location.
     */
    public static function getConversionLocation(int $videoId): string
    {
        return WP_CONTENT_DIR . '/uploads/converted-video/' . sha1((string)$videoId) . '/';
    }

    /**
     * Get the screenshot URL of the video at the given percentage.
     *
     * @param int $videoId The ID of the video.
     * @param string $conversionLocation The location where the video is converted to different codecs.
     * @param int $percentage The percentage of the video to extract the screenshot from.
     *
     * @return string The URL of the screenshot or an empty string if the screenshot could not be created.
     */
    public static function getScreenshotUrl(int $videoId, string $conversionLocation, int $percentage = 50): string
    {
        $videoFile = get_attached_file($videoId);

        $screenshot = $conversionLocation . 'screenshot-' . $percentage . '.jpg';

        // Create the screenshot if it doesn't exist
        if (!file_exists($screenshot)) {
            $imageExtractor = new ImageExtractor();
            $screenshot = $imageExtractor->extract($videoFile, $conversionLocation, $percentage);
        }

        if (!$screenshot) {
            return '';
        }

        return str_replace(WP_CONTENT_DIR, WP_CONTENT_URL, $screenshot);
    }

    /**
     * Get the video URLs of the video in the given conversion location.
     * The video URLs are the URLs of the converted video files.
     * The video files are located in the conversion location and are prefixed with 'video-[SIZE].[EXT=FORMAT]'.
     * Formats of the video files are mp4 and webm as a fallback.
     * Sizes of the video files are 1080p, 720p and 480p.
     * The video URLs are relative to the WP_CONTENT_DIR.
     *
     * @param string $conversionLocation The location where the video is converted to different codecs.
     * @param int|null $videoId Optional video ID for debug information
     * @return array Array of video URLs with format, size and mediaquery information.
     */
    public static function getVideoUrls(string $conversionLocation, ?int $videoId = null): array
    {
        $formats = ['mp4', 'webm'];
        $sizes = [480, 720, 1080];
        $mediaQueries = [
            480 => '(max-width: 480px)',
            720 => '(min-width: 481px and max-width: 720px)',
            1080 => '(min-width: 721px)',
        ];
        $videoUrls = [];
        $debug = [];

        // Try to find converted videos
        foreach ($sizes as $size) {
            foreach ($formats as $format) {
                $filePath = $conversionLocation . 'video-' . $size . 'p.' . $format;
                $debug[] = [
                    'path' => $filePath,
                    'exists' => file_exists($filePath),
                    'size' => file_exists($filePath) ? filesize($filePath) : 0,
                ];

                if (file_exists($filePath) && filesize($filePath) > 0) {
                    $videoUrls[] = [
                        'format' => $format,
                        'size' => [
                            'h' => $size,
                            'w' => floor($size * (16 / 9)),
                        ],
                        'mediaquery' => $mediaQueries[$size],
                        'url' => str_replace(WP_CONTENT_DIR, WP_CONTENT_URL, $filePath),
                    ];
                }
            }
        }

        // Log debug information if no URLs are found and debugging is enabled
        if (empty($videoUrls) && defined('WP_DEBUG') && WP_DEBUG) {
            error_log('VideoLocal debug - No video URLs found:');
            error_log(print_r($debug, true));
            error_log('Conversion location: ' . $conversionLocation);

            if ($videoId) {
                $originalFile = get_attached_file($videoId);
                error_log('Video ID: ' . $videoId);
                error_log('Original file path: ' . $originalFile);
                error_log('Original file exists: ' . (file_exists($originalFile) ? 'yes' : 'no'));
            }
        }

        return $videoUrls;
    }

    /**
     * Add a video task to the queue.
     * This task will convert the video to different codecs and extract a screenshot.
     *
     * @param int $videoId The ID of the video.
     * @param string $conversionLocation The location where the video is converted to different codecs.
     * @param int $screenshotLocation The percentage of the video to extract the screenshot from.
     * @return void
     */
    public static function addVideoTask(int $videoId, string $conversionLocation, int $screenshotLocation = 50): void
    {
        // Create the folder if it doesn't exist
        if (!file_exists($conversionLocation)) {
            mkdir($conversionLocation, 0755, true);
        }

        // Create a new video task and add it to the queue
        $videoTask = new VideoTask($videoId, $conversionLocation, $screenshotLocation);
        QueueManager::getInstance()->addToQueue($videoTask);
    }
}
