using Minio;
using Minio.DataModel.Args;
using Minio.Exceptions;

namespace RobotNet.MapManager.Services;

public class MapEditorStorageRepository
{
    private class MinioOption
    {
        public bool UsingLocal { get; set; }
        public string? Endpoint { get; set; }
        public string? User { get; set; }
        public string? Password { get; set; }
        public string? Bucket { get; set; }
    }

    private readonly IMinioClient MinioClient;
    private readonly MinioOption MinioOptions = new();
    public MapEditorStorageRepository(IConfiguration configuration)
    {
        configuration.Bind("MinIO", MinioOptions);
        MinioClient = new MinioClient()
                         .WithEndpoint(MinioOptions.Endpoint)
                         .WithCredentials(MinioOptions.User, MinioOptions.Password)
                         .WithSSL(false)
                         .Build();
    }

    public async Task<(bool, string)> UploadAsync(string path, string objectName, Stream data, long size, string contentType, CancellationToken cancellationToken)
    {
        try
        {
            if (!MinioOptions.UsingLocal)
            {
                var beArgs = new BucketExistsArgs().WithBucket(MinioOptions.Bucket);
                bool found = await MinioClient.BucketExistsAsync(beArgs, cancellationToken).ConfigureAwait(false);
                if (!found)
                {
                    var mbArgs = new MakeBucketArgs()
                                    .WithBucket(MinioOptions.Bucket);
                    await MinioClient.MakeBucketAsync(mbArgs, cancellationToken).ConfigureAwait(false);
                }
                var getListBucketsTask = await MinioClient.ListBucketsAsync(cancellationToken);
                var putObjectArgs = new PutObjectArgs()
                        .WithBucket(MinioOptions.Bucket)
                        .WithObject($"{path}/{objectName}")
                        .WithObjectSize(size)
                        .WithStreamData(data)
                        .WithContentType(contentType);
                await MinioClient.PutObjectAsync(putObjectArgs, cancellationToken).ConfigureAwait(false);
                return (true, "");
            }

            var mapImageFolder = string.IsNullOrEmpty(MinioOptions.Bucket) ? "MapImages" : MinioOptions.Bucket;
            if (!Directory.Exists(mapImageFolder)) Directory.CreateDirectory(mapImageFolder);
            var folderPath = Path.Combine(mapImageFolder, path);
            if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath);
            var pathLocal = Path.Combine(folderPath, $"{objectName}.png");
            if (File.Exists($"{pathLocal}.bk")) File.Delete($"{pathLocal}.bk");
            if (File.Exists(pathLocal)) File.Move(pathLocal, $"{pathLocal}.bk");
            using (Stream fileStream = new FileStream(pathLocal, FileMode.Create))
            {
                await data.CopyToAsync(fileStream, cancellationToken);
            }
            return (true, "");
        }
        catch (MinioException ex)
        {
            return (false, ex.Message);
        }
    }

    public (bool usingLocal, string url) GetUrl(string path, string objectName)
    {
        try
        {
            if (!MinioOptions.UsingLocal)
            {
                var presignedGetObjectArgs = new PresignedGetObjectArgs()
                        .WithBucket(MinioOptions.Bucket)
                        .WithObject($"{path}/{objectName}")
                        .WithExpiry(60 * 60 * 24);
                var url = MinioClient.PresignedGetObjectAsync(presignedGetObjectArgs);
                url.Wait();
                return (false, url.Result);
            }
            var mapImageFolder = string.IsNullOrEmpty(MinioOptions.Bucket) ? "MapImages" : MinioOptions.Bucket;
            if (Directory.Exists(mapImageFolder))
            {
                var folderPath = Path.Combine(mapImageFolder, path);
                if (Directory.Exists(folderPath))
                {
                    var pathLocal = Path.Combine(folderPath, $"{objectName}.png");
                    if (File.Exists(pathLocal))
                    {
                        return (true, pathLocal);
                    }
                }
            }
            return (true, "");
        }
        catch (MinioException ex)
        {
            return (false, ex.Message);
        }
    }

    public async Task<(bool, string)> DeleteAsync(string path, string objectName, CancellationToken cancellationToken)
    {
        try
        {
            if (!MinioOptions.UsingLocal)
            {
                var removeObjectArgs = new RemoveObjectArgs()
                        .WithBucket(MinioOptions.Bucket)
                        .WithObject($"{path}/{objectName}");
                await MinioClient.RemoveObjectAsync(removeObjectArgs, cancellationToken).ConfigureAwait(false);
                return (true, "");
            }
            var mapImageFolder = string.IsNullOrEmpty(MinioOptions.Bucket) ? "MapImages" : MinioOptions.Bucket;
            if (Directory.Exists(mapImageFolder))
            {
                var folderPath = Path.Combine(mapImageFolder, path);
                if (Directory.Exists(folderPath))
                {
                    var pathLocal = Path.Combine(folderPath, $"{objectName}.png");
                    if (File.Exists(pathLocal)) File.Delete(pathLocal);
                    if (File.Exists($"{pathLocal}.bk")) File.Delete($"{pathLocal}.bk");
                }
            }
            return (true, "");
        }
        catch (MinioException ex)
        {
            return (false, ex.Message);
        }
    }

}