<?php

namespace Daylight\Core\Modules\MediaLibrary\Services;

use Daylight\Core\Modules\MediaLibrary\Jobs\GenerateMediaConversionJob;
use Daylight\Core\Modules\MediaLibrary\Models\Directory;
use Daylight\Core\Modules\MediaLibrary\Models\Media;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Laravel\Facades\Image;

class MediaUploadService
{
    public function upload(UploadedFile $file, ?int $directoryId = null): Media
    {
        try {
            // Validate file
            $this->validateFile($file);

            // Generate unique filename
            $fileName = $this->generateUniqueFileName($file->getClientOriginalName(), $directoryId);

            // Get file extension
            $extension = $file->getClientOriginalExtension();

            // Determine storage path using directory slug
            $path = $this->generateStoragePath($fileName, $directoryId);

            $disk = config('media.disk', 'public');

            // For images, fix rotation and strip EXIF before storing
            if ($this->isImage($file->getMimeType())) {
                $image = Image::read($file->getRealPath());

                // Auto-orient based on EXIF and strip metadata
                $image->orient();

                // Save to storage with public visibility
                $encodedImage = $image->encode();
                Storage::disk($disk)->put($path, (string) $encodedImage, 'public');

                // Get dimensions
                $width = $image->width();
                $height = $image->height();
            } else {
                // Store non-image files directly with public visibility
                Storage::disk($disk)->put($path, file_get_contents($file->getRealPath()), 'public');
                $width = null;
                $height = null;
            }

            // Create media record
            $media = Media::create([
                'directory_id' => $directoryId,
                'name' => pathinfo($fileName, PATHINFO_FILENAME),
                'size' => $file->getSize(),
                'handle' => $path,
                'path' => $path,
                'disk' => $disk,
                'extension' => $extension,
                'type' => $file->getMimeType(),
                'hash' => md5_file($file->getRealPath()),
            ]);

            // Dispatch conversion jobs for images
            if ($media->isImage()) {
                $conversions = config('media.conversions', []);
                foreach ($conversions as $conversionName => $config) {
                    GenerateMediaConversionJob::dispatch($media, $conversionName, $config);
                }
            }

            return $media;
        } catch (\Exception $e) {
            \Log::error('Media upload failed', [
                'file' => $file->getClientOriginalName(),
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);
            throw $e;
        }
    }

    public function generateUniqueFileName(string $originalName, ?int $directoryId = null): string
    {
        // Sanitize filename
        $pathInfo = pathinfo($originalName);
        $basename = $pathInfo['filename'];
        $extension = $pathInfo['extension'] ?? '';

        // Remove special characters
        $basename = preg_replace('/[^a-zA-Z0-9-_]/', '-', $basename);
        $basename = preg_replace('/-+/', '-', $basename);
        $basename = trim($basename, '-');

        $fileName = $extension ? "{$basename}.{$extension}" : $basename;

        // Check if filename exists in database for this directory
        $query = Media::query()->where('directory_id', $directoryId);

        $existingNames = $query
            ->where('name', 'like', $basename . '%')
            ->pluck('name')
            ->toArray();

        // Also check for conversion names
        $existingConversionNames = $query
            ->with('conversions')
            ->get()
            ->flatMap(fn ($media) => $media->conversions->pluck('file_name'))
            ->toArray();

        $allExistingNames = array_merge($existingNames, $existingConversionNames);

        if (! in_array($basename, $allExistingNames) && ! in_array($fileName, $allExistingNames)) {
            return $fileName;
        }

        // Find next available number
        $counter = 2;
        while (true) {
            $testBasename = "{$basename}-{$counter}";
            $testFileName = $extension ? "{$testBasename}.{$extension}" : $testBasename;

            if (! in_array($testBasename, $allExistingNames) && ! in_array($testFileName, $allExistingNames)) {
                return $testFileName;
            }

            $counter++;
        }
    }

    protected function generateStoragePath(string $fileName, ?int $directoryId = null): string
    {
        if ($directoryId) {
            $directory = Directory::query()->find($directoryId);

            if ($directory) {
                // Build path using ancestor slugs for full hierarchy
                $slugs = $directory->ancestors()
                    ->get()
                    ->pluck('slug')
                    ->reverse()
                    ->push($directory->slug)
                    ->implode('/');

                return "media/{$slugs}/{$fileName}";
            }
        }

        return "media/{$fileName}";
    }

    public static function generateConversionPath(Media $media, string $conversionName, string $format): string
    {
        // Get the directory path of the original file
        $pathInfo = pathinfo($media->path);
        $directory = $pathInfo['dirname'];
        $basename = $pathInfo['filename'];

        // Create a conversions subdirectory in the same directory as the original
        // Example: media/products/electronics/conversions/
        $conversionsDir = $directory !== '.' ? "{$directory}/conversions" : 'conversions';

        // Conversion filename includes the original basename
        $conversionFileName = "{$basename}-{$conversionName}.{$format}";

        return "{$conversionsDir}/{$conversionFileName}";
    }

    protected function validateFile(UploadedFile $file): void
    {
        $maxSize = config('media.max_size', 32768) * 1024; // Convert KB to bytes
        $acceptedMimes = config('media.accepted_mimes', []);

        if (empty($acceptedMimes)) {
            throw new \Exception('No accepted MIME types configured. Please check config/media.php');
        }

        if ($file->getSize() > $maxSize) {
            throw new \Exception('File size exceeds maximum allowed size.');
        }

        $mimeType = $file->getMimeType();

        if (! in_array($mimeType, $acceptedMimes, true)) {
            throw new \Exception("File type '{$mimeType}' not accepted.");
        }
    }

    protected function isImage(string $mimeType): bool
    {
        return str_starts_with($mimeType, 'image/') && $mimeType !== 'image/svg+xml';
    }
}
