<?php

namespace App\Services;

use App\Models\AppBannerSetting;
use App\Models\Image;
use App\Models\ThumbnailSetting;
use App\Services\Image\ImageService;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Spatie\Image\Image as SpatieImage;
use Spatie\Image\Manipulations;
use Illuminate\Support\Facades\Storage;

/**
 * @phpstan-import-type Format from ImageService
 * @phpstan-import-type ThumbSize from ImageService
 */
class UploadedImageService
{
    protected ?UploadedFile $imageFile = null;
    protected ?string $b64img = '';

    public function __construct(
        protected ImageService $imageService,
    ) {
    }

    public function setFile(UploadedFile $imageFile)
    {
        $this->imageFile = $imageFile;
        $this->imageService->setName(Str::random());

        if ($this->shouldPreserveSourcePixels()) {
            $this->imageService->setExtension(strtolower($imageFile->extension()));
        }

        return $this;
    }

    public function imageService()
    {
        return $this->imageService;
    }

    public function setKey(String $key = null)
    {
        $this->imageService->setKey($key);

        return $this;
    }

    public function setRelatedModel(Model $model)
    {
        $this->imageService->setRelatedModel($model);

        return $this;
    }

    public function storeRelationship()
    {
        $this->imageService->store();

        return $this;
    }

    public function replaceFirst(): static
    {
        /** @var Image newImage */
        /** @var Image oldImage */
        [$newImage, $oldImage] = $this->imageService->replaceFirst();

        if (!\is_null($oldImage)) {
            $thumbnailSettings = $this->imageService->getThumbnailSettings();
            $fileSizes = $thumbnailSettings
                ->map(fn (ThumbnailSetting $settings) => $settings->size)
                ->add('');

            $this->imageService->setImageModel($oldImage);

            foreach ($fileSizes as $size) {
                $filePath = $this->publicPath(
                    $this->imageService->getRelativePath($size)
                );

                if (Storage::exists($filePath)) {
                    Storage::delete($filePath);
                }
            }
        }

        return $this;
    }

    /**
     * @param ThumbSize $size
     * @return string
     */
    public function getFilename(string $size = '')
    {
        return $this->imageService->getFilename($size);
    }

    public function moveToStorage()
    {
        $settings = $this->imageService->getImageSettings();
        $fullImagePath = $this->getFullStoragePath();

        $this->ensureDirectoryExists($settings->upload_to);

        if ($this->shouldPreserveSourcePixels()) {
            Storage::put(
                $this->publicPath($settings->upload_to, $this->getFilename()),
                file_get_contents($this->imageFile->getPathname())
            );

            return $this;
        }

        $manipulator = $this->makeManipulator();

        $manipulator
            ->format($this->imageService->getExtension())
            ->save($fullImagePath);

        return $this;
    }

    public function move()
    {
        return $this->moveToStorage();
    }

    /**
     * @param Format $format
     */
    public function createThumbnails(string $key = null, string $format = 'webp')
    {
        Log::info($key);
        $imageSettings = $this->imageService()->getImageSettings();
        $this->imageService->setKey($key);
        $thumbnailSettings = $this->imageService->getThumbnailSettings($key);

        if ($this->shouldPreserveSourcePixels()) {
            /** @var ThumbnailSetting $settings */
            foreach ($thumbnailSettings as $settings) {
                Storage::put(
                    $this->publicPath($imageSettings->upload_to, $this->getFilename($settings->size)),
                    file_get_contents($this->imageFile->getPathname())
                );
            }

            return $this;
        }

        $manipulator = $this->makeManipulator();

        /** @var ThumbnailSetting $settings */
        foreach ($thumbnailSettings as $settings) {
            $fullImagePath = $this->getFullStoragePath($settings->size);
            $width = $settings->width;
            $height = $settings->height;

            if ($settings->cropped) {
                $manipulator->crop(Manipulations::CROP_CENTER, $width, $height);
            } else {
                $manipulator->width($width)->height($height);
            }

            $this->ensureDirectoryExists($imageSettings->upload_to);

            $manipulator->format($format)->save($fullImagePath);
        }

        return $this;
    }

    /**
     * @param ThumbSize $size
     */
    public function getFullStoragePath(string $size = '')
    {
        $settings = $this->imageService->getImageSettings();
        $fullImagePath = $this->basePath(
            $settings->upload_to,
            $this->getFilename($size)
        );

        return $fullImagePath;
    }

    /**
     * @param ThumbSize $size
     */
    public function getUrl(string $size = '')
    {
        return $this->imageService->getUrl($size);
    }

    protected function makeManipulator()
    {
        return SpatieImage::load($this->imageFile->getPathname());
    }

    public function basePath(string ...$path)
    {
        $strPath = \join('/', $path);

        return Storage::path($this->publicPath($strPath));
    }

    protected function ensureDirectoryExists(string $dir)
    {
        $path = $this->publicPath($dir);

        if (!Storage::exists($path)) {
            Storage::makeDirectory($path);
        }
    }

    protected function publicPath(string ...$path)
    {
        $strPath = \join('/', $path);

        return "public/{$strPath}";
    }

    public function setImageBase64(string $imageBase64)
    {

        $this->b64img = $imageBase64;
        $this->imageService->setName(Str::random());
    }

    protected function shouldPreserveSourcePixels(): bool
    {
        if (!$this->imageFile) {
            return false;
        }

        $key = $this->imageService->getKey();
        $extension = strtolower($this->imageFile->extension());

        return \in_array($key, [
            AppBannerSetting::IMAGE_KEY_APP_STORE,
            AppBannerSetting::IMAGE_KEY_PLAY_STORE,
        ], true) && \in_array($extension, ['png', 'webp'], true);
    }

    public function base64UploadTo(String $size)
    {
        $manipulator = SpatieImage::load($this->b64img);

        $settings = $this->imageService->getImageSettings();

        $fullImagePath = $this->getFullStoragePath($size);

        $this->ensureDirectoryExists($settings->upload_to);

        $manipulator
            ->format($this->imageService->getExtension())
            ->save($fullImagePath);

        return $this;
    }
}
