<?php

namespace App\Modules\Image\Infraestructure;

use App\Modules\Image\Domain\BasicImage;
use App\Modules\Image\Domain\ImageThumbnailData;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Intervention\Image\ImageManagerStatic;
use Throwable;

class BasicConfigImageStorage
{

    private const EXTENSION = 'png';

    private Request $request;

    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    public function storeImage(string $store, string $inputName, ImageThumbnailData $thumbData)
    {
        if (!$this->request->hasFile($inputName)) {
            return null;
        }
        $thumbUrl = '';
        $imageUrl = '';
        $tempUrl = '';
        try {
            $path = config('var.PATH_PRIVATE');
            $imageDir = $this->createDirIfNotExists('image' . DIRECTORY_SEPARATOR . $store . DIRECTORY_SEPARATOR . 'large', 'img');
            $thumbDir = $this->createDirIfNotExists('image' . DIRECTORY_SEPARATOR . $store . DIRECTORY_SEPARATOR . 'thumb', 'img');
            $ext = $this->request->file($inputName)->getClientOriginalExtension();

            $tempUrl = $this->moveToTemp($inputName, $ext);

            $thumbUrl = $this->generateThumb($path . $thumbDir, $ext, $thumbData, $tempUrl);
            $imageUrl = $this->generateImage($path . $imageDir, $ext, $tempUrl);
            unlink($tempUrl);
            $tempUrl = '';
            return new BasicImage(
                $thumbDir . DIRECTORY_SEPARATOR . $thumbUrl,
                $imageDir . DIRECTORY_SEPARATOR . $imageUrl,
                self::EXTENSION,
                filesize($path . $imageDir . DIRECTORY_SEPARATOR . $imageUrl)
            );
        } catch (Throwable $ex) {
            if ($tempUrl) {
                unlink($tempUrl);
            }
            if ($thumbUrl) {
                unlink($path . $thumbDir . DIRECTORY_SEPARATOR . $thumbUrl);
            }
            if ($imageUrl) {
                unlink($path . $imageDir . DIRECTORY_SEPARATOR . $imageUrl);
            }
            throw $ex;
        }
    }

    private function generateThumb($thumbDir, $ext, ImageThumbnailData $thumbData, $temp)
    {
        $imageThumb = ImageManagerStatic::make($temp);
        $imageThumb->resize($thumbData->getWidth(), $thumbData->getHeight());
        list($thumbUrl, $thumbFile) = $this->generateFilePointer($thumbDir, $ext);
        fwrite($thumbFile, $imageThumb->encode(self::EXTENSION));
        fclose($thumbFile);
        return $thumbUrl;
    }

    private function generateImage($imageDir, $ext, $temp): string
    {
        $imageThumb = ImageManagerStatic::make($temp);
        list($imageUrl, $imageFile) = $this->generateFilePointer($imageDir, $ext);
        fwrite($imageFile, $imageThumb->encode(self::EXTENSION));
        fclose($imageFile);
        return $imageUrl;
    }

    private function generateFilePointer($dir, $ext)
    {
        $flag = 0;
        $attempts = 10;
        do {
            $generated = date('His') . uniqid() . '.' . $ext;
            $generatedUrl = $dir . DIRECTORY_SEPARATOR . $generated;
            $flag++;
            $file = false;
            if ($flag < $attempts) {
                try {
                    $file = fopen($generatedUrl, 'x');
                } catch (\Throwable $th) {
                }
            }
        } while ($file === false && $flag < $attempts);
        if ($flag >= $attempts) {
            throw new HttpException(406, 'Error generating file.');
        }
        return [$generated, $file];
    }

    private function moveToTemp($inputName, $ext): string
    {
        $tempName = uniqid() . '.' . $ext;
        $path = config('var.PATH_PRIVATE') . 'temp' . DIRECTORY_SEPARATOR;
        if (!$this->request->file($inputName)->move($path, $tempName)) {
            throw new HttpException(406, 'Error uploading file');
        }
        return $path . $tempName;
    }

    public function createDirIfNotExists($path, $prefix): string
    {
        $filesPath = config('var.PATH_PRIVATE');
        $directory = $path . DIRECTORY_SEPARATOR . $prefix . date('ym');
        $dirPath = $filesPath . $directory;

        if (!is_dir($dirPath)) {
            @mkdir($dirPath, 0700);
        }
        return $directory;
    }
}
