<?php

namespace App\Helpers;

use App\Models\Upload;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use App\Helpers\PlanHelper;
use App\Helpers\NudityHelper;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use App\Modifiers\{
    Compressor,
    Converter,
    Resizer,
    Rotater,
    Watermark,
    Preferences,
    Effects,
    Exif,
};

class UploadRestUrlHelper
{
    private static $defaultStorage;

    public function __construct() 
    {
        self::$defaultStorage = defaultStorage()->storage_key;
    }

    public static function upload(
        string $fileUrl, 
        array $formData
    ): array {
        $request = request();
        $token = $request->header('token');

        if (!$token) {
            return [
                false,
                'User token is missing.'
            ];
        }

        $userData = DB::table('users')
            ->where('api_token', $token)
            ->first();

        if (!$userData) {
            return [
                false,
                'User token is wrong.'
            ];
        }

        $fileName = pathinfo($fileUrl, PATHINFO_BASENAME) ?? false;
        $fileExt = pathinfo($fileUrl, PATHINFO_EXTENSION) ?? false;
        
        if (!$fileName || !$fileExt) {
            return [
                false,
                'The file URL is incorrect.',
            ];
        }

        // Check file url status
        $fileHeaders = self::fileStatus($fileUrl);
        if ($fileHeaders != '200') {
            return [
                false,
                __('lang.cannot_retrieved_file'),
            ];
        }

        // Check file size
        $fileSize = self::fileSize($fileUrl);
        $fileSize = (int) intval($fileSize) / 1024;
        $maxSize = config("upload.MAX_FILE_SIZE");
        if ($fileSize > $maxSize) {
            return [
                false,
                __("lang.file_max_size",['var' => formatKiloBytes($maxSize)]),
            ];
        }

        // Check file format (Extension)
        $allowedTypes = uploadableTypes('array');
        if (!in_array($fileExt, $allowedTypes)) {
            return [
                false,
                __('lang.file_cannot_upload'),
            ];
        }

        // Check user permission for api usage
        $canIRest = PlanHelper::canIRest(
            $userData->id, 
        );
        if (!$canIRest[0]) {
            return [
                false,
                $canIRest[1], 
            ];
        }

        // Check user permission for upload process
        $canIUpload = PlanHelper::canIUpload(
            $userData->id, 
            $fileName, 
            $fileSize
        );
        if (!$canIUpload[0]) {
            return [
                false,
                $canIUpload[1], 
            ];
        }

        // Start upload
        try {
            $contents = self::fileBody($fileUrl);
            if (!$contents) {
                return [
                    false,
                    __('lang.cannot_retrieved_file'),
                ];
            }

            $fileRandom = Str::random(40);
            $fileName = "{$fileRandom}.{$fileExt}";
            $upload = Storage::disk('temp')
                ->put($fileName, $contents);

            if (!$upload) {
                return [
                    false,
                    __('lang.file_cannot_upload'),
                ];
            }

            $nudityChecker = setting('nudity_status');
            if ($nudityChecker == 1) {
                $nudityCheck = NudityHelper::check($fileName);
                if (!$nudityCheck[0]) {
                    return [
                        false,
                        $nudityCheck[1], 
                    ];
                }
            }

            $manipulate = self::start(
                $fileName,
                $formData,
                $userData->id,
            );

            if ($manipulate[0]) {
                return [
                    true,
                    url("get/{$manipulate[1]}"),
                    url("file/{$manipulate[1]}"),
                ];
            } else {
                Storage::disk('temp')
                    ->delete($fileName);

                return [
                    false,
                    $manipulate[1], 
                ];
            }
        } catch (\Exception $e) {
            Log::error($e);
            return [
                false,
                __('lang.cannot_retrieved_file'),
            ];
        }
    }

    public static function start(
        string $fileName, 
        array $formData,
        int $userId
    ): array {

        $compress = self::compress(
            $fileName,
            $formData,
        );

        if ($compress[0]) {

            $resize = self::resize(
                $compress[1],
                $formData,
            );

            if ($resize[0]) {

                $rotate = self::rotate(
                    $resize[1],
                    $formData,
                );
                if ($rotate[0]) {
    
                    $watermark = self::watermark(
                        $rotate[1],
                        $formData,
                    );
                    if ($watermark[0]) {

                        $preferences = self::preferences(
                            $watermark[1],
                            $formData,
                        );
                        if ($preferences[0]) {

                            $effects = self::effects(
                                $watermark[1],
                                $formData,
                            );
                            if ($effects[0]) {

                                $exif = self::exif(
                                    $effects[1],
                                    $formData,
                                );
                                if ($exif[0]) {

                                    $convert = self::convert(
                                        $exif[1],
                                        $formData,
                                    );
                            
                                    if($convert[0]) {

                                        $database = self::database(
                                            $convert[1],
                                            $formData,
                                            $userId
                                        );

                                        if ($database[0]) {
                                            return [
                                                true,
                                                $database[1],
                                            ];
                                        } else {
                                            return [
                                                false,
                                                $database[1],
                                            ];
                                        }

                                    } else {
                                        return [
                                            false,
                                            $convert[1],
                                        ];
                                    }

                                } else {
                                    return [
                                        true,
                                        $exif[1],
                                    ];
                                }
                        
                            } else {
                                return [
                                    false,
                                    $effects[1],
                                ]; 
                            }

                        } else {
                            return [
                                false,
                                $preferences[1],
                            ];
                        }
                        
                    } else {
                        return [
                            false,
                            $watermark[1],
                        ];
                    }

                } else {
                    return [
                        false,
                        $rotate[1],
                    ];
                }

            } else {
                return [
                    false,
                    $resize[1],
                ];
            }
    
        } else {
            return [
                false,
                $compress[1],
            ];
        }
    }
    
    public static function compress(
        string $fileName, 
        array $formData
    ): array {
        if (isset($formData['compress']) && $formData['compress']) {
            $compressor = new Compressor;
            $make = $compressor->make($fileName, $formData['compress']);

            return $make;
        }

        return [
            true,
            $fileName
        ];
    }

    public static function convert(
        string $fileName, 
        array $formData
    ): array {
        if (isset($formData['type']) && $formData['type']) {
            $converter = new Converter;
            $make = $converter->make($fileName, $formData['type']);

            return $make;
        }

        return [
            true,
            $fileName
        ];
    }

    public static function resize(
        string $fileName, 
        array $formData
    ): array {
        if (
            (
                isset($formData['size']['width']) 
                && $formData['size']['width']
            )
            || (
                isset($formData['size']['height']) 
                && $formData['size']['height']
            )
        ) {
            $resizer = new Resizer;
            $make = $resizer->make($fileName, $formData['size']);

            return $make;
        }

        return [
            true,
            $fileName
        ];
    }

    public static function rotate(
        string $fileName, 
        array $formData
    ): array {
        if (
            isset($formData['rotation-reflect']) 
            && $formData['rotation-reflect']
        ) {
            $rotater = new Rotater;
            $make = $rotater->make($fileName, $formData['rotation-reflect']);

            return $make;
        }

        return [
            true,
            $fileName
        ];
    }

    public static function watermark(
        string $fileName, 
        array $formData
    ): array {
        if (
            isset($formData['watermark']) 
            && $formData['watermark']
        ) {
            $watermark = new Watermark;
            $make = $watermark->make($fileName, $formData['watermark']);

            return $make;
        }

        return [
            true,
            $fileName
        ];
    }

    public static function preferences(
        string $fileName, 
        array $formData
    ): array {
        if (
            (
                isset($formData['preferences']['brightness']) 
                && $formData['preferences']['brightness']
            )
            || (
                isset($formData['preferences']['contrast']) 
                && $formData['preferences']['contrast']
            )
            || (
                isset($formData['preferences']['gamma']) 
                && $formData['preferences']['gamma']
            )
            || (
                isset($formData['preferences']['red']) 
                && $formData['preferences']['red']
            )
            || (
                isset($formData['preferences']['green']) 
                && $formData['preferences']['green']
            )
            || (
                isset($formData['preferences']['blue']) 
                && $formData['preferences']['blue']
            )
        ) {
            $preferences = new Preferences;
            $make = $preferences->make($fileName, $formData['preferences']);

            return $make;
        }

        return [
            true,
            $fileName
        ];
    }

    public static function effects(
        string $fileName, 
        array $formData
    ): array {
        if (
            (
                isset($formData['effects']['grayscale']) 
                && $formData['effects']['grayscale']
            )
            || (
                isset($formData['effects']['blur']) 
                && $formData['effects']['blur']
            )
            || (
                isset($formData['effects']['invert']) 
                && $formData['effects']['invert']
            )
            || (
                isset($formData['effects']['pixelation']) 
                && $formData['effects']['pixelation']
            )
            || (
                isset($formData['effects']['reduce']) 
                && $formData['effects']['reduce']
            )
        ) {
            $effects = new Effects;
            $make = $effects->make($fileName, $formData['effects']);

            return $make;
        }

        return [
            true,
            $fileName
        ];
    }

    public static function exif(
        string $fileName, 
        array $formData
    ): array {
        if (
            (
                isset($formData['exif']['basic']) 
                && $formData['exif']['basic'] == 1
            )
            || (
                isset($formData['exif']['deep']) 
                && $formData['exif']['deep'] == 1
            )
        ) {
            $exif = new Exif;
            $make = $exif->make($fileName, $formData['exif']);

            return $make;
        }

        return [
            true,
            $fileName
        ];
    }

    public static function database(
        string $fileName, 
        array $formData,
        int $userId,
    ): array {
        $request = request();

        if (!Storage::disk('temp')->exists($fileName)) {
            return [
                false, 
                'File not found.'
            ];
        }

        $userIp = $request->ip();
        $fileNameArr = explode('.', $fileName);
        $fileSize = Storage::disk('temp')
            ->size($fileName);

        $fileData['created_by_id'] = $userId;
        $fileData['created_by_ip'] = $userIp;
        $fileData['updated_by_id'] = $userId;
        $fileData['updated_by_ip'] = $userIp;
        $fileData['filename'] = $fileName;
        $fileData['filesize'] = $fileSize;
        $fileData['filetype'] = $fileNameArr[1];
        $fileData['disk'] = self::$defaultStorage;

        if (isset($formData['auto-remove']) && $formData['auto-remove']) {
            $fileData['autoremove'] = $formData['auto-remove'];
        }

        if (isset($formData['password']) && $formData['password']) {
            $fileData['password'] = $formData['password'];
        }
        
        $create = Upload::create($fileData);

        if ($create) {

            try{
                Storage::disk(self::$defaultStorage)->put(
                    $fileName,
                    Storage::disk('temp')->get($fileName)
                );

                Storage::disk('temp')->delete($fileName);

                return [
                    true, 
                    $fileName,
                ];
            } catch (\Exception $e) {
                Log::error($e);
                Upload::where('id', $create->id)->delete();
                return [
                    false,
                    __('lang.file_cannot_upload'),
                ];
            }
        }

        return [
            false, 
            __('lang.file_cannot_upload'),
        ];
    }
    
    private static function fileBody($url)
    {
        return Http::get($url)->body();
    }

    private static function fileStatus($url)
    {
        $response = Http::get($url);
        return $response->successful()
            ? true
            : false;
    }

    private static function fileSize($url)
    {
        $response = Http::get($url);
        return $response->header('Content-Length');
    }

}
