<?php

namespace App\Libs\Services;

use App\Libs\Exception\InvalidConfigurationException;
use App\Models\BackendAuth\BackendUser;
use App\Models\MediaItem;
use Illuminate\Http\UploadedFile;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Str;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Image;
use Storage;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

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

        if (!empty($search)) {
            $query = MediaItem::where(function ($query) use ($search) {
                $query->orWhere('filename', 'like', "%{$search}%");
            });
        } else {
            $query = MediaItem::query();
        }
        if (empty($sort)) {
            $rows = $query->paginate($rowCount);
        } 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($rowCount);
                break;
            }
        }

        return [
            "total" => $rows->total(),
            "current" => $rows->currentPage(),
            "rowCount" => $rowCount,
            "rows" => $rows->getCollection()
        ];
    }

    public function getMediaListByInfiniteScrolling($lastId, $rowCount, $typeFilter, $relatedFilter, $searchString)
    {
        $query = MediaItem::query();

        if (!empty($typeFilter)) {
            $realFilter = array_intersect($typeFilter, array("image", "video", "document", "svg"));
        } else {
            $realFilter = ["none"];
        }
        $query->whereIn('media_type', $realFilter);

        if (!empty($relatedFilter)) {
            $query->where('related_type', $relatedFilter);
        }

        if ($searchString && strlen(trim($searchString)) > 2) {
            $query->where(function ($q) use ($searchString) {
                $q->where('original_filename', 'like', '%' . $searchString . '%')
                    ->orWhere('title', 'like', '%' . $searchString . '%')
                    ->orWhere('filename', 'like', '%' . $searchString . '%');
            });
        }

        if ($lastId && is_numeric($lastId)) {
            $query->where('id', '<', $lastId);
        }

        $rows = $query->orderBy('id', 'desc')->take($rowCount);

        return [
            "rowCount" => $rowCount,
            "rows" => $rows->get()
        ];
    }

    /**
     * Saves the uploaded file and it's respective MediaItem
     *
     * @param  BackendUser  $author
     * @param  UploadedFile  $file
     * @param  string  $relatedType
     *
     * @return MediaItem
     * @throws HttpException
     */
    public function uploadMedia(BackendUser $author, UploadedFile $file, $relatedType)
    {
        $destinationPath = config('path.internal.media');
        $originalFilename = $file->getClientOriginalName();
        $extension = strtolower($file->getClientOriginalExtension());
        $withoutExt = preg_replace('/\\.[^.\\s]{3,4}$/', '', $originalFilename);
        $size = $file->getSize();
        $width = null;
        $height = null;
        $newFilename = urlize($withoutExt);
        $finalName = $newFilename . '.' . $extension;


        $mediaType = $this->_getMediaType($extension);
        if (!$mediaType) {
            throw new HttpException(403, 'Invalid extension file');
        }

        if ($mediaType == MediaItem::MEDIA_TYPE_IMAGE) {

            $img = Image::make($file->getRealPath());
            $img->orientate();

            //if we save the gif image pointing to the file the animation will be lost
            if ($img->mime() !== 'image/gif') {
                $img->save();
            }

            $width = $img->width();
            $height = $img->height();
        }


        //Checks for colliding filenames
        $i = 1;

        while (Storage::has($destinationPath . '/' . $finalName)) {
            $finalName = $newFilename . '-' . $i . '.' . $extension;
            $i++;
            //for safety
            if ($i > 200) {
                throw new HttpException(400, 'The uploaded file already exists');
            }
        }

        $path = $file->storeAs($destinationPath, $finalName);

        if (!$path) {
            throw new HttpException(400, 'Error saving the uploaded file');
        }

        $mediaItem = new MediaItem(array(
            'filename' => $finalName,
            'original_filename' => $originalFilename,
            'title' => $originalFilename,
            'extension' => $extension,
            'related_type' => $relatedType,
            'media_type' => $mediaType,
            'backend_user_id' => $author->id,
            'size' => $size,
            'width' => $width,
            'height' => $height,
        ));

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

    /**
     * get the media type (video|document|image|svg)
     *
     * @param  string  $ext
     *
     * @return bool|string
     */
    private function _getMediaType($ext)
    {
        $allowedExts = config('backoffice.media.allowedExts');
        foreach ($allowedExts as $type => $allowedExt) {
            if (in_array(strtolower($ext), $allowedExt)) {
                return $type;
            }
        }
        return false;
    }

    /**
     * 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"];
        $pos = strrpos($filename, "_");
        if ($pos === false) {
            throw new InvalidConfigurationException("Invalid size");
        }

        $originalFileName = substr($fileInfo["filename"], 0, $pos) . ".{$extension}";
        $originalFilePath = config("path.internal.media") . "/{$originalFileName}";
        $targetFilePath = config("path.internal.thumbs") . "/{$filename}";
        $transformationSize = substr($fileInfo["filename"], $pos + 1);

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

        if (!Storage::has($originalFilePath)) {
            throw new NotFoundHttpException();
        }

        $size = $allowedSizes[$transformationSize];

        $quality = 90;

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

        //TODO S3
        $img = Image::make(Storage::path($originalFilePath));
        $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->fit($width, $height);
        } else {
            $img->resize($width, $height, function ($constraint) {
                $constraint->aspectRatio();
            });
        }

        if (!Storage::has(config('path.internal.thumbs'))) {
            Storage::makeDirectory(config('path.internal.thumbs'));
        }

        //TODO S3
        $img->save(Storage::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;
        $mediaPath = config('path.internal.media');
        $filePath = $mediaPath . '/' . $filename;

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

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

            foreach ($allowedSizes as $size => $sizeConfig) {
                $thumbFile = "{$thumbsPath}/{$withoutExt}_{$size}.{$ext}";
                if (Storage::has($thumbFile)) {
                    Storage::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)
    {
        $mediaItem = MediaItem::find($mediaId);

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

        $destinationPath = config('path.internal.media');
        $cropData['x'] = (int) $cropData['x'];
        $cropData['y'] = (int) $cropData['y'];
        $cropData['width'] = (int) $cropData['width'];
        $cropData['height'] = (int) $cropData['height'];

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

        $filename = $mediaItem->filename;
        $mediaPath = config('path.internal.media');
        $mediaUri = config('path.frontend.media');

        if (!Storage::has($mediaPath . '/' . $filename)) {
            throw new NotFoundHttpException();
        }

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

        $newFilename = urlize($withoutExt);
        $finalName = $newFilename . '.' . $extension;
        //Checks for colliding filenames
        $i = 1;
        while (Storage::has($mediaPath . '/' . $finalName)) {
            $finalName = $newFilename . '-' . $i . '.' . $extension;
            $i++;
            //for safety
            if ($i > 200) {
                throw new HttpException(400, 'Not possible to crop');
            }
        }

        $img = Image::make(url(Storage::url($mediaPath . '/' . $filename)));

        // crop image
        $img->crop($cropData['width'], $cropData['height'], $cropData['x'], $cropData['y']);
        //TODO S3
        $img->save(Storage::path($destinationPath . '/' . $finalName));

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

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