<?php

namespace App\Services;

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Exception;
use Intervention\Image\Laravel\Facades\Image;

class MediaService
{
    /**
     * Store a media file with advanced features
     *
     * @param UploadedFile $file
     * @param string $type
     * @param string|null $storageDriver
     * @param array $options
     * @return array
     */
    public function store(UploadedFile $file, string $type = 'image', ?string $storageDriver = null, array $options = []): array
    {
        try {
            // Check if this is a test mode
            $isTestMode = $options['test_mode'] ?? false;
            
            if (!$isTestMode) {
                // Validate file
                $this->validateFile($file, $type);
            }
            
            // Determine storage driver
            $driver = $this->determineStorageDriver($type, $storageDriver);
            
            // Generate file path
            $filePath = $this->generateFilePath($file, $type);
            
            // Process file if needed (skip for test mode)
            $processedFile = $isTestMode ? $file : $this->processFile($file, $type, $options);
            
            // Store file with fallback to configured drivers if primary fails (e.g., missing S3 adapter)
            $usedDriver = $driver;
            $stored = false;
            $lastError = null;
            
            try {
                $stored = $this->storeFile($processedFile, $filePath, $driver, $options);
            } catch (Exception $e) {
                $lastError = $e->getMessage();
                $stored = false;
            }
            
            if (!$stored) {
                $fallbackDrivers = config("media.drivers.{$driver}.fallback_drivers", []);
                foreach ($fallbackDrivers as $fallbackDriver) {
                    try {
                        if ($this->storeFile($processedFile, $filePath, $fallbackDriver, $options)) {
                            $usedDriver = $fallbackDriver;
                            $stored = true;
                            break;
                        }
                    } catch (Exception $e) {
                        $lastError = $e->getMessage();
                        $stored = false;
                        continue;
                    }
                }
            }
            
            if (!$stored) {
                $message = "Failed to store file on driver: {$driver}";
                if ($lastError) {
                    $message .= " ({$lastError})";
                }
                throw new Exception($message);
            }
            
            // Generate thumbnails if enabled (skip for test mode)
            $thumbnails = $isTestMode ? [] : $this->generateThumbnails($processedFile, $filePath, $usedDriver, $type);
            
            return [
                'success' => true,
                'path' => $filePath,
                'driver' => $usedDriver,
                'thumbnails' => $thumbnails,
                'url' => $this->getUrl($filePath, $type, $usedDriver),
                'size' => $file->getSize(),
                'mime_type' => $file->getMimeType(),
                'original_name' => $file->getClientOriginalName(),
            ];
            
        } catch (Exception $e) {
            Log::error('Media storage failed', [
                'type' => $type,
                'driver' => $storageDriver,
                'error' => $e->getMessage(),
                'file' => $file->getClientOriginalName(),
            ]);
            
            return [
                'success' => false,
                'error' => $e->getMessage(),
            ];
        }
    }
    
    /**
     * Get media URL with fallback support
     *
     * @param string $path
     * @param string $type
     * @param string|null $storageDriver
     * @param array $options
     * @return string
     */
    public function getUrl(string $path, string $type = 'image', ?string $storageDriver = null, array $options = []): string
    {
        // Handle empty paths
        if (empty($path)) {
            return $this->getPlaceholderUrl($type);
        }
        
        // Determine storage driver
        $driver = $storageDriver ?: config('media.default_driver', 'local');
        
        // Try primary driver with alternative paths
        $url = $this->tryGetUrlWithAlternatives($path, $driver, $type, $options);
        if ($url) {
            return $url;
        }
        
        // Try fallback drivers
        $fallbackDrivers = config("media.drivers.{$driver}.fallback_drivers", []);
        foreach ($fallbackDrivers as $fallbackDriver) {
            $url = $this->tryGetUrlWithAlternatives($path, $fallbackDriver, $type, $options);
            if ($url) {
                return $url;
            }
        }
        
        // Return placeholder if file not found
        return $this->getPlaceholderUrl($type);
    }

    /**
     * Try to get URL with alternative paths
     *
     * @param string $path
     * @param string $driver
     * @param string $type
     * @param array $options
     * @return string|null
     */
    private function tryGetUrlWithAlternatives(string $path, string $driver, string $type, array $options): ?string
    {
        // Try original path
        $url = $this->generateUrl($path, $driver, $options);
        if ($url && $this->fileExists($path, $driver)) {
            return $url;
        }
        
        // Try alternative paths
        $alternativePaths = $this->generateAlternativePaths($path, $type);
        foreach ($alternativePaths as $altPath) {
            $url = $this->generateUrl($altPath, $driver, $options);
            if ($url && $this->fileExists($altPath, $driver)) {
                return $url;
            }
        }
        
        return null;
    }

    /**
     * Generate alternative paths to check
     *
     * @param string $path
     * @param string $type
     * @return array
     */
    private function generateAlternativePaths(string $path, string $type): array
    {
        $alternatives = [];
        
        // Remove leading slashes and normalize
        $cleanPath = ltrim($path, '/');
        
        // If path starts with known prefixes, also try without them
        if (str_starts_with($cleanPath, 'public/')) {
            $alternatives[] = substr($cleanPath, strlen('public/'));
        }
        if (str_starts_with($cleanPath, 'storage/')) {
            $alternatives[] = substr($cleanPath, strlen('storage/'));
        }
        
        // Add common variations
        $alternatives[] = $cleanPath;
        $alternatives[] = "uploads/{$cleanPath}";
        $alternatives[] = "media/{$cleanPath}";
        $alternatives[] = "public/{$cleanPath}";
        
        // For specific types, add type-specific paths
        if ($type === 'avatar') {
            $alternatives[] = "avatars/{$cleanPath}";
            $alternatives[] = "users/avatars/{$cleanPath}";
        }
        
        if ($type === 'cover') {
            $alternatives[] = "covers/{$cleanPath}";
            $alternatives[] = "users/covers/{$cleanPath}";
        }
        
        return array_unique($alternatives);
    }
    
    /**
     * Delete media file from all storage drivers
     *
     * @param string $path
     * @param string|null $storageDriver
     * @return bool
     */
    public function delete(string $path, ?string $storageDriver = null): bool
    {
        $driver = $storageDriver ?: config('media.default_driver', 'local');
        $drivers = array_merge([$driver], config("media.drivers.{$driver}.fallback_drivers", []));
        
        $deleted = false;
        foreach ($drivers as $driverName) {
            try {
                $storage = Storage::disk($driverName);
                if ($storage->exists($path)) {
                    $storage->delete($path);
                    $deleted = true;
                }
                
                // Delete thumbnails if they exist
                $this->deleteThumbnails($path, $driverName);
                
            } catch (Exception $e) {
                Log::warning("Failed to delete file from driver: {$driverName}", [
                    'path' => $path,
                    'error' => $e->getMessage(),
                ]);
            }
        }
        
        return $deleted;
    }
    
    /**
     * Validate uploaded file
     *
     * @param UploadedFile $file
     * @param string $type
     * @throws Exception
     */
    private function validateFile(UploadedFile $file, string $type): void
    {
        // Get type-specific settings from database
        $maxSize = $this->getTypeMaxSize($type);
        $allowedExtensions = $this->getTypeAllowedExtensions($type);
        $maxDimensions = $this->getTypeMaxDimensions($type);
        
        // Check file size
        if ($file->getSize() > $maxSize) {
            throw new Exception("File size exceeds maximum allowed size for {$type}");
        }
        
        // Check file extension
        $extension = strtolower($file->getClientOriginalExtension());
        if (!in_array($extension, $allowedExtensions)) {
            throw new Exception("File extension '{$extension}' not allowed for {$type}");
        }
        
        // Check MIME type from settings
        $mimeType = $file->getMimeType();
        $allowedMimeTypes = setting('media_allowed_mime_types', 'image/jpeg,image/png,image/gif,image/webp,video/mp4,application/pdf');
        $allowedMimeTypesArray = array_map('trim', explode(',', $allowedMimeTypes));
        if (!in_array($mimeType, $allowedMimeTypesArray)) {
            throw new Exception("MIME type '{$mimeType}' not allowed");
        }
        
        // Check for blocked extensions from settings
        $blockedExtensions = setting('media_blocked_extensions', 'php,php3,php4,php5,phtml,pl,py,jsp,asp,sh,cgi');
        $blockedExtensionsArray = array_map('trim', explode(',', $blockedExtensions));
        if (in_array($extension, $blockedExtensionsArray)) {
            throw new Exception("File extension '{$extension}' is blocked for security reasons");
        }
    }

    /**
     * Get maximum file size for media type
     */
    private function getTypeMaxSize(string $type): int
    {
        $sizeMap = [
            'avatar' => setting('avatar_max_size', 2048),
            'cover' => setting('cover_max_size', 5120),
            'image' => setting('image_max_size', 10240),
            'video' => setting('video_max_size', 102400),
            'document' => setting('document_max_size', 51200),
        ];

        return ($sizeMap[$type] ?? 10240) * 1024; // Convert KB to bytes
    }

    /**
     * Get allowed extensions for media type
     */
    private function getTypeAllowedExtensions(string $type): array
    {
        $extensionMap = [
            'avatar' => setting('avatar_allowed_extensions', 'jpg,jpeg,png,gif'),
            'cover' => setting('cover_allowed_extensions', 'jpg,jpeg,png'),
            'image' => setting('image_allowed_extensions', 'jpg,jpeg,png,gif,webp'),
            'video' => setting('video_allowed_extensions', 'mp4,avi,mov,wmv,flv'),
            'document' => setting('document_allowed_extensions', 'pdf,doc,docx,txt,rtf'),
        ];

        $extensions = $extensionMap[$type] ?? 'jpg,jpeg,png';
        return array_map('trim', explode(',', $extensions));
    }

    /**
     * Get maximum dimensions for media type
     */
    private function getTypeMaxDimensions(string $type): array
    {
        $dimensionMap = [
            'avatar' => [
                'width' => setting('avatar_max_width', 500),
                'height' => setting('avatar_max_height', 500),
            ],
            'cover' => [
                'width' => setting('cover_max_width', 1920),
                'height' => setting('cover_max_height', 1080),
            ],
            'image' => [
                'width' => setting('image_max_width', 4096),
                'height' => setting('image_max_height', 4096),
            ],
        ];

        return $dimensionMap[$type] ?? ['width' => 4096, 'height' => 4096];
    }
    
    /**
     * Determine storage driver based on type and configuration
     *
     * @param string $type
     * @param string|null $storageDriver
     * @return string
     */
    private function determineStorageDriver(string $type, ?string $storageDriver): string
    {
        if ($storageDriver) {
            return $storageDriver;
        }
        
        $typeConfig = config("media.types.{$type}");
        if ($typeConfig && $typeConfig['preferred_driver']) {
            return $typeConfig['preferred_driver'];
        }
        
        return config('media.default_driver', 'local');
    }
    
    /**
     * Generate file path
     *
     * @param UploadedFile $file
     * @param string $type
     * @return string
     */
    private function generateFilePath(UploadedFile $file, string $type): string
    {
        $typeConfig = config("media.types.{$type}");
        $basePath = $typeConfig['path'] ?? 'uploads';
        
        $originalName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
        $slug = Str::slug($originalName);
        $extension = $file->getClientOriginalExtension();
        $filename = substr($slug, 0, 150) . '-' . time() . '.' . $extension;
        
        return "{$basePath}/" . date('Y-m') . "/{$filename}";
    }
    
    /**
     * Process file (resize, compress, etc.)
     *
     * @param UploadedFile $file
     * @param string $type
     * @param array $options
     * @return UploadedFile
     */
    private function processFile(UploadedFile $file, string $type, array $options): UploadedFile
    {
        $typeConfig = config("media.types.{$type}");
        
        // Only process images
        if (!in_array($file->getMimeType(), ['image/jpeg', 'image/png', 'image/gif', 'image/webp'])) {
            return $file;
        }
        
        // Check if image processing is enabled from settings
        if (!setting('media_image_processing', true)) {
            return $file;
        }
        
        try {
            $image = Image::read($file);
            
            // Resize if needed
            if (setting('media_resize_large_images', true)) {
                $maxDimensions = $this->getTypeMaxDimensions($type);
                $maxWidth = $maxDimensions['width'];
                $maxHeight = $maxDimensions['height'];
                
                if ($image->width() > $maxWidth || $image->height() > $maxHeight) {
                    $image->scaleDown($maxWidth, $maxHeight);
                }
            }
            
            // Set quality from settings
            $quality = $options['quality'] ?? setting('media_image_quality', 85);
            
            // Create temporary file
            $tempPath = tempnam(sys_get_temp_dir(), 'media_');
            $image->save($tempPath, ['quality' => $quality]);
            
            // Create new UploadedFile instance
            return new UploadedFile(
                $tempPath,
                $file->getClientOriginalName(),
                $file->getMimeType(),
                null,
                true
            );
            
        } catch (Exception $e) {
            Log::warning('Image processing failed', [
                'file' => $file->getClientOriginalName(),
                'error' => $e->getMessage(),
            ]);
            
            return $file;
        }
    }
    
    /**
     * Store file on specified driver
     *
     * @param UploadedFile $file
     * @param string $path
     * @param string $driver
     * @param array $options
     * @return bool
     */
    private function storeFile(UploadedFile $file, string $path, string $driver, array $options): bool
    {
        $storage = Storage::disk($driver);
        
        return $storage->putFileAs(
            dirname($path),
            $file,
            basename($path),
            $options['visibility'] ?? 'public'
        );
    }
    
    /**
     * Generate thumbnails for images
     *
     * @param UploadedFile $file
     * @param string $path
     * @param string $driver
     * @param string $type
     * @return array
     */
    private function generateThumbnails(UploadedFile $file, string $path, string $driver, string $type): array
    {
        // Check if thumbnail generation is enabled from settings
        if (!setting('media_create_thumbnails', true) || !in_array($file->getMimeType(), ['image/jpeg', 'image/png', 'image/gif', 'image/webp'])) {
            return [];
        }
        
        $thumbnails = [];
        $storage = Storage::disk($driver);
        
        try {
            $image = Image::read($file);
            $pathInfo = pathinfo($path);
            
            // Get thumbnail sizes from config
            $thumbnailSizes = config('media.image_processing.thumbnail_sizes', [
                'small' => [150, 150],
                'medium' => [300, 300],
                'large' => [600, 600],
            ]);
            
            foreach ($thumbnailSizes as $size => $dimensions) {
                $thumbnailPath = $pathInfo['dirname'] . '/' . $pathInfo['filename'] . "_{$size}." . $pathInfo['extension'];
                
                $thumbnail = clone $image;
                $thumbnail->cover($dimensions[0], $dimensions[1]);
                
                $tempPath = tempnam(sys_get_temp_dir(), 'thumbnail_');
                $thumbnail->save($tempPath, ['quality' => setting('media_image_quality', 85)]);
                
                $storage->put($thumbnailPath, file_get_contents($tempPath), 'public');
                unlink($tempPath);
                
                $thumbnails[$size] = $thumbnailPath;
            }
            
        } catch (Exception $e) {
            Log::warning('Thumbnail generation failed', [
                'file' => $file->getClientOriginalName(),
                'error' => $e->getMessage(),
            ]);
        }
        
        return $thumbnails;
    }
    
    /**
     * Delete thumbnails
     *
     * @param string $path
     * @param string $driver
     */
    private function deleteThumbnails(string $path, string $driver): void
    {
        $storage = Storage::disk($driver);
        $imageProcessing = config('media.image_processing');
        
        if (!$imageProcessing['create_thumbnails']) {
            return;
        }
        
        $pathInfo = pathinfo($path);
        
        foreach ($imageProcessing['thumbnail_sizes'] as $size => $dimensions) {
            $thumbnailPath = $pathInfo['dirname'] . '/' . $pathInfo['filename'] . "_{$size}." . $pathInfo['extension'];
            
            if ($storage->exists($thumbnailPath)) {
                $storage->delete($thumbnailPath);
            }
        }
    }
    
    /**
     * Generate URL for file
     *
     * @param string $path
     * @param string $driver
     * @param array $options
     * @return string|null
     */
    private function generateUrl(string $path, string $driver, array $options): ?string
    {
        $driverConfig = config("media.drivers.{$driver}");
        if (!$driverConfig) {
            return null;
        }
        
        switch ($driver) {
            case 's3':
                return $this->generateS3Url($path, $driverConfig, $options);
            case 'public':
            case 'local':
                return $this->generateLocalUrl($path, $driverConfig);
            default:
                return $this->generateDefaultUrl($path, $driverConfig);
        }
    }
    
    /**
     * Generate S3 URL
     *
     * @param string $path
     * @param array $config
     * @param array $options
     * @return string
     */
    private function generateS3Url(string $path, array $config, array $options): string
    {
        $bucket = $config['bucket'] ?? '';
        $region = $config['region'] ?? '';
        $endpoint = $config['endpoint'] ?? '';
        
        if (!empty($endpoint)) {
            $baseUrl = rtrim($endpoint, '/');
            return "{$baseUrl}/{$bucket}/{$path}";
        }
        
        $baseUrl = "https://{$bucket}.s3.{$region}.amazonaws.com";
        
        if (!empty($options['cloudfront_domain'])) {
            $baseUrl = "https://{$options['cloudfront_domain']}";
        }
        
        return "{$baseUrl}/{$path}";
    }
    
    /**
     * Generate local URL
     *
     * @param string $path
     * @param array $config
     * @return string
     */
    private function generateLocalUrl(string $path, array $config): string
    {
        $baseUrl = $config['url'] ?? '';
        
        if (empty($baseUrl)) {
            // Map local 'public/*' storage paths to the public '/storage/*' URL
            if (str_starts_with($path, 'public/')) {
                $relative = substr($path, strlen('public/'));
                return asset('storage/' . ltrim($relative, '/'));
            }
            return asset($path);
        }
        
        return rtrim($baseUrl, '/') . '/' . ltrim($path, '/');
    }
    
    /**
     * Generate default URL
     *
     * @param string $path
     * @param array $config
     * @return string
     */
    private function generateDefaultUrl(string $path, array $config): string
    {
        $baseUrl = $config['url'] ?? '';
        
        if (empty($baseUrl)) {
            return asset($path);
        }
        
        return rtrim($baseUrl, '/') . '/' . ltrim($path, '/');
    }
    
    /**
     * Check if file exists on driver
     *
     * @param string $path
     * @param string $driver
     * @return bool
     */
    private function fileExists(string $path, string $driver): bool
    {
        try {
            return Storage::disk($driver)->exists($path);
        } catch (Exception $e) {
            return false;
        }
    }
    
    /**
     * Get placeholder URL
     *
     * @param string $type
     * @return string
     */
    public function getPlaceholderUrl(string $type): string
    {
        $placeholder = config("media.placeholders.{$type}", config('media.placeholders.image'));
        return asset($placeholder);
    }
} 