using Infrastructure.Extensions;
using Infrastructure.Helpers;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Minio;
using Minio.DataModel;
using Minio.DataModel.Args;
using Minio.Exceptions;
using System.IO;

namespace Infrastructure.CloudSdk.minio;

public class MinioService
{
    private IMinioClient _minioClient;
    public string _bucketName;
    public string endPoint;
    public bool UseSSL;
    public string AccessKey;
    public string SecretKey;

    public MinioService()
    {
        InitializeMinIOClient();
        //EnsureBucketExistsAsync(_bucketName).Wait();
    }

    private void InitializeMinIOClient()
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile(
                $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development"}.json",
                optional: true)
            .AddEnvironmentVariables();
        // 构建配置
        var configuration = builder.Build();
        _bucketName = configuration["Minio:BucketName"];
        endPoint = configuration["Minio:Endpoint"];
        UseSSL = configuration["Minio:UseSSL"].ToBool();
        AccessKey = configuration["Minio:AccessKey"];
        SecretKey = configuration["Minio:SecretKey"];
        _minioClient = new MinioClient()
            .WithEndpoint(endPoint)
            .WithCredentials(AccessKey, SecretKey)
            .Build();
    }

    /// <summary>
    /// 创建存储桶（如果不存在）
    /// </summary>
    public async Task CreateBucketIfNotExists(string _bucketName)
    {
        var existsArgs = new BucketExistsArgs().WithBucket(_bucketName);
        bool found = await _minioClient.BucketExistsAsync(existsArgs);
        if (!found)
        {
            var makeArgs = new MakeBucketArgs().WithBucket(_bucketName);
            await _minioClient.MakeBucketAsync(makeArgs);
            Console.WriteLine($"Bucket {_bucketName} created.");
        }
    }

    public async Task<string> GetMetaObject(string objectName, string bucketName)
    {
        if (string.IsNullOrEmpty(bucketName))
        {
            bucketName = _bucketName;
        }

        if (objectName.StartsWith("http"))
        {
            objectName = objectName.Replace($"http://{endPoint}/{_bucketName}/", "");
        }

        var statArgs = new StatObjectArgs()
            .WithBucket(bucketName)
            .WithObject(objectName);
        var meta = await _minioClient.StatObjectAsync(statArgs);
        var md5 = meta.MetaData["md5"];
        return md5; // 
    }

    public async Task EnsureBucketExistsAsync(string bucketName)
    {
        var existsArgs = new BucketExistsArgs().WithBucket(bucketName);
        var x = await _minioClient.BucketExistsAsync(existsArgs);
        Console.WriteLine($" {bucketName} exist status: " + x);
        // 如果存储桶不存在，则创建存储桶
        if (!x)
        {
            var makeArgs = new MakeBucketArgs().WithBucket(bucketName);
            await _minioClient.MakeBucketAsync(makeArgs);
        }
    }


    /// <summary>
    /// 下载文件
    /// </summary>
    public async Task DownLoadObject(string bucketName, string objectKey, string localDir, string objectETag,
        CancellationToken token = default)
    {
        var index = objectKey.LastIndexOf("/", StringComparison.Ordinal);
        if (index > 0)
        {
            var dir = Path.Combine(localDir, objectKey.Substring(0, index));
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
        }

        var localPath = Path.Combine(localDir, objectKey.Replace('/', Path.DirectorySeparatorChar));
        var getArgs = new GetObjectArgs()
            .WithBucket(string.IsNullOrEmpty(bucketName) ? _bucketName : bucketName)
            .WithObject(objectKey)
            .WithFile(localPath);
        var stat = await _minioClient.GetObjectAsync(getArgs, token);
    }

    /// <summary>
    /// 列出所有对象
    /// </summary>
    public async Task<IAsyncEnumerable<Item>> ListAllObject(string bucketName, string prefix, bool recursive,
        CancellationToken token = default)
    {
        // Just list of objects
        // Check whether 'mybucket' exists or not.
        if (string.IsNullOrEmpty(bucketName))
        {
            bucketName = _bucketName;
        }

        var existsArgs = new BucketExistsArgs().WithBucket(bucketName);
        var found = await _minioClient.BucketExistsAsync(existsArgs, token);
        if (found)
        {
            var args = new ListObjectsArgs()
                .WithBucket(bucketName)
                .WithPrefix(prefix)
                .WithRecursive(recursive);
            return _minioClient.ListObjectsEnumAsync(args, token);
        }

        Console.WriteLine("mybucket does not exist");
        throw new Exception("bucket not found");
    }

    /// <summary>
    /// 删除文件
    /// </summary>
    public async Task DeleteFile(string objectName)
    {
        try
        {
            if (objectName.StartsWith("http"))
            {
                objectName = objectName.Replace($"http://{endPoint}/{_bucketName}/", "");
            }

            var deleteargs = new RemoveObjectArgs().WithBucket(_bucketName).WithObject(objectName);
            await _minioClient.RemoveObjectAsync(deleteargs);
            Console.WriteLine($"File {objectName} deleted.");
        }
        catch (MinioException ex)
        {
            Console.WriteLine($"MinIO Exception: {ex.Message}");
        }
    }

    public async Task<string> UploadFile(IFormFile file, string bucketName)
    {
        return await UploadFile(file, bucketName, "");
    }

    public async Task<string> UploadFile(IFormFile file, string bucketName, string prifix)
    {
        try
        {
            if (string.IsNullOrEmpty(bucketName))
            {
                bucketName = _bucketName;
            }

            //判断桶是否存在
            var beArgs = new BucketExistsArgs().WithBucket(bucketName);
            bool found = await _minioClient.BucketExistsAsync(beArgs).ConfigureAwait(false);
            if (!found)
            {
                var mbArgs = new MakeBucketArgs()
                    .WithBucket(bucketName);
                await _minioClient.MakeBucketAsync(mbArgs).ConfigureAwait(false);
            }

            var suffix = Path.GetExtension(file.FileName);
            string objectName;
            if (string.IsNullOrEmpty(prifix))
            {
                 objectName = $"{GenerateId.GenerateOrderNumber()}{suffix}";  
            }
            else
            {
                objectName = $"{prifix}/{GenerateId.GenerateOrderNumber()}{suffix}";
            }

            // 使用内存流上传
            using var stream = new MemoryStream();
            await file.CopyToAsync(stream);
            stream.Position = 0;
            var md5 = Md5.CalculateStreamMd5(stream);
            stream.Position = 0;
            var putArgs = new PutObjectArgs()
                .WithBucket(bucketName)
                .WithObject(objectName)
                .WithStreamData(stream)
                .WithObjectSize(stream.Length)
                .WithHeaders(new Dictionary<string, string>()
                {
                    //x-amz-meta-md5
                    ["X-Amz-Meta-Md5"] = md5
                })
                .WithContentType("application/octet-stream");
            //.WithContentType(file.ContentType);
            var result = await _minioClient.PutObjectAsync(putArgs);
            Console.WriteLine("code :" + result.ResponseStatusCode);
            return "http://" + endPoint + "/" + bucketName + "/" + objectName;
        }
        catch (Exception ex)
        {
            throw new Exception($"上传文件失败: {ex.Message}");
        }
    }

    public async Task<string> GetObjectUrl(string bucket, string myobject)
    {
        PresignedGetObjectArgs args = new PresignedGetObjectArgs()
            .WithBucket(bucket)
            .WithObject(myobject)
            .WithExpiry(60 * 60 * 24);
        var url = await _minioClient.PresignedGetObjectAsync(args);
        return url;
    }

    public async Task PutObjectAsync(string buketName,string tupianName,string objectName,MemoryStream memory) {

        if (string.IsNullOrEmpty(buketName))
        {
            buketName = _bucketName;
        }
        PutObjectArgs args = new PutObjectArgs();
        args.WithBucket(buketName).WithObject(objectName).WithStreamData(memory).WithObjectSize(memory.Length).WithContentType("image/jpeg");
        
        await _minioClient.PutObjectAsync(args);
       
    }
    public  async Task<Stream> GetObjectAsStream(string bucketName, string objectName)
    {

        if (string.IsNullOrEmpty(bucketName))
        {
            bucketName = _bucketName;
        }
        var memoryStream = new MemoryStream();
        try
        {
            await _minioClient.GetObjectAsync(new GetObjectArgs()
                 .WithBucket(bucketName)
                 .WithObject(objectName)
                 .WithCallbackStream(stream =>
                 {
                     stream.CopyTo(memoryStream);
                 }));

            memoryStream.Position = 0; // 重置流位置
            return memoryStream;
        }
        catch (Exception e)
        {
            Console.WriteLine($"Error getting object: {e.Message}");
            return null;
        }
    }

}