
// 








namespace SafeCampus.System;

/// <summary>
/// <inheritdoc cref="IFileService"/>
/// </summary>
public class FileService : DbRepository<SysFile>, IFileService
{
    private readonly IConfigService _configService;

    public FileService(IConfigService configService)
    {
        _configService = configService;
    }

    /// <inheritdoc/>
    public async Task<SqlSugarPagedList<SysFile>> Page(FilePageInput input)
    {
        var query = Context.Queryable<SysFile>().WhereIF(!string.IsNullOrEmpty(input.Engine), it => it.Engine == input.Engine)//根据关键字查询
            .WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Name.Contains(input.SearchKey))//根据关键字查询
            .OrderByIF(!string.IsNullOrEmpty(input.SortField), $"{input.SortField} {input.SortOrder}")//排序
            .OrderBy(it => it.Id);
        var pageInfo = await query.ToPagedListAsync(input.PageNum, input.PageSize);//分页
        return pageInfo;
    }

    /// <inheritdoc/>
    public async Task<long> UploadFile(string engine, IFormFile file)
    {
        return await StorageFile(engine, file);
    }

    /// <inheritdoc/>
    public async Task Delete(BaseIdListInput input)
    {
        var ids = input.Ids;//获取ID
        await DeleteByIdsAsync(ids.Cast<object>().ToArray());//根据ID删除数据库
    }

    /// <inheritdoc/>
    public FileStreamResult GetFileStreamResult(string path, string fileName, bool isPathFolder = false)
    {
        if (isPathFolder) path = path.CombinePath(fileName);
        fileName = HttpUtility.UrlEncode(fileName, Encoding.GetEncoding("UTF-8"));//文件名转utf8不然前端下载会乱码
        //文件转流
        var result = new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream")
        {
            FileDownloadName = fileName
        };
        return result;
    }

    public async Task<FileStreamResult> GetFileStreamResultFromMinio(string objectName, string fileName)
    {
        var minioService = App.GetService<MinioUtils>();
        var stream = await minioService.DownloadFileAsync(objectName);

        fileName = HttpUtility.UrlEncode(fileName, Encoding.GetEncoding("UTF-8"));//文件名转utf8不然前端下载会乱码
        //文件转流
        var result = new FileStreamResult(stream, "application/octet-stream")
        {
            FileDownloadName = fileName
        };
        return result;
    }

    /// <inheritdoc/>
    public FileStreamResult GetFileStreamResult(byte[] byteArray, string fileName)
    {
        fileName = HttpUtility.UrlEncode(fileName, Encoding.GetEncoding("UTF-8"));//文件名转utf8不然前端下载会乱码
        //文件转流
        var result = new FileStreamResult(new MemoryStream(byteArray), "application/octet-stream")
        {
            FileDownloadName = fileName
        };
        return result;
    }

    /// <inheritdoc/>
    public async Task<FileStreamResult> Download(BaseIdInput input)
    {
        var devFile = await GetByIdAsync(input.Id);
        if (devFile != null)
        {
            if (devFile.Engine == SysDictConst.FILE_ENGINE_LOCAL)
                return GetFileStreamResult(devFile.StoragePath, devFile.Name);
            if (devFile.Engine == SysDictConst.FILE_ENGINE_MINIO)
                return await GetFileStreamResultFromMinio(devFile.ObjName, devFile.Name);
            return null;
        }
        return null;
    }

    #region 方法

    /// <summary>
    /// 存储文件
    /// </summary>
    /// <param name="engine"></param>
    /// <param name="file"></param>
    private async Task<long> StorageFile(string engine, IFormFile file)
    {
        var bucketName = string.Empty;// 存储桶名称
        var storageUrl = string.Empty;// 定义存储的url，本地文件返回文件实际路径，其他引擎返回网络地址
        var objName = string.Empty;// 定义存储的url，本地文件返回文件实际路径，其他引擎返回网络地址

        var objectId = CommonUtils.GetSingleId();//生成id

        switch (engine)
        {
            //存储本地
            case SysDictConst.FILE_ENGINE_LOCAL:
                bucketName = "defaultBucketName";// 存储桶名称
                storageUrl = await StorageLocal(objectId, file);
                break;
            //存储本地
            case SysDictConst.FILE_ENGINE_MINIO:
                var config = await _configService.GetByConfigKey(CateGoryConst.CONFIG_FILE_MINIO, SysConfigConst.FILE_MINIO_DEFAULT_BUCKET_NAME);
                if (config != null)
                {
                    bucketName = config.ConfigValue;// 存储桶名称
                    (objName, storageUrl) = await StorageMinio(objectId, file);
                }
                break;

            default:

                throw Oops.Bah("不支持的文件引擎");
        }
        var fileSizeKb = (long)(file.Length / 1024.0);// 文件大小KB
        var fileSuffix = Path.GetExtension(file.FileName).ToLower();// 文件后缀
        var devFile = new SysFile
        {
            Id = objectId,
            Engine = engine,
            Bucket = bucketName,
            Name = file.FileName,
            Suffix = fileSuffix.Split(".")[1],
            ObjName = objName,
            SizeKb = fileSizeKb,
            SizeInfo = GetSizeInfo(fileSizeKb),
            StoragePath = storageUrl
        };
        if (engine != CateGoryConst.CONFIG_FILE_LOCAL)//如果不是本地，设置下载地址
        {
            devFile.DownloadPath = storageUrl;
        }
        //如果是图片,生成缩略图
        if (IsPic(fileSuffix))
        {
            //$"data:image/png;base64," + imgByte;
            await using var fileStream = file.OpenReadStream();//获取文件流
            var image = SKImage.FromEncodedData(fileStream);//获取图片
            var bmp = SKBitmap.FromImage(image);
            var thubnail = bmp.GetPicThumbnail(100, 100);//压缩图片
            var thubnailBase64 = ImageUtil.ImgToBase64String(thubnail);//转base64
            devFile.Thumbnail = "data:image/png;base64," + thubnailBase64;
        }
        await InsertAsync(devFile);
        return objectId;
    }

    /// <summary>
    /// 存储本地文件
    /// </summary>
    /// <param name="fileId"></param>
    /// <param name="file"></param>
    private async Task<string> StorageLocal(long fileId, IFormFile file)
    {
        string uploadFileFolder;
        var configKey = string.Empty;
        //判断是windows还是linux
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            configKey = SysConfigConst.FILE_LOCAL_FOLDER_FOR_UNIX;//Linux
        }
        else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            configKey = SysConfigConst.FILE_LOCAL_FOLDER_FOR_WINDOWS;//Windows
        }
        //获取路径配置
        var config = await _configService.GetByConfigKey(CateGoryConst.CONFIG_FILE_LOCAL, configKey);
        if (config != null)
        {
            uploadFileFolder = config.ConfigValue;//赋值路径
            var now = DateTime.Now.ToString("d");
            var filePath = Path.Combine(uploadFileFolder, now);
            if (!Directory.Exists(filePath))//如果不存在就创建文件夹
                Directory.CreateDirectory(filePath);
            var fileSuffix = Path.GetExtension(file.FileName).ToLower();// 文件后缀
            var fileObjectName = $"{fileId}{fileSuffix}";//存储后的文件名
            var fileName = Path.Combine(filePath, fileObjectName);//获取文件全路局
            fileName = fileName.Replace("\\", "/");//格式化一系
            //存储文件
            using (var stream = File.Create(Path.Combine(filePath, fileObjectName)))
            {
                await file.CopyToAsync(stream);
            }
            return fileName;
        }
        throw Oops.Oh("文件存储路径未配置");
    }

    /// <summary>
    /// 存储到minio
    /// </summary>
    /// <param name="fileId"></param>
    /// <param name="file"></param>
    /// <returns></returns>
    private async Task<(string objName, string downloadUrl)> StorageMinio(long fileId, IFormFile file)
    {
        var minioService = App.GetService<MinioUtils>();
        var now = DateTime.Now.ToString("d");
        var fileSuffix = Path.GetExtension(file.FileName).ToLower();// 文件后缀
        var fileObjectName = $"{now}/{fileId}{fileSuffix}";//存储后的文件名
        var downloadUrl = await minioService.PutObjectAsync(fileObjectName, file);
        return (fileObjectName, downloadUrl);
    }

    /// <summary>
    /// 获取文件大小
    /// </summary>
    /// <param name="fileSizeKb"></param>
    /// <returns></returns>
    private string GetSizeInfo(long fileSizeKb)
    {
        var b = fileSizeKb * 1024;
        const int mb = 1024 * 1024;
        const int kb = 1024;
        if (b / mb >= 1)
        {
            return Math.Round(b / (float)mb, 2) + "MB";
        }

        if (b / kb >= 1)
        {
            return Math.Round(b / (float)kb, 2) + "KB";
        }
        if (b == 0)
        {
            return "0B";
        }
        return null;
    }

    /// <summary>
    /// 判断是否是图片
    /// </summary>
    /// <param name="suffix">后缀名</param>
    /// <returns></returns>
    private bool IsPic(string suffix)
    {
        //图片后缀名列表
        var pics = new[]
        {
            ".png", ".bmp", ".gif", ".jpg", ".jpeg", ".psd"
        };
        if (pics.Contains(suffix))
            return true;
        return false;
    }

    #endregion 方法
}
