<?php

namespace FluxCms\Core\Services;

use FluxCms\Core\Models\CmsMedia;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Drivers\Gd\Driver as GdDriver;
use Intervention\Image\ImageManager;

class MediaConversionService
{
    protected ImageManager $manager;

    protected string $disk;

    protected string $conversionsPath;

    public function __construct()
    {
        $this->manager = new ImageManager(new GdDriver);
        $this->disk = config('flux-cms.media.disk', 'public');
        $this->conversionsPath = config('flux-cms.media.conversions_path', 'cms/media/conversions');
    }

    /**
     * Generate a specific conversion for a media item.
     *
     * @return string|null The path to the conversion file, or null on failure.
     */
    public function convert(CmsMedia $media, string $profile): ?string
    {
        if (! $media->isImage()) {
            return null;
        }

        $profileConfig = config("flux-cms.media.profiles.{$profile}");
        if (! $profileConfig) {
            return null;
        }

        $originalPath = Storage::disk($this->disk)->path($media->path);
        if (! file_exists($originalPath)) {
            return null;
        }

        $extension = $this->isSvg($media) ? 'svg' : ($profileConfig['format'] ?? 'webp');

        if ($this->isSvg($media)) {
            return $media->path;
        }

        $conversionFilename = pathinfo($media->filename, PATHINFO_FILENAME)
            .'-'.$profile
            .'.'.$extension;

        $conversionPath = $this->conversionsPath.'/'.$profile.'/'.$conversionFilename;
        $absolutePath = Storage::disk($this->disk)->path($conversionPath);

        $dir = dirname($absolutePath);
        if (! is_dir($dir)) {
            mkdir($dir, 0755, true);
        }

        try {
            $image = $this->manager->read($originalPath);

            $width = $profileConfig['width'];
            $height = $profileConfig['height'];
            $fit = $profileConfig['fit'] ?? 'cover';
            $quality = $profileConfig['quality'] ?? 85;

            if ($fit === 'cover') {
                $image->cover($width, $height);
            } else {
                $image->scaleDown($width, $height);
            }

            $encoded = match ($extension) {
                'webp' => $image->toWebp($quality),
                'png' => $image->toPng(),
                'gif' => $image->toGif(),
                default => $image->toJpeg($quality),
            };

            $encoded->save($absolutePath);

            $conversions = $media->conversions ?? [];
            $conversions[$profile] = $conversionPath;
            $media->update(['conversions' => $conversions]);

            return $conversionPath;
        } catch (\Throwable $e) {
            report($e);

            return null;
        }
    }

    /**
     * Generate the thumbnail conversion used in the admin grid.
     */
    public function generateThumbnail(CmsMedia $media): ?string
    {
        return $this->convert($media, 'thumb');
    }

    /**
     * Generate all configured profile conversions.
     *
     * @return array<string, string|null>
     */
    public function generateAllConversions(CmsMedia $media): array
    {
        $profiles = array_keys(config('flux-cms.media.profiles', []));
        $results = [];

        foreach ($profiles as $profile) {
            $results[$profile] = $this->convert($media, $profile);
        }

        return $results;
    }

    /**
     * Delete all conversion files for a media item.
     */
    public function deleteConversions(CmsMedia $media): void
    {
        $conversions = $media->conversions ?? [];

        foreach ($conversions as $path) {
            if (Storage::disk($this->disk)->exists($path)) {
                Storage::disk($this->disk)->delete($path);
            }
        }

        $media->update(['conversions' => []]);
    }

    /**
     * Delete the original file and all conversions.
     */
    public function deleteAll(CmsMedia $media): void
    {
        $this->deleteConversions($media);

        if (Storage::disk($this->disk)->exists($media->path)) {
            Storage::disk($this->disk)->delete($media->path);
        }
    }

    /**
     * Store an uploaded file and create the CmsMedia record.
     */
    public function storeUpload(
        \Illuminate\Http\UploadedFile $file,
        ?string $collection = null,
        bool $generateThumbnail = true,
    ): CmsMedia {
        $originalsPath = config('flux-cms.media.originals_path', 'cms/media/originals');
        $path = $file->store($originalsPath, $this->disk);

        $type = $this->detectType($file);
        $width = null;
        $height = null;

        if ($type === 'image' && ! $this->isSvgFile($file)) {
            try {
                $dimensions = getimagesize($file->getRealPath());
                if ($dimensions) {
                    $width = $dimensions[0];
                    $height = $dimensions[1];
                }
            } catch (\Throwable) {
            }
        }

        $media = CmsMedia::create([
            'filename' => $file->getClientOriginalName(),
            'disk' => $this->disk,
            'path' => $path,
            'type' => $type,
            'mime_type' => $file->getMimeType(),
            'file_size' => $file->getSize(),
            'original_width' => $width,
            'original_height' => $height,
            'collection' => $collection,
            'conversions' => [],
        ]);

        if ($generateThumbnail && $media->isImage() && ! $this->isSvg($media)) {
            $this->generateThumbnail($media);
        }

        return $media;
    }

    protected function detectType(\Illuminate\Http\UploadedFile $file): string
    {
        $mime = $file->getMimeType();

        if (str_starts_with($mime, 'image/')) {
            return 'image';
        }

        if ($mime === 'application/pdf') {
            return 'pdf';
        }

        return 'document';
    }

    protected function isSvg(CmsMedia $media): bool
    {
        return $media->mime_type === 'image/svg+xml'
            || str_ends_with(strtolower($media->filename), '.svg');
    }

    protected function isSvgFile(\Illuminate\Http\UploadedFile $file): bool
    {
        return $file->getMimeType() === 'image/svg+xml'
            || str_ends_with(strtolower($file->getClientOriginalName()), '.svg');
    }
}
