using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Amazon.S3;
using Amazon.S3.Model;
using BMA.EHR.Recurit.Exam.Service.Core;
using BMA.EHR.Recurit.Exam.Service.Data;
using BMA.EHR.Recurit.Exam.Service.Models.Documents;
using BMA.EHR.Recurit.Exam.Service.Responses.Document;
using Elasticsearch.Net;
using Microsoft.EntityFrameworkCore;

namespace BMA.EHR.Recurit.Exam.Service.Services
{
    public class MinIOService
    {
        #region " Fields "

        private readonly ApplicationDbContext _context;
        private readonly IConfiguration _configuration;
        private readonly IWebHostEnvironment _webHostEnvironment;
        private readonly AmazonS3Client _s3Client;
        private string _bucketName = string.Empty;

        #endregion

        #region " Constructors "

        public MinIOService(ApplicationDbContext context,
                            IConfiguration configuration,
                            IWebHostEnvironment webHostEnvironment)
        {
            _context = context;
            _configuration = configuration;
            _webHostEnvironment = webHostEnvironment;

            var config = new AmazonS3Config
            {
                ServiceURL = _configuration["MinIO:Endpoint"],
                ForcePathStyle = true
            };

            _s3Client = new AmazonS3Client(_configuration["MinIO:AccessKey"], _configuration["MinIO:SecretKey"], config);
            this._bucketName = _configuration["MinIO:BucketName"] ?? "bma-recruit";
        }

        public static IConfigurationRoot Configuration
        {
            get
            {
                return new ConfigurationBuilder()
                                  .SetBasePath(Directory.GetCurrentDirectory())
                                  .AddJsonFile("appsettings.json")
                                  .Build();
            }
        }

        #endregion

        #region " Methods "

        public async Task<Document> UploadFileAsync(IFormFile file, string newFileName = "")
        {
            var fileName = "";
            var fileExt = Path.GetExtension(file.FileName);
            if (newFileName != "")
                fileName = $"{newFileName}";
            else
                fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');


            var tmpDir = Path.Combine(_webHostEnvironment.ContentRootPath, "tmp");
            if (!Directory.Exists(tmpDir))
                Directory.CreateDirectory(tmpDir);

            var tmpFile = Path.Combine(tmpDir, $"tmp_{DateTime.Now.ToString("ddMMyyyyHHmmss")}{fileExt}");

            try
            {
                using (var ms = new MemoryStream())
                {
                    var id = Guid.NewGuid();
                    file.CopyTo(ms);
                    var fileBytes = ms.ToArray();
                    System.IO.MemoryStream filestream = new System.IO.MemoryStream(fileBytes);

                    var request = new PutObjectRequest
                    {
                        BucketName = _bucketName,
                        Key = id.ToString("D"),
                        InputStream = filestream,
                        ContentType = file.ContentType,
                        CannedACL = S3CannedACL.PublicRead
                    };

                    await _s3Client.PutObjectAsync(request);

                    // create document object
                    var doc = new Document()
                    {
                        FileName = fileName,
                        FileType = file.ContentType,
                        FileSize = Convert.ToInt32(file.Length),
                        ObjectRefId = id,
                        CreatedDate = DateTime.Now
                    };

                    await _context.Documents.AddAsync(doc);
                    await _context.SaveChangesAsync();

                    return doc;
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                File.Delete(tmpFile);
            }
        }

        public async Task<FileDownloadResponse> DownloadFileAsync(Guid fileId)
        {
            try
            {
                var doc = await _context.Documents.AsQueryable()
                                    .FirstOrDefaultAsync(x => x.Id == fileId);

                if (doc == null)
                    throw new Exception(GlobalMessages.FileNotFoundOnServer);

                using (var memoryStream = new MemoryStream())
                {
                    GetObjectRequest request = new GetObjectRequest
                    {
                        BucketName = _bucketName,
                        Key = doc.ObjectRefId.ToString("D")
                    };

                    using (GetObjectResponse response = await _s3Client.GetObjectAsync(request))
                    {
                        using (Stream responseStream = response.ResponseStream)
                        {
                            responseStream.CopyTo(memoryStream);
                        }
                    }

                    var fileContent = memoryStream.ToArray();

                    return new FileDownloadResponse
                    {
                        FileName = doc.FileName,
                        FileType = doc.FileType,
                        FileContent = fileContent
                    };
                };
            }
            catch
            {
                throw;
            }
        }

        public async Task DeleteFileAsync(Guid fileId)
        {
            try
            {
                var doc = await _context.Documents.AsQueryable()
                                    .FirstOrDefaultAsync(x => x.Id == fileId);

                if (doc == null)
                    throw new Exception(GlobalMessages.FileNotFoundOnServer);
                else
                {
                    DeleteObjectRequest request = new DeleteObjectRequest
                    {
                        BucketName = _bucketName,
                        Key = doc?.ObjectRefId.ToString("D")
                    };

                    // delete from minio
                    await _s3Client.DeleteObjectAsync(request);


                    _context.Documents.Remove(doc);
                    await _context.SaveChangesAsync();
                }
            }
            catch
            {
                throw;
            }
        }

        public async Task<string> ImagesPath(Guid fileId)
        {
            if (fileId == null)
                return "";

            var doc = await _context.Documents.AsQueryable()
                                .FirstOrDefaultAsync(x => x.Id == fileId);

            if (doc == null)
                throw new Exception(GlobalMessages.FileNotFoundOnServer);
            var config = new AmazonS3Config
            {
                ServiceURL = Configuration.GetValue<string>("MinIO:Endpoint"),
                ForcePathStyle = true
            };

            DateTime expires = DateTime.UtcNow.AddHours(6);
            GetPreSignedUrlRequest request = new GetPreSignedUrlRequest
            {
                BucketName = _bucketName,
                Key = doc?.ObjectRefId.ToString("D"),
                Expires = expires,
            };
            string path = _s3Client.GetPreSignedURL(request);

            return path;
        }

        public async Task<string> ImagesPathString(string fileId)
        {
            if (fileId == null)
                return "";

            var config = new AmazonS3Config
            {
                ServiceURL = Configuration.GetValue<string>("MinIO:Endpoint"),
                ForcePathStyle = true
            };

            DateTime expires = DateTime.UtcNow.AddHours(6);
            GetPreSignedUrlRequest request = new GetPreSignedUrlRequest
            {
                BucketName = _bucketName,
                Key = fileId,
                Expires = expires,
            };
            string path = _s3Client.GetPreSignedURL(request);

            return path;
        }

        #endregion
    }
}