<?php
/**
 * Created by PhpStorm.
 * User: ywl
 * Date: 2017/4/14
 * Time: 11:38
 */

namespace App\Common\Services;


use App\Exceptions\ApiException;
use App\Models\BaseModel;
use App\Services\CommonBaseService;
use App\Common\Facades\OssFacade;
use FFMpeg\Coordinate\TimeCode;
use FFMpeg\FFMpeg;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Request;

class UploadFileService extends CommonBaseService
{

    /**
     * 文件mineType类型
     * @var array
     */
    private $mineTypeList = [];

    /**
     * 文件扩展名
     * @var array
     */
    private $fileExt = ['png', 'jpg', 'jpeg', 'gif', 'mp4', 'mp3', 'ico', '3gpp', 'ac3', '3gpp', 'ac3',
        'asf', 'au', 'css', 'csv', 'doc', 'dot', 'dtd', 'dwg', 'dxf', 'gif', 'jp2', 'jpe',
        'mp2', 'mp3', 'mp4', 'mpeg', 'mpg', 'mpp', 'ogg', 'pdf', 'pot', 'pps', 'ppt', 'rtf', 'svf',
        'tif', 'tiff', 'txt', 'wdb', 'wps', 'xhtml', 'xlc', 'xlm', 'xls', 'xlt', 'xlw', 'xml',
        'zip', 'xlsx','docx','webp'];

    /**
     * 文件扩展名
     * @var string
     */
    private $nowFileExt = '';

    /**
     * 文件限制大小,默认20M
     * @var int
     */
    private $limitSize = 1024 * 1024 * 100;

    /**
     * 保存的路径
     * @var string
     */
    private $basePath = './';
    /**
     * 设置文件路径
     * @var string
     */
    private $filePath = '/files';
    /**
     * 上传文件字段
     * @var string
     */
    private $fileField = 'file';

    public function __construct(BaseModel $model)
    {
        parent::__construct($model);
        if(config('app.upload_limit_size')) {
            $this->limitSize = 1024 * 1024 * config('app.upload_limit_size');
        }
    }

    /**
     * 获取本地路径
     * @param $file
     * @return string
     */
    public function getLocalPath($file = '')
    {
        $resourcesPath = 'upload';
        if(config('app.language_path')) {
            if(config('app.language_path') != 'zh-cn') {
                $resourcesPath = config('app.language_path').'/upload';
            }
        }
        return  resource_path($resourcesPath) . $file;
    }

    /**
     * 上传图片文件
     * @param $request
     * @param string $fileField
     * @param array|string $fileExt
     * @param bool $isUploadOss
     * @return array
     */
    public function upload($request, $fileField = 'file', $fileExt = [], $isUploadOss = true,$isReName=false)
    {
        $this->fileField = $fileField;
        if (!empty($fileExt)) {
            $this->setFileExt($fileExt);
        }
        $this->validate($request);
        $result = $this->saveLocal($request,$isReName);
        $localPath=$this->getLocalPath().$result['path'];
        if (strpos($result['mimeType'], 'image') !== false) {
            list($width, $height) = getimagesize($localPath);
            $result['resolution'] = $width . '*' . $height;
        }
        $resourcesPath = '/resources';
        if(config('app.language_path')) {
            if(config('app.language_path') != 'zh-cn') {
                $resourcesPath = $resourcesPath.'/'.config('app.language_path');
            }
        }
        $result['url'] = $request->root() .$resourcesPath. $result['path'];
        $path = $this->getLocalPath() . $this->filePath;
        $useFfmpeg= config('app.USE_FFMPEG');
        if ($useFfmpeg&&!empty($result['mimeType'])&&strpos($result['mimeType'], 'video') !== false) {
            $ffmpeg=FFMpeg::create(array(
                'ffmpeg.binaries'  => config('app.ffmpeg.ffmpeg_binaries'),
                'ffprobe.binaries' => config('app.ffmpeg.ffprobe_binaries'),
                'timeout'          =>config('app.ffmpeg.timeout'), // The timeout for the underlying process
                'ffmpeg.threads'   => config('app.ffmpeg.ffmpeg_threads'),   // The number of threads that FFMpeg should use
            ));
            $picFilename = $this->getFilename($path,'jpg');
            $video = $ffmpeg->open($localPath);

            $fileSavePath=$path.'/'.$picFilename;
            $video->frame(TimeCode::fromSeconds(1))->save($fileSavePath);
            if(file_exists($fileSavePath)){
                $checkFileNameStr=str_replace('.'.$result['ext'],'',$picFilename);
                if (preg_match('/^.*[,\.#%\'\+\*\:;^`\{\}\(\)\[\]\s]/', $checkFileNameStr)) {
                    $picFilename=  preg_replace('/[,\.#%\'\+\*\:;^`\{\}\(\)\[\]\s]/','-', $checkFileNameStr).'.'.$result['ext'];
                }
                $coverOssUrl = OssFacade::uploadToOss($fileSavePath, $picFilename, md5_file($fileSavePath),'share_center',$isReName);
                $result['pic']=$coverOssUrl;//封面图
            }
        }
        if ($isUploadOss) {
            $filename = $result['original_filename'];
            $checkFileNameStr=str_replace('.'.$result['ext'],'',$filename);
            if (preg_match('/^.*[,\.#%\'\+\*\:;^`\{\}\(\)\[\]\s]/', $checkFileNameStr)) {
                $filename=  preg_replace('/[,\.#%\'\+\*\:;^`\{\}\(\)\[\]\s]/','-', $checkFileNameStr).'.'.$result['ext'];
            }
            $ossUrl = OssFacade::uploadToOss($localPath, $filename, md5(file_get_contents($localPath)),'share_center',$isReName);
            $result['url'] = $ossUrl;
            //Log::info(json_encode($result));
            if (!empty($result['mimeType'])&&strpos($result['mimeType'], 'video') !== false) {
                // 视频截图
                $picFilename = $this->getFilename($path,'jpg');
                $fileSavePath = $path.'/'.$picFilename;
                downloadFile($ossUrl.'?x-oss-process=video/snapshot,t_1000,f_jpg,w_800,h_600,m_fast', $fileSavePath);
                if (file_exists($fileSavePath)) {
                    $coverOssUrl = OssFacade::uploadToOss($fileSavePath, $picFilename, md5_file($fileSavePath),'share_center',$isReName);
                    $result['pic']=$coverOssUrl;//封面图
                }
            }
        }
        return $result;
    }

    /**
     * 上传Base64图片
     * @param $request
     * @param string $fileField
     * @param array|string $fileExt
     * @param bool $isUploadOss
     * @return array
     */
    public function uploadImgBase64($data, $fileExt = 'jpg', $host='',$selectFileName='',$isUploadOss = true)
    {
        if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $data, $result)) {
            $type = $result[2];
            if (in_array($type, array('pjpeg', 'jpeg', 'jpg', 'gif', 'bmp', 'png'))) {
                $this->nowFileExt = strtolower($type);
                $path =  $this->filePath;//$this->basePath.$this->filePath;
                $path.="/" .date('md') . "/".uniqid()."/";
                $localPath=$this->getLocalPath().$path;
                if(empty($selectFileName)){
                    $filename = $this->getFilename($localPath);
                }else{
                    $filename=  $selectFileName.'.'.$this->nowFileExt;
                }

                $filepath = $localPath . '/' . $filename;
                if (!file_exists($localPath)) {
                    mkdir($localPath, 0777, true);
                }
                if (file_put_contents($filepath, base64_decode(str_replace($result[1], '', $data)))) {
                    $size = filesize($filepath);
                    $s = getimagesize($filepath);
                    $width = empty($s[0]) ? '' : $s[0];
                    $height = empty($s[1]) ? '' : $s[1];
                    $result = [
                        'path' => $path.$filename,
                        'size' => $size,
                        'filename' => $filename,
                        'original_filename' => $filename,
                        'ext' => $this->nowFileExt,
                        'resolution' => $width . '*' . $height,
                        'url' => $host.'/resources'.$path.$filename,
                        'mimeType' => 'image/' . $this->nowFileExt
                    ];
                    if ($isUploadOss) {
                        $ossUrl = OssFacade::uploadToOss($filepath, $filename, md5(file_get_contents($filepath)));
                        $result['url'] = $ossUrl;
                    }
                    return $result;
//                    echo '图片上传成功</br><img src="' .$img_path. '">';
                } else {
                    throw new ApiException(1101);

                }
            } else {
                //文件类型错误
                throw new ApiException(1101);
            }

        } else {
            //文件错误
            throw new ApiException(1101);
        }
        return false;
    }

    /**
     * 上传文本转为文件
     * @param $request
     * @param string $fileField
     * @param array|string $fileExt
     * @param bool $isUploadOss
     * @return array
     */
    public function uploadTextToFile($data, $fileExt = '', $isUploadOss = true)
    {
        if (!empty($fileExt)) {
            $this->nowFileExt = strtolower($fileExt);
            $path = $this->getLocalPath() . $this->filePath;
            $filename = $this->getFilename($path);
            $filepath = $path . '/' . $filename;
            if (!file_exists($path)) {
                mkdir($path, 0777, true);
            }
            if (file_put_contents($filepath, $data)) {
                $size = filesize($filepath);
                $s = getimagesize($filepath);
                $result = [
                    'path' => $filepath,
                    'size' => $size,
                    'filename' => $filename,
                    'original_filename' => $filename,
                    'ext' => $this->nowFileExt,
                    'url' => ''
                ];
                if ($isUploadOss) {
                    $ossUrl = OssFacade::uploadToOss($filepath, $filename, md5(file_get_contents($filepath)));
                    $result['url'] = $ossUrl;
                }
                return $result;
            }
        }
        return false;
    }

    /**
     * 设置扩展名
     * @param array $fileExt
     * @return $this
     */
    public function setFileExt($fileExt)
    {
        if (empty($fileExt)) {
            return;
        }
        if (!is_array($fileExt)) {
            $fileExt = explode(',', $fileExt);
        }
        $this->fileExt = $fileExt;
        return $this;
    }

    /**
     * 上传验证
     * @param $request
     * @return bool
     * @throws ApiException
     */
    private function validate($request)
    {
        $fileInfo = $request->file($this->fileField);
//        if (!$request->hasFile($this->fileField) || !$fileInfo->isValid()) {
//            throw new ApiException(30002);
//        }
        if (!$this->checkFileExt($fileInfo)) {
            throw new ApiException(1101);
        }
        if (!$this->checkLimitSize($fileInfo)) {
            $limit = floor($this->limitSize / (1024 * 1024)) . 'M';
            throw new ApiException(1102, ['limit' => $limit]);
        }
        $this->checkDirectory($this->filePath);
        return true;
    }

    /**
     * 检查文件扩展名
     * @param $fileInfo
     * @return bool
     */
    private function checkFileExt($fileInfo)
    {
        $fileType = strtolower($fileInfo->getClientOriginalExtension());
        if (!$fileType || !in_array($fileType, $this->fileExt)) {
            return false;
        }
        $this->nowFileExt = strtolower($fileType);
        return true;
    }

    /**
     * 验证文件大小
     * @param $fileInfo
     * @return bool
     */
    private function checkLimitSize($fileInfo)
    {
        $clientSize = $fileInfo->getSize();
        if ($clientSize > $this->limitSize) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 检验文件夹，不存在则创建
     * @param $path
     */
    private function checkDirectory($path)
    {
        $folders = explode('/', $path);
        $basePath = $this->getLocalPath();//$this->basePath;
        foreach ($folders as $item) {
            if (empty($item)) {
                continue;
            }
            if (strrpos($basePath, '/') !== strlen($basePath) - 1) {
                $basePath .= '/';
            }
            $basePath .= $item;
            $this->makeDir($basePath);
        }
    }

    /**
     * 新增文件目录
     * @param $path
     * @return mixed
     */
    private function makeDir($path)
    {
        if (is_dir($path)) {
            return $path;
        } else {
            mkdir($path, 0777, true);
            return $path;
        }
    }

    /**
     * 上传保存到本地
     * @param $request
     * @return array
     */
    private function saveLocal($request,$isReName=false)
    {
        $fileInfo = $request->file($this->fileField);

        $path = $this->filePath;//$this->basePath.$this->filePath;
        $path.="/" .date('md') . "/";
        $OldFilename = $fileInfo->getClientOriginalName();
        if($isReName){
            $localPath=$this->getLocalPath().$path;
            $filename = $this->getFilename($localPath);
        }else{
            $path.= uniqid()."/";
            $localPath=$this->getLocalPath().$path;
            $filename= $OldFilename;
        }
        $fileSize = $fileInfo->getSize();
        $mimeType = $fileInfo->getMimeType();
        $fileInfo->move($localPath, $filename);
//        Log::info($this->filePath.'/'.$filename);
        return [
            'path' => $path.$filename,
            'size' => $fileSize,
            'filename' => $filename,
            'original_filename' => $OldFilename,
            'ext' => $this->nowFileExt,
            'mimeType' => $mimeType
        ];
    }

    /**
     * 获取文件名
     * @param $path
     * @return string
     */
    private function getFilename($path,$ext='')
    {
        $suffix=empty($ext)? $this->nowFileExt:$ext;
        $fileName = date('YmdHis') . mt_rand(100, 999) . '.' . $suffix;
        if (file_exists($path .'/'. $fileName)) {
            return $this->getFileName($path,$ext);
        }
        return $fileName;
    }

    /**
     * 设置文件路径
     * @param string $path
     * @return $this
     */
    public function setFilePath($path)
    {
        $this->filePath = $path;
        return $this;
    }

    /**
     * 设置文件大小
     * @param string $maxSize
     * @return $this
     */
    public function setMaxSize($maxSize = '')
    {
        if ($maxSize && is_numeric($maxSize)) {
            $this->limitSize = $maxSize;
        }
        return $this;
    }

    /**
     * 设置文件mineType
     * @param array $mineType
     * @return $this
     */
    public function setMineType($mineType = [])
    {
        if ($mineType) {
            $this->mineTypeList = $mineType;
        }
        return $this;
    }

    /**
     * 上传远程文件
     * */
    public function uploadRemoteImg($remoteUrl, $isReName = false)
    {
        $ossUrl = '';
        $ext = getExtFromUrl($remoteUrl);
        if (!empty($remoteUrl) && in_array($ext, $this->fileExt)) {
            $path = $this->filePath;
            $path .= "/" . date('md') . "/";
            $OldFilename = basename($remoteUrl);
            //如果包含中文则重命名
            if(preg_match('/[\x{4e00}-\x{9fa5}]/u', $OldFilename)) {
                $py = app()->make(PinYinService::class);
                $newName = strtolower($py->getAllPY($OldFilename));
                if(!empty($newName)) {
                    $OldFilename = preg_replace('/[^a-zA-Z0-9_u4e00-u9fa5]/', '-', $newName).'.'.$ext;
                } else {
                    $isReName = true;
                }
            }
            if ($isReName) {
                $localPath = $this->getLocalPath().$this->filePath;
                $path .= uniqid() . "/";
                $filename = $this->getFilename($localPath, $ext);
            } else {
                $path .= uniqid() . "/";
                $filename = $OldFilename;
            }
            $realPath = $this->getLocalPath().$path;
            if (!file_exists($realPath)) {
                mkdir($realPath, 0777, true);
            }
            $saveToUrl = $realPath . $filename;
            if (str_contains($remoteUrl, 'data:image/png;')) {
                try {
                    // 分离出数据
                    list($type, $data) = explode(';', $remoteUrl);
                    list(, $data) = explode(',', $data);
                    // 将Base64字符串解码成二进制数据
                    $image = base64_decode($data);
                    // 指定文件名和保存路径
                    // 保存文件
                    file_put_contents($saveToUrl, $image);
                    $ossUrl = $path.$filename;
                } catch (\Exception $e) {

                }
            } else {
                if ((strpos($remoteUrl, "http://") !== false || strpos($remoteUrl, "https://") !== false)) {
                    $remoteUrl = preg_replace('/\?v=.*/', '', $remoteUrl);
                    $saveToUrl = mb_convert_encoding($saveToUrl, "UTF-8");
                    downloadRemoteFile($remoteUrl, $saveToUrl);
                    $ossUrl = $path.$filename;
                }
            }
        }
        if ($ossUrl) {
            $ossUrl = Request::root() . '/resources' . $ossUrl;
        }
        return $ossUrl;
    }

    /**
     * 上传图片文件
     * @param $request
     * @param string $fileField
     * @param array|string $fileExt
     * @param bool $isUploadOss
     * @return array
     */
    public function uploadCustomImage($request, $fileField = 'file', $fileExt = [], $isUploadOss = true, $isReName = false)
    {
        $this->fileField = $fileField;
        if (!empty($fileExt)) {
            $this->setFileExt($fileExt);
        }
        $this->validateCustom($request);
        $result = $this->saveCustomLocal($request);
        $resourcesPath = '/resources';
        if(config('app.language_path')) {
            if(config('app.language_path') != 'zh-cn') {
                $resourcesPath = $resourcesPath.'/'.config('app.language_path');
            }
        }
        $result['url'] = $request->root() . $resourcesPath . $result['path'];
        return $result;
    }

    private function validateCustom($request)
    {
        $fileInfo = $request->file($this->fileField);
        if (!$this->checkFileExt($fileInfo)) {
            throw new ApiException(1101);
        }
        $clientSize = $fileInfo->getSize();
        $limitSize = 1024 * 1024 * 5;
        if ($clientSize > $limitSize) {
            throw new ApiException(1102, ['limit' => 5]);
        }
        $this->checkDirectory($this->filePath);
        return true;
    }

    private function saveCustomLocal($request)
    {
        $fileInfo = $request->file($this->fileField);
        $path = '/custom_files';//$this->basePath.$this->filePath;
        $path .= "/" . date('Ymd') . "/".strtolower(uniqid())."/";
        $OldFilename = $fileInfo->getClientOriginalName();
        $localPath = $this->getLocalPath() . $path;
        $filename = $this->getFilename($localPath);
        $fileSize = $fileInfo->getSize();
        $mimeType = $fileInfo->getMimeType();
        $fileInfo->move($localPath, $filename);
        return [
            'path' => $path . $filename,
            'size' => $fileSize,
            'filename' => $filename,
            'original_filename' => $OldFilename,
            'ext' => $this->nowFileExt,
            'mimeType' => $mimeType
        ];
    }
}
