<?php

namespace App\Services\Backoffice;

use App\Libs\Backoffice\Exceptions\InvalidConfigurationException;
use App\Models\Backoffice\Auth\BackendUser;
use App\Models\Backoffice\MediaItem;
use Intervention\Image\Drivers\Imagick\Driver;
use Illuminate\Http\UploadedFile;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\ImageManager;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class MediaService
{
    public function getMediaListPerPage($page, $perPage, $sort, $search, $folderId = null)
    {
        Paginator::currentPageResolver(function () use ($page) {
            return $page;
        });

        $query = MediaItem::query();
        if (!empty($search)) {
            $query->where(function ($q) use ($search) {
                $q->whereRaw('MATCH(search_content) AGAINST (? IN BOOLEAN MODE)', [$search]);
            });
        }
        if (!empty($folderId)) {
            $query->where('media_folder_id', $folderId);
        }
        if (empty($sort)) {
            $rows = $query->paginate($perPage);
        } else {
            foreach ($sort as $field => $direction) {
                if (empty($direction) || !Str::is(strtolower($direction), "desc")) {
                    $orderDirection = "asc";
                } else {
                    $orderDirection = $direction;
                }
                $rows = $query->orderBy($field, strtolower($orderDirection))->paginate($perPage);
                break;
            }
        }

        return [
            "total" => $rows->total(),
            "page" => $rows->currentPage(),
            "perPage" => $perPage,
            "items" => $rows->getCollection()
        ];
    }

    /**
     * Generates the thumb image and return the image object
     *
     * @param  string  $filename
     *
     * @return Intervention\Image\Image
     * @throws InvalidConfigurationException
     */
    public function generateMediaThumb($filename)
    {
        $fileInfo = pathinfo($filename);
        $extension = $fileInfo["extension"];
        $filenameWithoutExt = $fileInfo["filename"];
        $folderDirectory = $fileInfo["dirname"];

        $pos = strrpos($filenameWithoutExt, "_");
        if ($pos === false) {
            throw new InvalidConfigurationException("Invalid size");
        }

        $originalFileName = substr($filenameWithoutExt, 0, $pos).".{$extension}";
        $filePath = 'uploads/' . $folderDirectory . '/' . $originalFileName;
        $transformationSize = substr($filenameWithoutExt, $pos + 1);
        $targetFilePath = config("path.internal.thumbs")."/{$filename}";

        $manager = new ImageManager(Driver::class);

        $img = $manager->read(Storage::disk('public')->path($filePath));

        if (is_null($img)) {
            throw new InvalidConfigurationException("Invalid image file");
        }

        $allowedSizes = config('imageResize');
        if (!isset($allowedSizes[$transformationSize])) {
            throw new InvalidConfigurationException("Invalid size");
        }

        if (!Storage::disk('public')->exists($filePath)) {
            throw new NotFoundHttpException();
        }

        $size = $allowedSizes[$transformationSize];
        $quality = 90;

        if (isset($size["quality"])) {
            $quality = $size["quality"];
        }

        $width = !empty($size["width"]) ? $size["width"] : null;
        $height = !empty($size["height"]) ? $size["height"] : null;

        if (is_null($width) && is_null($height)) {
            throw new InvalidConfigurationException("The size {$transformationSize} must have at least a width or a height");
        }

        if (!is_null($width) && !is_null($height)) {
            $img->cover($width, $height);
        } else {
            $img->scaleDown($width, $height);
        }

        $thumbsPath = config('path.internal.thumbs');
        $thumbsFolderPath = $thumbsPath . '/' . $folderDirectory;

        if (!Storage::disk('public')->exists($thumbsFolderPath)) {
            Storage::disk('public')->makeDirectory($thumbsFolderPath);
        }

        $img->save(Storage::disk('public')->path($targetFilePath), $quality);
        return $img;
    }

    /**
     * Updates media title / author / alt
     *
     * @param  int  $mediaId
     * @param  array data
     *
     * @return MediaItem
     * @throws NotFoundHttpException
     */
    public function updateMediaItem($mediaId, $data)
    {
        $mediaItem = MediaItem::find($mediaId);
        if (is_null($mediaItem)) {
            throw new NotFoundHttpException();
        }

        $mediaItem->title = $data['title'];
        $mediaItem->alt = $data['alt'];
        $mediaItem->author = $data['author'];
        $mediaItem->save();

        return $mediaItem;
    }

    /**
     * Delete the MediaItem and it's files
     *
     * @param  int  $mediaId
     *
     * @return MediaItem
     * @throws NotFoundHttpException
     */
    public function deleteMediaItem($mediaId)
    {
        $mediaItem = MediaItem::find($mediaId);

        if (is_null($mediaItem)) {
            throw new NotFoundHttpException();
        }

        // Deletes main file
        $filename = $mediaItem->filename;
        $filePath = $mediaItem->path;
        $basePath = Storage::disk($mediaItem->base_folder)->path('');

        if (Storage::disk($mediaItem->base_folder)->exists($filePath)) {
            Storage::disk($mediaItem->base_folder)->delete($filePath);
        }

        // Deletes thumbs (for images)
        if ($mediaItem->isImage() == MediaItem::MEDIA_TYPE_IMAGE) {
            $allowedSizes = config('imageResize');
            $thumbsPath = config('path.internal.thumbs');
            $withoutExt = preg_replace('/\\.[^.\\s]{3,4}$/', '', $filename);
            $ext = pathinfo($basePath . '/' . $filePath, PATHINFO_EXTENSION);
            $path = pathinfo($filePath, PATHINFO_DIRNAME);

            foreach ($allowedSizes as $size => $sizeConfig) {
                $thumbFile = "{$thumbsPath}/{$mediaItem->base_folder}/{$path}/{$withoutExt}_{$size}.{$ext}";
                echo($thumbFile);
                if (Storage::disk('public')->exists($thumbFile)) {
                    Storage::disk('public')->delete($thumbFile);
                }
            }
        }

        $mediaItem->delete();
        return $mediaItem;
    }


    /**
     * Crop an existing MediaItem and creates a new one with the cropped image
     *
     * @param  int  $mediaId
     * @param  BackendUser  $author
     * @param  array  $cropData
     *
     * @return MediaItem
     * @throws NotFoundHttpException
     */
    public function cropMediaItem($mediaId, BackendUser $author, $cropData)
    {
        $mediaItemOriginal = MediaItem::find($mediaId);

        if (is_null($mediaItemOriginal)) {
            throw new NotFoundHttpException();
        }

        $mediaFolder = $mediaItemOriginal->mediaFolder;

        $destinationPath = $mediaFolder->path;
        $cropData['left'] = (int) $cropData['left'];
        $cropData['top'] = (int) $cropData['top'];
        $cropData['width'] = (int) $cropData['width'];
        $cropData['height'] = (int) $cropData['height'];

        if (!$mediaItemOriginal->isImage()) {
            throw new NotFoundHttpException();
        }

        $filename = $mediaItemOriginal->filename;
        $mediaPath = $mediaItemOriginal->path;

        if (!Storage::disk($mediaItemOriginal->base_folder)->exists($mediaPath)) {
            throw new NotFoundHttpException();
        }

        $extension = $mediaItemOriginal->extension;
        $withoutExt = preg_replace('/\\.[^.\\s]{3,4}$/', '', $filename);

        $newFilename = Str::slug($withoutExt);
        $finalName = $newFilename.'_cropped.'.$extension;
        //Checks for colliding filenames
        $i = 1;
        while (Storage::disk($mediaItemOriginal->base_folder)->exists($destinationPath.'/'.$finalName)) {
            $finalName = $newFilename.'_cropped-'.$i.'.'.$extension;
            $i++;
            //for safety
            if ($i > 200) {
                throw new HttpException(400, 'Not possible to crop');
            }
        }

        $manager = new ImageManager(Driver::class);
        $img = $manager->read(Storage::disk($mediaItemOriginal->base_folder)->path($mediaPath));

        // crop image
        $img->crop($cropData['width'], $cropData['height'], $cropData['left'], $cropData['top']);
        //TODO S3
        $img->save(Storage::disk($mediaItemOriginal->base_folder)->path($destinationPath.'/'.$finalName));

        $mediaItem = new MediaItem([
            'filename' => $finalName,
            'original_filename' => $mediaItemOriginal->original_filename,
            'title' => $mediaItemOriginal->title,
            'extension' => $extension,
            'related_type' => $mediaItemOriginal->related_type,
            'media_type' => $mediaItemOriginal->media_type,
            'backend_user_id' => $author->id,
            //TODO s3
            'size' => Storage::disk($mediaItemOriginal->base_folder)->size($destinationPath.'/'.$finalName),
            'width' => $img->width(),
            'height' => $img->height(),
            'base_folder' => $mediaItemOriginal->base_folder,
            'path' => trim($destinationPath.'/'.$finalName, '/'),
            'media_folder_id' => $mediaItemOriginal->media_folder_id,
        ]);

        $mediaItem->save();
        return $mediaItem;
    }
}
