<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Attachment;
use App\Models\Product;
use App\Models\Supplier;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Collection;
use Illuminate\View\View;

class FileBatchUploadController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index(): View
    {
        $suppliers = Supplier::orderBy('name')->get(['id', 'name'])->toArray();

        return view('admin.fileBatchController.index')->with(
            [
                'suppliers' => $suppliers,
                'formUUID' => \Str::uuid(),
            ]

        );
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request): Response|JsonResponse
    {
        $functionName = 'client'.ucfirst($request->input('requestType'));
        if (method_exists($this, $functionName)) {
            return call_user_func([$this, $functionName], $request);
        }

        return response()->json([
            'jsonrpc' => '2.0',
            'request_param' => $request->all(),
            'result' => null,
            'id' => 'id',
        ], 200);
    }

    private array $responseJSON = [
        'jsonrpc' => '2.0',
        'result' => null,
        'status' => 200,
    ];

    private array $responseError = [];

    private int $responseStatus = 200;

    private function responseAddError(string $errorStr)
    {
        $this->responseError[] = $errorStr;
    }

    private function responseSetResultStatus(int $status)
    {
        $this->responseJSON['status'] = $status;
    }

    private function responseSetStatus(int $status)
    {
        $this->responseStatus = $status;
    }

    private function responseAddResult($data)
    {
        if (! $this->responseJSON['result']) {
            $this->responseJSON['result'] = [];
        }
        $this->responseJSON['result'] = array_merge($this->responseJSON['result'], $data);
    }

    private function responseAddNewData($pointer, $data)
    {
        $this->responseJSON[$pointer] = $data;
    }

    private function responseMake($result = null, $errors = null, $status = null)
    {
        if ($errors) {
            $this->responseError = array_merge($this->responseError, $errors);
        }
        if (count($this->responseError) > 0) {
            $this->responseJSON['errors'] = $this->responseError;
            $this->responseSetStatus(500);
        }

        if ($status) {
            $this->responseSetStatus($status);
        }
        if ($result) {
            if (! $this->responseJSON['result']) {
                $this->responseJSON['result'] = $result;
            } else {
                $this->responseJSON['result'] = array_merge($this->responseJSON['result'], $result);
            }
        }

        return response()->json($this->responseJSON, $this->responseStatus);
    }

    private function removeAttachment(Collection $attachments)
    {
        /** @var Attachment $attachment */
        foreach ($attachments as $attachment) {
            $attachment->removeFile();
            $attachment->delete();
            // dd($attachment->toArray());
        }
    }

    private array $acceptedAttachmentType = ['picture', 'specification'];

    private function clientFilesAttach(Request $request)
    {
        //        sleep(30);
        $this->responseAddNewData('function', lcfirst(str_replace('client', '', __FUNCTION__)));

        if (! $supplier = Supplier::find($request->get('supplier'))) {
            return $this->responseMake(null, ['Ismeretlen Beszállító']);
        }
        if (! $request->get('attachmentType') || ! in_array($request->get('attachmentType'), $this->acceptedAttachmentType)) {
            return $this->responseMake(null, ['Ismeretlen csatolmány típus']);
        }
        $attachTypePointer = $request->get('attachmentType');
        // $attachTypePointer='specification';
        $attachTypeRelationPointer = $request->get('attachmentType').'s';
        // sleep(2);

        $fileNamesArray = json_decode($request->get('fileNames'));
        foreach ($fileNamesArray as $i => $v) {
            //            print " $i $v".PHP_EOL;
            $product = Product::with($attachTypePointer)->where('supplier_id', '=', $supplier->id)->where('supplierProductNumber', '=', pathinfo($v)['filename'])->get();
            if (count($product) < 1) {
                $this->responseAddResult([$i => ['name' => $v, 'status' => 'error', 'pid' => null, 'desc' => 'Nincs ilyen termék']]);

                continue;
            }
            if (count($product) > 1) {
                $this->responseAddResult([$i => ['name' => $v, 'status' => 'error', 'pid' => null, 'desc' => 'Több termék rendelkezik ezzel a cikkszámmal']]);

                continue;
            }
            $product = $product->first();
            $attachCount = count($product->{$attachTypePointer}()->get()->toArray());
            if ($attachCount > 0) {
                $this->removeAttachment($product->{$attachTypePointer}()->get());
            }
            /*            $this->responseAddResult([$i=>['attachCount'=>$attachCount, 'name'=>$v,'status'=>'ok','pid'=>$product->id,'desc'=>$product->name]]);
                        continue;
                        dd($product->{$attachTypePointer}()->get()->toArray());*/

            $path = \Storage::disk('uploader')->getDriver()->getAdapter()->getPathPrefix();
            $dir = $this->getTargetDirectory();
            $file = pathinfo($v)['basename'];
            $attachment = new Attachment;
            $attachment->attachType = $request->get('attachmentType');
            $FileObject = new UploadedFile($path.$dir.$file, $file);
            $attachment->addFile($FileObject);
            $attachment->attachable()->associate($product);
            $attachment->save();
            /**@todo:need remove file
             *
             */
            $this->responseAddResult([$i => ['name' => $v, 'status' => 'ok', 'pid' => $product->id, 'desc' => $product->name]]);
        }

        return $this->responseMake();
    }

    private function clientFilesAdded(Request $request)
    {
        $this->responseAddNewData('function', lcfirst(str_replace('client', '', __FUNCTION__)));

        if (! $supplier = Supplier::find($request->get('supplier'))) {
            return $this->responseMake(null, ['Ismeretlen Beszállító']);
        }
        if (! $request->get('attachmentType') || ! in_array($request->get('attachmentType'), $this->acceptedAttachmentType)) {
            return $this->responseMake(null, ['Ismeretlen csatolmány típus']);
        }

        $fileNamesArray = json_decode($request->get('fileNames'));

        foreach ($fileNamesArray as $i => $v) {
            //            print " $i $v".PHP_EOL;
            $product = Product::where('supplier_id', '=', $supplier->id)->where('supplierProductNumber', '=', pathinfo($v)['filename'])->get();
            if (count($product) < 1) {
                $this->responseAddResult([$i => ['name' => $v, 'status' => 'error', 'pid' => null, 'desc' => 'Nincs ilyen termék']]);

                continue;
            }
            if (count($product) > 1) {
                $this->responseAddResult([$i => ['name' => $v, 'status' => 'error', 'pid' => null, 'desc' => 'Több termék rendelkezik ezzel a cikkszámmal']]);

                continue;
            }
            $product = $product->first();
            $this->responseAddResult([$i => ['name' => $v, 'status' => 'ok', 'pid' => $product->id, 'desc' => $product->name]]);
        }

        return $this->responseMake();
    }

    private function getTargetDirectory()
    {
        return \request()->get('formUUID').DIRECTORY_SEPARATOR;
        // return csrf_token().DIRECTORY_SEPARATOR;
    }

    private function chunkUpload(Request $request)
    {
        $request->get('chunks');
        $request->get('chunk');

        $partStr = '.part';
        $fileNameWithPath = $this->getTargetDirectory();
        $fileNameWithPath .= $request->get('name').$partStr;

        \Storage::disk('uploader')->append($fileNameWithPath, $request->file('file')->get());
        // \Storage::disk('uploader')->append($fileNameWithPath,Carbon::now());
        if ($request->get('chunk') == $request->get('chunks') - 1) {
            \Storage::disk('uploader')->move($fileNameWithPath, str_replace($partStr, '', $fileNameWithPath));
        }

        return response()->json([
            'jsonrpc' => '2.0',
            'result' => null,
            'id' => 'id',
        ], 200);
    }

    public function upload(Request $request)
    {
        $this->responseAddNewData('function', 'upload');
        if ($request->get('chunks') > 1) {
            return $this->chunkUpload($request);
        }
        $fileNameWithPath = $this->getTargetDirectory().$request->get('name');
        if (\Storage::disk('uploader')->put($fileNameWithPath, $request->file('file')->get())) {
            $this->responseAddResult([$request->get('fileId') => ['name' => $request->get('name'), 'status' => 'uploaded', 'uploaded' => Carbon::now()->toDateTimeString()]]);

            return $this->responseMake();
        }
        $this->responseAddError('Feltöltési hiba!');

        return $this->responseMake();
    }

    private function x()
    {
        // 5 minutes execution time
        @set_time_limit(5 * 60);

        // Uncomment this one to fake upload time
        usleep(500000);
        // die(ini_get("upload_tmp_dir"));
        // Settings
        // $targetDir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload";
        $targetDir = './uploads';
        $cleanupTargetDir = true; // Remove old files
        $maxFileAge = 12 * 3600; // Temp file age in seconds

        // Create target dir
        if (! file_exists($targetDir)) {
            @mkdir($targetDir);
        }

        // Get a file name
        if (isset($_REQUEST['name'])) {
            $fileName = $_REQUEST['name'];
        } elseif (! empty($_FILES)) {
            $fileName = $_FILES['file']['name'];
        } else {
            $fileName = uniqid('file_');
        }

        $filePath = $targetDir.DIRECTORY_SEPARATOR.$fileName;

        // Chunking might be enabled
        $chunk = isset($_REQUEST['chunk']) ? intval($_REQUEST['chunk']) : 0;
        $chunks = isset($_REQUEST['chunks']) ? intval($_REQUEST['chunks']) : 0;

        // Remove old temp files
        if ($cleanupTargetDir) {
            if (! is_dir($targetDir) || ! $dir = opendir($targetDir)) {
                exit('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}');
            }

            while (($file = readdir($dir)) !== false) {
                $tmpfilePath = $targetDir.DIRECTORY_SEPARATOR.$file;

                // If temp file is current file proceed to the next
                if ($tmpfilePath == "{$filePath}.part") {
                    continue;
                }

                // Remove temp file if it is older than the max age and is not the current file
                if (preg_match('/\.part$/', $file) && (filemtime($tmpfilePath) < time() - $maxFileAge)) {
                    @unlink($tmpfilePath);
                }
            }
            closedir($dir);
        }

        // Open temp file
        if (! $out = @fopen("{$filePath}.part", $chunks ? 'ab' : 'wb')) {
            exit('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
        }

        if (! empty($_FILES)) {
            if ($_FILES['file']['error'] || ! is_uploaded_file($_FILES['file']['tmp_name'])) {
                exit('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
            }

            // Read binary input stream and append it to temp file
            if (! $in = @fopen($_FILES['file']['tmp_name'], 'rb')) {
                exit('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
            }
        } else {
            if (! $in = @fopen('php://input', 'rb')) {
                exit('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
            }
        }

        while ($buff = fread($in, 4096)) {
            fwrite($out, $buff);
        }

        @fclose($out);
        @fclose($in);

        // Check if file has been uploaded
        if (! $chunks || $chunk == $chunks - 1) {
            // Strip the temp .part suffix off
            rename("{$filePath}.part", $filePath);
        }

        // Return Success JSON-RPC response
        exit('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}');

    }
}
