<?php

namespace App\Helpers\FirstParty\MediaEngine;

use App\Models\Media;
use App\Models\MediaCollection;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class MediaEngine
{
    const LOCAL_TEMP_DISK = 'local_temp';

    const USER_UPLOADED = 'user_uploaded';

    const USER_RENDERED = 'user_rendered';

    const USER = 'user';

    public static function getMediaCloudUrl($media)
    {
        // Check for uploaded_url first (URL-based media)
        if (! empty($media->uploaded_url)) {
            return $media->uploaded_url;
        }

        // Fallback to storage disk URL (file-based media)
        return Storage::disk($media->disk)->url($media->file_path.$media->file_name);
    }

    public static function loadMediaToLocalTemp($uuid)
    {
        $media = self::getMediaByUuid($uuid);

        $result = [
            'media' => $media,
            'uuid' => $uuid,
            'temp_path' => null,
            'temp' => null,
        ];

        if (! $media) {
            return $result;
        }

        $tempPath = 'temp_'.$media->file_name;

        // Handle URL-based media
        if (! empty($media->uploaded_url)) {
            if (! Storage::disk(self::LOCAL_TEMP_DISK)->exists($tempPath)) {
                $fileContent = @file_get_contents($media->uploaded_url);
                if ($fileContent !== false) {
                    if (Storage::disk(self::LOCAL_TEMP_DISK)->put($tempPath, $fileContent)) {
                        $result['temp_path'] = $tempPath;
                        $result['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath);
                    }
                }
            } else {
                $result['temp_path'] = $tempPath;
                $result['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath);
            }

            return $result;
        }

        // Handle file-based media (existing behavior)
        if (! Storage::disk($media->disk)->exists($media->file_path.$media->file_name)) {
            return $result;
        }

        $fileContent = Storage::disk($media->disk)->get($media->file_path.$media->file_name);

        if (! Storage::disk(self::LOCAL_TEMP_DISK)->exists($tempPath)) {
            if (Storage::disk(self::LOCAL_TEMP_DISK)->put($tempPath, $fileContent)) {
                $result['temp_path'] = $tempPath;
                $result['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath);
            }
        } else {
            $result['temp_path'] = $tempPath;
            $result['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath);
        }

        return $result;
    }

    public static function loadMediasToLocalTemp(array $uuids)
    {
        $medias = self::getMediaByUuids($uuids);
        $result = [];

        foreach ($medias as $media) {
            if (! $media) {
                continue; // Skip invalid media instead of returning early
            }

            $tempPath = 'temp_'.$media->file_name;
            $singleResult = [
                'media' => $media,
                'uuid' => $media->uuid,
                'temp_path' => null,
                'temp' => null,
                'cloud_url' => self::getMediaCloudUrl($media),
            ];

            // Handle URL-based media
            if (! empty($media->uploaded_url)) {
                if (Storage::disk(self::LOCAL_TEMP_DISK)->exists($tempPath)) {
                    // File already exists in temp, just set the paths
                    $singleResult['temp_path'] = $tempPath;
                    $singleResult['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath);
                } else {
                    // File needs to be downloaded to temp
                    $fileContent = @file_get_contents($media->uploaded_url);
                    if ($fileContent !== false) {
                        if (Storage::disk(self::LOCAL_TEMP_DISK)->put($tempPath, $fileContent)) {
                            $singleResult['temp_path'] = $tempPath;
                            $singleResult['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath);
                        }
                    }
                }
                $result[] = $singleResult;

                continue;
            }

            // Handle file-based media (existing behavior)
            // Check if file exists in cloud storage
            if (! Storage::disk($media->disk)->exists($media->file_path.$media->file_name)) {
                $result[] = $singleResult; // Add to results but with null temp paths

                continue;
            }

            // Check if already loaded in local temp
            if (Storage::disk(self::LOCAL_TEMP_DISK)->exists($tempPath)) {
                // File already exists in temp, just set the paths
                $singleResult['temp_path'] = $tempPath;
                $singleResult['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath);
            } else {
                // File needs to be loaded to temp
                $fileContent = Storage::disk($media->disk)->get($media->file_path.$media->file_name);
                if (Storage::disk(self::LOCAL_TEMP_DISK)->put($tempPath, $fileContent)) {
                    $singleResult['temp_path'] = $tempPath;
                    $singleResult['temp'] = Storage::disk(self::LOCAL_TEMP_DISK)->path($tempPath);
                }
            }

            $result[] = $singleResult;
        }

        return $result;
    }

    public static function deleteMediaFromLocalTemp($uuid)
    {
        $result = self::loadMediaToLocalTemp($uuid);

        if ($result['temp_path']) {
            Storage::disk(self::LOCAL_TEMP_DISK)->delete($result['temp_path']);
        }

        return [
            'media' => $result['media'],
            'uuid' => $uuid,
            'temp_path' => null,
            'temp' => null,
        ];
    }

    public static function deleteMediasFromLocalTemp(array $uuids)
    {
        $results = self::loadMediasToLocalTemp($uuids);
        $deletedResults = [];

        foreach ($results as $result) {
            if ($result['temp_path']) {
                Storage::disk(self::LOCAL_TEMP_DISK)->delete($result['temp_path']);
            }

            $deletedResults[] = [
                'media' => $result['media'],
                'uuid' => $result['uuid'],
                'temp_path' => null,
                'temp' => null,
            ];
        }

        return $deletedResults;
    }

    /**
     * Add a new media file to the specified media collection.
     *
     * Example: $newMedia = MediaEngine::addMedia(
     *                         'bgm_collection',
     *                         'bgm',
     *                         'user_upload',
     *                         'web_app',
     *                         $fileContent,  // File content or null
     *                         $url,          // URL or null
     *                         'save_file',   // Mode: 'save_file'|'download'|'save_url'
     *                         'background_music.mp3', // Optional - auto-generated if null
     *                         'r2',
     *                         'name of file',
     *                         $user_id,
     *                         $mimeType      // Optional MIME type
     * );
     */
    public static function addMedia(
        string $mediaCollectionKey,
        string $mediaType,
        string $mediaSource,
        string $mediaProvider,
        ?string $fileContent = null,
        ?string $url = null,
        ?string $mode = 'save_file',
        ?string $fileName = null,
        string $disk = 'r2',
        ?string $name = null,
        ?int $userId = null,
        ?string $mimeType = null
    ) {
        $mediaCollection = MediaCollection::where('key', $mediaCollectionKey)->first();

        if (! $mediaCollection) {
            throw new \InvalidArgumentException("Media collection with key '{$mediaCollectionKey}' not found.");
        }

        $config = config("platform.media.{$mediaCollectionKey}");

        // Smart mode detection for backward compatibility
        if ($mode === 'save_file') {
            // If mode is default but we have URL and no fileContent, switch to save_url for backward compatibility
            if (! empty($url) && empty($fileContent)) {
                $mode = 'save_url';
            }
        }

        // Validate mode parameter
        $validModes = ['save_file', 'download', 'save_url'];
        if (! in_array($mode, $validModes)) {
            throw new \InvalidArgumentException("Invalid mode '{$mode}'. Valid modes are: ".implode(', ', $validModes));
        }

        // Validate required parameters based on mode
        if ($mode === 'save_file' && empty($fileContent)) {
            throw new \InvalidArgumentException("fileContent is required when mode is 'save_file'.");
        }
        if (($mode === 'download' || $mode === 'save_url') && empty($url)) {
            throw new \InvalidArgumentException("url is required when mode is '{$mode}'.");
        }

        // Auto-generate fileName if not provided
        if (empty($fileName)) {
            if (! empty($url)) {
                // Extract filename from URL
                $pathInfo = pathinfo(parse_url($url, PHP_URL_PATH));
                $fileName = $pathInfo['basename'] ?? null;

                // If no filename in URL, generate one
                if (empty($fileName) || $fileName === '/') {
                    $extension = ! empty($config['extension']) ? $config['extension'] : 'file';
                    $fileName = $mediaType.'_'.epoch_now_timestamp().'.'.$extension;
                }
            } else {
                // Generate filename for file-based media
                $extension = ! empty($config['extension']) ? $config['extension'] : 'file';
                $fileName = $mediaType.'_'.epoch_now_timestamp().'.'.$extension;
            }
        }

        // Handle different modes
        if ($mode === 'save_url') {
            // URL-based media handling - just save URL reference
            $adjustedFileName = $config['prefix'].epoch_now_timestamp().'-'.$fileName;

            // Auto-detect MIME type from URL if not provided
            if (empty($mimeType)) {
                $fileDetails = self::getFileDetailsbyUrl($url);
                $mimeType = $fileDetails->mimetype;

                // Fallback to config mime if detection fails
                if (empty($mimeType) || $mimeType === 'application/octet-stream') {
                    $mimeType = $config['mime'];
                }
            }

            $media = new Media([
                'uuid' => Str::uuid(),
                'media_collection_id' => $mediaCollection->id,
                'user_id' => $userId,
                'media_type' => $mediaType,
                'media_source' => $mediaSource,
                'media_provider' => $mediaProvider,
                'mime_type' => $mimeType,
                'file_name' => $adjustedFileName,
                'file_path' => $config['location'], // Keep for consistency
                'disk' => $disk,
                'name' => $name,
                'uploaded_url' => $url,
            ]);

            $media->save();

            return $media;
        }

        // For 'save_file' and 'download' modes, we need to store file content
        if ($mode === 'download') {
            // Download content from URL
            $fileContent = @file_get_contents($url);
            if ($fileContent === false) {
                throw new \RuntimeException("Failed to download file from URL: {$url}");
            }
        }

        // File-based media handling (for both 'save_file' and 'download' modes)
        if (empty($fileContent)) {
            throw new \InvalidArgumentException('fileContent is required for file storage.');
        }

        // Adjust fileName with prefix, postfix, and ensure extension
        $adjustedFileName = $config['prefix'].epoch_now_timestamp().'-'.$fileName;

        // Construct file path
        $filePath = $config['location'];

        // Store the file
        $stored = Storage::disk($disk)->put($filePath.$adjustedFileName, $fileContent);

        if (! $stored) {
            throw new \RuntimeException("Failed to store file: {$filePath}");
        }

        // Get filetype
        $detectedMimeType = null;
        $tempFile = tempnam(sys_get_temp_dir(), 'mime_');
        try {
            file_put_contents($tempFile, $fileContent);
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $detectedMimeType = finfo_file($finfo, $tempFile);
            finfo_close($finfo);
        } finally {
            // This ensures the file is deleted even if an exception occurs
            if (file_exists($tempFile)) {
                unlink($tempFile);
            }
        }
        if (is_empty($detectedMimeType)) {
            $detectedMimeType = $config['mime'];
        }

        $media = new Media([
            'uuid' => Str::uuid(),
            'media_collection_id' => $mediaCollection->id,
            'user_id' => $userId,
            'media_type' => $mediaType,
            'media_source' => $mediaSource,
            'media_provider' => $mediaProvider,
            'mime_type' => $detectedMimeType,
            'file_name' => $adjustedFileName,
            'file_path' => $filePath,
            'disk' => $disk,
            'name' => $name,
            'uploaded_url' => null,
        ]);

        $media->save();

        return $media;
    }

    private static function ensureUniqueFileName($fileName, $disk, $location)
    {
        $uniqueFileName = $fileName;
        $counter = 1;

        while (Storage::disk($disk)->exists($location.$uniqueFileName)) {
            $info = pathinfo($fileName);
            $uniqueFileName = $info['filename'].'-'.$counter.'.'.$info['extension'];
            $counter++;
        }

        return $uniqueFileName;
    }

    public static function getMediaByUuid($uuid)
    {
        return Media::where('uuid', $uuid)->first();
    }

    public static function getMediaByUuids(array $uuids)
    {
        return Media::whereIn('uuid', $uuids)->get();
    }

    public static function getMediasByCollectionKey($key)
    {
        $collection = MediaCollection::where('key', $key)->first();

        if (! $collection) {
            return collect();
        }

        return $collection->media;
    }

    public static function getCollectionKeyByOwnerMediaType($owner_type, $media_type)
    {
        $mediaConfig = config('platform.media');

        foreach ($mediaConfig as $key => $item) {
            if ($item['owner_type'] == $owner_type && $item['media_type'] == $media_type) {
                return $key;
            }
        }
    }

    /**
     * Get file details from a URL including filename, extension and MIME type
     *
     * @param  string  $url  The URL of the file
     * @return object Object containing filename, extension and MIME type
     */
    public static function getFileDetailsbyUrl($url)
    {
        // Create an empty result object
        $result = new \stdClass;

        // Parse the URL to extract the filename
        $pathInfo = pathinfo(parse_url($url, PHP_URL_PATH));

        // Set the filename and extension
        $result->filename = $pathInfo['filename'] ?? '';
        $result->extension = $pathInfo['extension'] ?? '';

        // Initialize the MIME type as unknown
        $result->mimetype = 'application/octet-stream';

        // Try to get the real MIME type using fileinfo
        try {
            // Create a temporary file
            $tempFile = tempnam(sys_get_temp_dir(), 'file_');

            // Get the file content from URL
            $fileContent = @file_get_contents($url);

            if ($fileContent !== false) {
                // Write the content to the temporary file
                file_put_contents($tempFile, $fileContent);

                // Create a finfo object
                $finfo = new \finfo(FILEINFO_MIME_TYPE);

                // Get the MIME type
                $result->mimetype = $finfo->file($tempFile);

                // Clean up the temporary file
                @unlink($tempFile);
            }
        } catch (\Exception $e) {
            // If there's an error, try to determine MIME type from extension
            $commonMimeTypes = [
                // Images
                'jpg' => 'image/jpeg',
                'jpeg' => 'image/jpeg',
                'png' => 'image/png',
                'gif' => 'image/gif',
                'webp' => 'image/webp',
                'svg' => 'image/svg+xml',

                // Audio
                'mp3' => 'audio/mpeg',
                'wav' => 'audio/wav',
                'ogg' => 'audio/ogg',
                'm4a' => 'audio/mp4',
                'flac' => 'audio/flac',

                // Video
                'mp4' => 'video/mp4',
                'webm' => 'video/webm',
                'avi' => 'video/x-msvideo',
                'mov' => 'video/quicktime',
                'mkv' => 'video/x-matroska',
            ];

            if (! empty($result->extension) && isset($commonMimeTypes[strtolower($result->extension)])) {
                $result->mimetype = $commonMimeTypes[strtolower($result->extension)];
            }
        }

        return $result;
    }
}
