<?php

namespace Application\Service\Extension\Uploader\adapter;

use Application\Common\Com;
use Application\Service\Extension\Uploader\Image;
use Application\Service\Extension\Uploader\ImageUploader;
use Application\Service\Extension\Uploader\UploadAdapterAbstract;
use Application\Service\Extension\Uploader\UploadException;
use OSS\Core\OssException;
use OSS\Http\RequestCore_Exception;
use OSS\OssClient;

class Oss extends UploadAdapterAbstract
{
    // 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维，请登录 https://ram.console.aliyun.com 创建RAM账号。
    private string $accessKeyId;

    private string $accessKeySecret;

    // Endpoint以杭州为例，其它Region请按实际情况填写。
    private string $endpoint;

    // 存储空间名称
    private string $bucket;

    public function __construct(array $config, $bucket = 'yikeen'){
        parent::__construct($config);
        $this->accessKeyId = $config['oss'][$bucket]['accessKeyId'];
        $this->accessKeySecret = $config['oss'][$bucket]['accessKeySecret'];
        $this->endpoint = $config['oss'][$bucket]['endpoint'];
        $this->bucket = $config['oss'][$bucket]['bucket'];
    }

    /**
     * @param Image $file
     * @return array
     * @throws OssException
     * @throws UploadException
     */
    public function upload(Image $file)
    {
        $ossClient = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint);
        $savePath = ltrim($this->getSaveDir() . "/{$file->baseName}.{$file->extension}", '/');
        $response = $ossClient->uploadFile($this->bucket, $savePath, $file->tmpName);

        if (isset($response['info']['http_code']) == 200) {
            $path = $this->getSaveDir() . "/{$file->baseName}.{$file->extension}";
            return [
                'name' => $_POST['fileName'] ?? $file->name, // 文件原名
                'extension' => $file->extension,
                'dir' => $this->getSaveDir(),
                'size' => $file->size,
                'driver' => ImageUploader::DRIVER_OSS,
                'url' => self::getUploadDomain() . trim($path, '/'),
                'path' => $path,
                'base_name' => $file->baseName
            ];
        }

        throw new UploadException("文件上传失败");
    }

    /**
     * 原始路径，不带任何的样式
     * @param $fileName
     * @return String
     */
    public function getOriginUrl($fileName) : string
    {
        try {
            $ossClient = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint);
            // 获取文件的全部元信息。
            $objectMeta = $ossClient->getObjectMeta($this->bucket, $fileName);

            //指定过期时间为10分钟。
            $timeOut = 60;
            $options = [];

            return $ossClient->signUrl($this->bucket, $fileName, $timeOut, "GET", $options);
        } catch (OssException $e) {
            print $e->getMessage($e->getMessage(), __CLASS__ . __FUNCTION__);
            return '';
        }
    }

    public function getSignUrl($fileName, $style = 'sample')
    {
        try {
            $ossClient = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint);
            // 获取文件的全部元信息。
            $objectMeta = $ossClient->getObjectMeta($this->bucket, $fileName);

            //指定过期时间为10分钟。
            $timeOut = 60;
            $options = [];
            if(self::getFiletype($objectMeta['content-type']) == 'image'){
                $process = $this->getOssProcess($style);
                if ($process) {
                    $options = [
                        OssClient::OSS_PROCESS => $this->getOssProcess($style)
                    ];
                };
            }

            //指定过期时间为10分钟。
            /*$timeOut = 1000 * 60 * 10;
            $options = array(
                OssClient::OSS_PROCESS => "style/".$style );*/
            return $ossClient->signUrl($this->bucket, $fileName, $timeOut, "GET", $options);
        } catch (OssException $e) {
            print $e->getMessage($e->getMessage(), __CLASS__ . __FUNCTION__);
            return '';
        }
    }

    /**
     * 获取oss process配置
     * @param $params
     * @return string|void
     */
    private function getOssProcess($params)
    {
        // 应该是oss设置的style
        if (in_array($params, ['iphone', 'sample', 'thumb', 'origin', 'ocr'])) {
            return "style/{$params}";
        }

        // 参数不对就给个默认值
        return "style/iphone";
    }

    public static function getFiletype($mime = ''){
        $mime_arr = explode('/', $mime);
        switch($mime_arr[0]){
            case 'text': $str = $mime_arr[1] == 'html' ? 'html' : 'text';break;
            case 'image': $str = 'image';break;
            case 'audio': $str = 'audio';break;
            case 'video': $str = 'video';break;
            case 'application':
                switch ($mime_arr[1]){
                    case 'pdf': $str = 'pdf';break;
                    case 'x-zip-compressed': $str = 'zip';break;
                    case 'octet-stream': $str = 'gdocs';break;
                    default: $str = 'office';break;
                }
                break;
        }
        return $str;
    }

    /**
     * Notes: 下载OSS文件到本地
     * User: llbjj
     * DateTime: 2021/10/18 11:03
     *
     * @param $uploadFile 文件路径
     * @param $localFile    本地路径
     */
    public function downOssFile($uploadFile, $localFile){
        try {
            $ossClient = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint);

            $options = array(
                OssClient::OSS_FILE_DOWNLOAD => $localFile
            );
            $fileContent = $ossClient->getObject($this->bucket, $uploadFile);

            $filePath = dirname($localFile);
            if(!file_exists($filePath)){
                mkdir($filePath, 0777, true);
            }
            $fileHandle = fopen($localFile, 'w+');
            fwrite($fileHandle, $fileContent);
            fclose($fileHandle);
        } catch(OssException $e) {
            printf(__FUNCTION__ . ": FAILED\n");
            printf($e->getMessage() . "\n");
            return;
        }
    }

    /**
     * @throws OssException
     * @throws RequestCore_Exception
     */
    public function downloadOnline($filePath, string $fileName = '', bool $isSigned = true) {
        $ossClient   = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint);

        $fileName = $fileName ?: basename($filePath);
        $fileName = rawurlencode($fileName);
        $tmpFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $fileName;

        mkdir(pathinfo($tmpFile)['dirname'], 0777, true);

        if ($isSigned) {
            // 验证签名地址是否正确
            $this->validSignedUrl($filePath);
            file_put_contents($tmpFile, file_get_contents($filePath));
        }else {
            // 下载文件到临时目录，方便下面的流式下载
            $ossDownloadOptions = [
                OssClient::OSS_FILE_DOWNLOAD => $tmpFile,
            ];

            $ossClient->getObject($this->bucket, $filePath, $ossDownloadOptions);
        }

        ob_clean();
        flush();
        // 处理文件内容，例如设置header进行下载
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $fileName . '";');
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public'); // 允许下载
        header('Content-Length: ' . filesize($tmpFile));
        // 开始流式读取临时文件
        $fp = fopen($tmpFile, 'rb');
        if ($fp) {
            while (!feof($fp)) {
                echo fread($fp, 8192);
                ob_end_flush();
                flush();
            }
            fclose($fp);
        }
        // 最后删除文件，释放空间
        unlink($tmpFile);
        die;
    }

    public static function getUploadDomain(): string
    {
        if (defined('IS_UNIT_TEST')) {
            return "http://local.com/common/showPic?path=" ;
        }
        return "{$_SERVER['REQUEST_SCHEME']}://{$_SERVER['HTTP_HOST']}/common/showPic?path=" ;
    }

    public function validSignedUrl(string $signedUrl) : bool
    {
        $endpoint = str_ireplace('://', "://{$this->bucket}.", $this->endpoint);

        if(strpos($signedUrl, $endpoint) === false){
            throw new UploadException('无效的签名地址');
        }
        return true;
    }
}