<?php


class AttachmentService
{
    private $minio;
    private $shared;

    public function __construct($shared, $minio = null)
    {
        $this->shared = $shared;
        $this->minio = $minio;
    }

    public function upload($fileSystem, $validationConfig, $attachmentConfig, $request, $subFolder, $root = null)
    {
        if (!$request->hasFiles()) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "File parameter is empty");
        }

        $files = $request->getUploadedFiles();

        $this->checkFiles($files, $validationConfig);

        $params = $request->getPost();
        $params['keterangan'] = array_key_exists('keterangan', $params) ? $params['keterangan'] : '';
        $params['metadata'] = array_key_exists('metadata', $params) ? $params['metadata'] : -1;

        $attachments = [];
        $random = new \Phalcon\Security\Random();

        if ($fileSystem == FileSystemCloud::MINIO) {
            if (!$root) {
                $root = $attachmentConfig['tipe'] == Attachments::TIPE_PUBLIC
                    ? 'users_profpic' . DIRECTORY_SEPARATOR
                    : 'diskusi_attachment' . DIRECTORY_SEPARATOR;
            } else if (strcmp($root[-1], DIRECTORY_SEPARATOR) !== 0) {
                $root .= DIRECTORY_SEPARATOR;
            }

            foreach ($files as $file) {
                $uuid = $random->uuid();
                $key = $root . $subFolder . DIRECTORY_SEPARATOR . $uuid;

                $filepath = $file->getTempName();
                $this->minio->base_upload_attachment(
                    $filepath,
                    $file->getRealType(),
                    $attachmentConfig['tipe'] != Attachments::TIPE_PUBLIC,
                    $key
                );
                $attachment = new Attachments();
                $attachment->uuid = $uuid;
                $attachment->filename = $file->getName();
                $attachment->tipe = $attachmentConfig['tipe'];
                $attachment->keterangan = $params['keterangan'];
                $attachment->metadata = $params['metadata'];
                $attachment->status = Attachments::STATUS_CREATED;
                $attachment->created_by = $this->shared->user->um_id;
                $status = $attachment->save();

                if ($status) {
                    $attachments[] = $attachment;
                }
            }
        } elseif ($fileSystem == FileSystemCloud::LOCAL) {
            $root = $attachmentConfig['tipe'] == Attachments::TIPE_PUBLIC
                ? getenv("USER_IMAGE_FOLDER") . DIRECTORY_SEPARATOR
                : getenv("PRIVATE_FOLDER") . DIRECTORY_SEPARATOR . 'diskusi_attachment' . DIRECTORY_SEPARATOR;

            $directory = $root . $subFolder;

            if (!file_exists($directory) && !is_dir($directory)) {
                mkdir($directory, 0770, true);
            }

            foreach ($files as $file) {
                $uuid = $random->uuid();
                $key = $directory . DIRECTORY_SEPARATOR . $uuid;
                $file->moveTo($key);

                $attachment = new Attachments();
                $attachment->uuid = $uuid;
                $attachment->filename = $file->getName();
                $attachment->tipe = $attachmentConfig['tipe'];
                $attachment->keterangan = $params['keterangan'];
                $attachment->metadata = $params['metadata'];
                $attachment->status = Attachments::STATUS_CREATED;
                $attachment->created_by = $this->shared->user->um_id;
                $status = $attachment->save();

                if ($status) {
                    $attachments[] = $attachment;
                }
            }
        }

        return $attachments;
    }

    public function uploadUsingSecondaryS3($fileSystem, $validationConfig, $attachmentConfig, $request, $subFolder, $root = null)
    {
        if (!$request->hasFiles()) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "File parameter is empty");
        }

        $files = $request->getUploadedFiles();

        $this->checkFiles($files, $validationConfig);

        $params = $request->getPost();
        $params['keterangan'] = array_key_exists('keterangan', $params) ? $params['keterangan'] : '';
        $params['metadata'] = array_key_exists('metadata', $params) ? $params['metadata'] : -1;

        $attachments = [];
        $random = new \Phalcon\Security\Random();

        if ($fileSystem == FileSystemCloud::MINIO) {
            if (!$root) {
                $root = $attachmentConfig['tipe'] == Attachments::TIPE_PUBLIC
                    ? 'users_profpic' . DIRECTORY_SEPARATOR
                    : 'diskusi_attachment' . DIRECTORY_SEPARATOR;
            } else if (strcmp($root[-1], DIRECTORY_SEPARATOR) !== 0) {
                $root .= DIRECTORY_SEPARATOR;
            }

            /** @var MinioService $minioService */
            $minioService = $this->minio;
            foreach ($files as $file) {
                $uuid = $random->uuid();
                $key = $root . $subFolder . DIRECTORY_SEPARATOR . $uuid;

                $filepath = $file->getTempName();
                if ($minioService->isSecondaryS3Available()) {
                    $minioService->baseUploadUsingSecondaryS3(
                        $filepath,
                        $file->getRealType(),
                        $attachmentConfig['tipe'] != Attachments::TIPE_PUBLIC,
                        $key
                    );
                } else {
                    $minioService->base_upload_attachment(
                        $filepath,
                        $file->getRealType(),
                        $attachmentConfig['tipe'] != Attachments::TIPE_PUBLIC,
                        $key
                    );
                }
                $attachment = new Attachments();
                $attachment->uuid = $uuid;
                $attachment->filename = $file->getName();
                $attachment->tipe = $attachmentConfig['tipe'];
                $attachment->keterangan = $params['keterangan'];
                $attachment->metadata = $params['metadata'];
                $attachment->status = Attachments::STATUS_CREATED;
                $attachment->created_by = $this->shared->user->um_id;
                $status = $attachment->save();

                if ($status) {
                    $attachments[] = $attachment;
                }
            }
        }

        return $attachments;
    }

    public function download($directory, $uuid, $urlOnly = false, $root = null)
    {
        $attachment = Attachments::findOrFail(
            [
                "uuid = :uuid:",
                "bind" => [
                    "uuid" => $uuid
                ]
            ]
        );

        if (!empty($attachment)) {
            $key = $directory . DIRECTORY_SEPARATOR . $uuid;
            if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                if ($attachment->tipe == Attachments::TIPE_PRIVATE) {
                    $url = $this->minio->base_get_private_url((is_null($root) ? 'diskusi_attachment' : $root) . DIRECTORY_SEPARATOR . $key, $attachment->filename, true);
                } else {
                    $url = $this->minio->base_get_public_url((is_null($root) ? 'users_profpic' : $root) . DIRECTORY_SEPARATOR . $key, $attachment->filename, true);
                }
                $response = new \Phalcon\Http\Response();

                return $urlOnly ? new SimpleResponse(str_replace('"', '', $url)) : $response->redirect($url, true, 302);
            } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
                $filename = AttachmentHelper::getLocalPath($attachment, $directory, $root);

                if (!file_exists($filename)) {
                    throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found");
                }

                return MediaHelper::fileResponse($filename, $attachment->filename, true);
            }
        } else {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found or Not active");
        }
    }

    public function downloadUsingSecondaryS3($directory, $uuid, $urlOnly = false, $root = null)
    {
        $attachment = Attachments::findOrFail(
            [
                "uuid = :uuid:",
                "bind" => [
                    "uuid" => $uuid
                ]
            ]
        );

        if (!empty($attachment)) {
            /** @var MinioService $minioService */
            $minioService = $this->minio;
            $key = $directory . DIRECTORY_SEPARATOR . $uuid;
            if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                if ($minioService->isSecondaryS3Available()) {
                    if ($attachment->tipe == Attachments::TIPE_PRIVATE) {
                        $url = $minioService->basePrivateUrlUsingSecondaryS3((is_null($root) ? 'diskusi_attachment' : $root) . DIRECTORY_SEPARATOR . $key, $attachment->filename, true);
                    } else {
                        $url = $minioService->basePublicUrlUsingSecondaryS3((is_null($root) ? 'users_profpic' : $root) . DIRECTORY_SEPARATOR . $key, $attachment->filename, true);
                    }
                } else {
                    if ($attachment->tipe == Attachments::TIPE_PRIVATE) {
                        $url = $minioService->base_get_private_url((is_null($root) ? 'diskusi_attachment' : $root) . DIRECTORY_SEPARATOR . $key, $attachment->filename, true);
                    } else {
                        $url = $minioService->base_get_public_url((is_null($root) ? 'users_profpic' : $root) . DIRECTORY_SEPARATOR . $key, $attachment->filename, true);
                    }
                }
                $response = new \Phalcon\Http\Response();

                return $urlOnly ? new SimpleResponse(str_replace('"', '', $url)) : $response->redirect($url, true, 302);
            } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
                $filename = AttachmentHelper::getLocalPath($attachment, $directory);

                if (!file_exists($filename)) {
                    throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found");
                }

                return MediaHelper::fileResponse($filename, $attachment->filename, true);
            }
        } else {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found or Not active");
        }
    }

    public function downloadUsingDualS3($directory, $uuid, $urlOnly, $modulName)
    {
        /** @var Attachments $attachment */
        $attachment = Attachments::findOrFail(
            [
                "uuid = :uuid:",
                "bind" => [
                    "uuid" => $uuid
                ]
            ]
        );

        if (!empty($attachment)) {
            if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
                /** @var MinioService $minioService */
                $minioService = $this->minio;
                $filename = $attachment->filename;
                $filekey = $directory . DIRECTORY_SEPARATOR . $uuid;

                if ($attachment->tipe == Attachments::TIPE_PRIVATE) {
                    $filekey = 'diskusi_attachment' . DIRECTORY_SEPARATOR . $filekey;
                    $url = $minioService->checkAndDownloadPrivate(
                        $modulName,
                        $attachment->created_at,
                        $filekey,
                        $filename,
                        true
                    );
                } else {
                    $filekey = 'users_profpic' . DIRECTORY_SEPARATOR . $filekey;
                    $url = $minioService->checkAndDownloadPublic(
                        $modulName,
                        $attachment->created_at,
                        $filekey,
                        $filename,
                        true
                    );
                }

                $response = new \Phalcon\Http\Response();
                return $urlOnly ? new SimpleResponse(str_replace('"', '', $url)) : $response->redirect($url, true, 302);
            } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
                $filename = AttachmentHelper::getLocalPath($attachment, $directory);

                if (!file_exists($filename)) {
                    throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found");
                }

                return MediaHelper::fileResponse($filename, $attachment->filename, true);
            }
        } else {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found or Not active");
        }
    }

    public function downloadPrivate($directory, $fileKey, $name, $extension, $isDownload = true)
    {
        $key = $directory . DIRECTORY_SEPARATOR . $fileKey;
        if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
            $url = $this->minio->base_get_private_url($key, $name . '.' . $extension, $isDownload);
            return new SimpleResponse(str_replace('"', '', $url));
        } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found");
        }
        return new SimpleResponse("");
    }

    public function downloadPublic($directory, $fileKey, $name, $extension, $isDownload = true)
    {
        $key = $directory . DIRECTORY_SEPARATOR . $fileKey;
        if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
            $url = $this->minio->base_get_public_url($key, $name . '.' . $extension, $isDownload);
            return new SimpleResponse(str_replace('"', '', $url));
        } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found");
        }

        return new SimpleResponse("");
    }

    public function downloadPublicUsingSecondaryS3($directory, $fileKey, $name, $extension, $isDownload = true)
    {
        $key = $directory . DIRECTORY_SEPARATOR . $fileKey;
        if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
            /** @var MinioService $minioService */
            $minioService = $this->minio;
            if ($minioService->isSecondaryS3Available()) {
                $url = $minioService->basePublicUrlUsingSecondaryS3($key, $name . '.' . $extension, $isDownload);
            } else {
                $url = $minioService->base_get_public_url($key, $name . '.' . $extension, $isDownload);
            }
            return new SimpleResponse(str_replace('"', '', $url));
        } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found");
        }

        return new SimpleResponse("");
    }

    public function checkFilePublicInSecondaryS3($directory, $fileKey)
    {
        $key = $directory . DIRECTORY_SEPARATOR . $fileKey;
        if (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::MINIO) {
            return $this->minio->isFileExistNoLog($key);
        } elseif (getenv("FILESYSTEM_CLOUD") == FileSystemCloud::LOCAL) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "File Not Found");
        }

        return false;
    }

    private function checkFiles($files, $validationConfig)
    {
        $errors = [];

        $allowedTypes = '';
        $maxSizeKB = 25000;
        $maxFileCount = 10;

        if (array_key_exists('allowed-types', $validationConfig)) {
            $allowedTypes = $validationConfig['allowed-types'];
        }
        if (array_key_exists('max-size-kb', $validationConfig)) {
            $maxSizeKB = $validationConfig['max-size-kb'];
        }
        if (array_key_exists('max-file-count', $validationConfig)) {
            $maxFileCount = $validationConfig['max-file-count'];
        }

        if (count($files) > $maxFileCount) {
            array_push($errors, [
                "field" => "file",
                "filename" => '-',
                "message" => "Melebihi jumlah file upload: " . $maxFileCount
            ]);
        }
        foreach ($files as $file) {
            if ($file->getSize() < 1) {
                array_push($errors, [
                    "field" => $file->getKey(),
                    "filename" => "-",
                    "message" => "File kosong"
                ]);
                continue;
            }
            if (!in_array($file->getRealType(), $allowedTypes)) {
                array_push($errors, [
                    "field" => $file->getKey(),
                    "filename" => $file->getName(),
                    "message" => "Tipe file tidak diizinkan: " . $file->getRealType()
                ]);
            }
            if (strlen($file->getName()) > 200) {
                array_push($errors, [
                    "field" => $file->getKey(),
                    "filename" => $file->getName(),
                    "message" => "Panjang maksimal nama file 200 karakter"
                ]);
            }
            if ($file->getSize() > $maxSizeKB * 1024) {
                array_push($errors, [
                    "field" => $file->getKey(),
                    "filename" => $file->getName(),
                    "message" => "Ukuran maksimal file: " . $maxSizeKB . " KB"
                ]);
            }
        }

        if (count($errors) > 0) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "Invalid File Upload", $errors);
        }
    }

    public function uploadPublicContent($fileSystem, $validationConfig, $request, $subFolder)
    {
        if (!$request->hasFiles()) {
            throw new CustomErrorException(BaseResponse::INVALID_REQUEST, "File parameter is empty");
        }

        $files = $request->getUploadedFiles();

        $this->checkFiles($files, $validationConfig);

        $params = $request->getPost();
        $params['keterangan'] = array_key_exists('keterangan', $params) ? $params['keterangan'] : '';
        $params['metadata'] = array_key_exists('metadata', $params) ? $params['metadata'] : -1;

        $attachments = [];
        $random = new \Phalcon\Security\Random();

        if ($fileSystem == FileSystemCloud::MINIO) {
            /** @var MinioService $minioService */
            $minioService = $this->minio;

            $root = 'public_content' . DIRECTORY_SEPARATOR;

            foreach ($files as $file) {
                $key = $root . $subFolder . DIRECTORY_SEPARATOR . $file->getName();

                $filepath = $file->getTempName();
                if ($minioService->isSecondaryS3Available()) {
                    $minioService->baseUploadUsingSecondaryS3(
                        $filepath,
                        $file->getRealType(),
                        false,
                        $key
                    );
                } else {
                    $minioService->base_upload_attachment(
                        $filepath,
                        $file->getRealType(),
                        false,
                        $key
                    );
                }

                $attachments[] = [
                    'filename' => $file->getName(),
                    'subdir' => $subFolder
                ];
            }
        } elseif ($fileSystem == FileSystemCloud::LOCAL) {
            $root = getenv("USER_IMAGE_FOLDER") . DIRECTORY_SEPARATOR;

            $directory = $root . $subFolder;

            if (!file_exists($directory) && !is_dir($directory)) {
                mkdir($directory, 0770, true);
            }

            foreach ($files as $file) {
                $key = $directory . DIRECTORY_SEPARATOR . $file->getName();
                $file->moveTo($key);

                $attachments[] = [
                    'filename' => $file->getName(),
                    'subdir' => $subFolder
                ];

//                $uuid = $random->uuid();
//                $attachment = new Attachments();
//                $attachment->uuid = $uuid;
//                $attachment->filename = $file->getName();
//                $attachment->tipe = Attachments::TIPE_PUBLIC;
//                $attachment->keterangan = $subFolder;
//                $attachment->metadata = $params['metadata'];
//                $attachment->status = Attachments::STATUS_CREATED;
//                $attachment->created_by = $this->shared->user->um_id;
//                $status = $attachment->save();
//
//                if ($status) {
//                    $attachments[] = $attachment;
//                }
            }
        }

        return $attachments;
    }

    public function presignedUrlUpload($subFolder, $isPublic = true, $root = null)
    {
        $random = new \Phalcon\Security\Random();

        if (!$root) {
            $root = $isPublic
                ? 'users_profpic' . DIRECTORY_SEPARATOR
                : 'diskusi_attachment' . DIRECTORY_SEPARATOR;
        } else if (strcmp($root[-1], DIRECTORY_SEPARATOR) !== 0) {
            $root .= DIRECTORY_SEPARATOR;
        }
        $uuid = $random->uuid();
        $key = $root . $subFolder . DIRECTORY_SEPARATOR . $uuid;

        /** @var MinioService $minioService */
        $minioService = $this->minio;
        $url = $minioService->getUploadPresignedUrl($key, $isPublic);

        return [
            "uuid" => $uuid,
            "url" => $url
        ];
    }
}
