using ImageMagick;
using Lada.Framework.Data.Enums;
using Lada.Framework.DTO;
using Lada.Framework.DTO.Results.Media;
using Lada.Framework.DTO.Models.Media;
using Lada.Framework.Infrastructures.AppSettings;
using Lada.Framework.Data.Repositories;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Lada.Framework.Infrastructures.Helpers;

namespace Lada.Framework.Infrastructures.Services
{
    public interface IImageService
    {
        /// <summary>
        /// Xử lý và lưu ảnh với tối ưu hóa, trả về URL
        /// </summary>
        /// <param name="file">File ảnh cần xử lý</param>
        /// <param name="imageType">Loại ảnh (để tổ chức thư mục)</param>
        /// <returns>Kết quả xử lý ảnh với URL</returns>
        Task<ImageProcessResult> ProcessImageAsync(IFormFile file, ImageType imageType);

        /// <summary>
        /// Xử lý và lưu ảnh với metadata vào database (optional)
        /// </summary>
        /// <param name="file">File ảnh cần xử lý</param>
        /// <param name="options">Tùy chọn lưu ảnh và metadata</param>
        /// <returns>Kết quả xử lý ảnh với metadata</returns>
        Task<ImageProcessResult> ProcessAndSaveAsync(IFormFile file, ImageSaveOptions options);

        /// <summary>
        /// Xử lý nhiều ảnh cùng lúc
        /// </summary>
        /// <param name="files">Danh sách file ảnh</param>
        /// <param name="imageType">Loại ảnh</param>
        /// <returns>Danh sách kết quả xử lý</returns>
        Task<List<ImageProcessResult>> ProcessMultipleAsync(List<IFormFile> files, ImageType imageType);

        /// <summary>
        /// Chuyển đổi SystemPath thành URL hiển thị
        /// </summary>
        /// <param name="systemPath">Đường dẫn hệ thống</param>
        /// <returns>URL hiển thị</returns>
        Task<string> GetUrlBySystemPath(string systemPath);

        /// <summary>
        /// Validate ảnh bằng header validation
        /// </summary>
        /// <param name="file">File cần validate</param>
        /// <returns>True nếu là ảnh hợp lệ</returns>
        Task<bool> ValidateImageAsync(IFormFile file);
    }

    public class ImageService : IImageService
    {
        private readonly MediaProcessingSettings _settings;
        private readonly IAppConfigService? _appConfigService;
        private readonly IRepository<Data.Domains.Medias.Media>? _mediaRepository;

        public ImageService(IOptions<MediaProcessingSettings> settings, IAppConfigService? appConfigService = null, IRepository<Data.Domains.Medias.Media>? mediaRepository = null)
        {
            _settings = settings.Value;
            _appConfigService = appConfigService;
            _mediaRepository = mediaRepository;
        }

        public async Task<ImageProcessResult> ProcessImageAsync(IFormFile file, ImageType imageType)
        {
            var result = new ImageProcessResult();

            try
            {
                // 1. Validate ảnh
                if (!await ValidateImageAsync(file))
                {
                    result.Result = Result.Failed;
                    result.Message = "File tải lên không phải là ảnh hợp lệ";
                    return result;
                }

                // 2. Lưu file gốc tạm thời
                var originalFolderPath = GetImageFolderPath(imageType);
                var originalFileName = GenerateFileName(file);
                var savedOriginalFilePath = await SaveToDiskAsync(file, originalFileName, originalFolderPath);

                // 3. Xử lý và tối ưu ảnh
                var optimizedFilePath = await OptimizeImageAsync(savedOriginalFilePath);

                // 4. Lấy thông tin ảnh sau khi xử lý
                var fileInfo = new FileInfo(optimizedFilePath);
                result.FileSize = fileInfo.Length;
                result.Format = Path.GetExtension(optimizedFilePath).TrimStart('.').ToUpper();

                // 5. Tạo URL
                var url = await GenerateImageUrl(optimizedFilePath);
                result.Url = url;
                result.SystemPath = optimizedFilePath;
                result.Result = Result.Success;

                // 5. Xóa file gốc để tối ưu storage
                RemoveFile(savedOriginalFilePath);
            }
            catch (Exception ex)
            {
                result.Result = Result.SystemError;
                result.Message = $"Lỗi xử lý ảnh: {ex.Message}";
            }

            return result;
        }

        public async Task<ImageProcessResult> ProcessAndSaveAsync(IFormFile file, ImageSaveOptions options)
        {
            var result = await ProcessImageAsync(file, options.ImageType);

            if (result.Result == Result.Success && options.SaveToDatabase && _mediaRepository != null)
            {
                try
                {
                    // Tạo metadata để lưu vào database
                    var mediaEntity = CreateMediaEntity(result, options);
                    var insertResult = await _mediaRepository.InsertAsync(mediaEntity);

                    // InsertAsync trả về số lượng records inserted, không phải ID
                    // ID được tự động generate và set vào entity sau khi insert
                    if (insertResult > 0)
                    {
                        result.MediaId = mediaEntity.Id; // Lấy ID từ entity sau khi insert
                    }
                    else
                    {
                        result.Message += " | Không thể lưu vào database";
                    }
                }
                catch (Exception ex)
                {
                    result.Message += $" | Lỗi lưu database: {ex.Message}";
                }
            }

            return result;
        }

        public async Task<List<ImageProcessResult>> ProcessMultipleAsync(List<IFormFile> files, ImageType imageType)
        {
            var results = new List<ImageProcessResult>();

            if (files != null && files.Any())
            {
                foreach (var file in files)
                {
                    var result = await ProcessImageAsync(file, imageType);
                    results.Add(result);
                }
            }

            return results;
        }

        public async Task<string> GetUrlBySystemPath(string systemPath)
        {
            try
            {
                string domain;
                string rootPath;

                if (_appConfigService != null)
                {
                    domain = await _appConfigService.GetConfigValueAsync("Media.Domain") ?? _settings.Domain;
                    rootPath = await _appConfigService.GetConfigValueAsync("Media.RootPath") ?? _settings.RootPath ?? throw new InvalidOperationException("RootPath not configured");
                }
                else
                {
                    domain = _settings.Domain;
                    rootPath = _settings.RootPath ?? throw new InvalidOperationException("RootPath not configured");
                }

                if (!string.IsNullOrEmpty(domain) && !string.IsNullOrEmpty(rootPath))
                {
                    // Normalize paths để đảm bảo consistent format
                    var normalizedSystemPath = systemPath.Replace("\\", "/");
                    var normalizedRootPath = rootPath.Replace("\\", "/").TrimEnd('/');

                    // Tạo relative path
                    var relativePath = normalizedSystemPath.Replace(normalizedRootPath + "/", "");

                    // Tạo URL
                    return $"{domain.TrimEnd('/')}/{relativePath}";
                }
            }
            catch (Exception)
            {
                // Log error if needed
            }

            return string.Empty;
        }

        public async Task<bool> ValidateImageAsync(IFormFile file)
        {
            if (file == null || file.Length == 0)
                return false;

            try
            {
                using var stream = file.OpenReadStream();
                var headerBytes = new byte[4];
                await stream.ReadAsync(headerBytes, 0, 4);

                return ValidateImageHeader(headerBytes, file.FileName);
            }
            catch
            {
                return false;
            }
        }

        #region Private Methods

        private string GetImageFolderPath(ImageType imageType)
        {
            var now = DateTimeOffset.Now;
            var imageTypeName = Enum.GetName(imageType) ?? "General";
            return $"Images\\{imageTypeName}\\{now.Year}\\{now.Month}\\{now.Day}\\";
        }

        private string GenerateFileName(IFormFile file)
        {
            var fileNameWithoutExt = Path.GetFileNameWithoutExtension(file.FileName);
            var fileExt = Path.GetExtension(file.FileName).ToLower();
            var fileName = fileNameWithoutExt.Length <= 90 ? fileNameWithoutExt : fileNameWithoutExt.Substring(0, 90);
            return $"{fileName}-{DateTimeOffset.Now.Ticks}{fileExt}";
        }

        private async Task<string> SaveToDiskAsync(IFormFile file, string fileName, string relativeFolder)
        {
            // Lấy RootPath từ AppConfigService (cached) hoặc fallback về settings
            var rootPath = await _appConfigService.GetConfigValueAsync("Media.RootPath") ?? _settings.RootPath ?? throw new InvalidOperationException("RootPath not configured");
            var fullFolderPath = Path.Combine(rootPath, relativeFolder);
            var filePath = Path.Combine(fullFolderPath, fileName);

            if (!Directory.Exists(fullFolderPath))
            {
                Directory.CreateDirectory(fullFolderPath);
            }

            var fileInfo = new FileInfo(filePath);
            if (fileInfo.Exists)
            {
                fileInfo.Delete();
            }

            using var fileStream = new FileStream(filePath, FileMode.Create);
            await file.CopyToAsync(fileStream);

            return filePath;
        }

        private Task<string> OptimizeImageAsync(string originalFilePath)
        {
            var fileInfo = new FileInfo(originalFilePath);
            var outputFolder = Path.Combine(fileInfo.Directory!.FullName, _settings.OutputFolder);
            var fileName = Path.GetFileNameWithoutExtension(originalFilePath);

            if (!Directory.Exists(outputFolder))
            {
                Directory.CreateDirectory(outputFolder);
            }

            var imageFormat = GetImageFormat();
            var outputFilePath = Path.Combine(outputFolder, $"{fileName}.{imageFormat.ToString().ToLower()}");

            var outputFileInfo = new FileInfo(outputFilePath);
            if (outputFileInfo.Exists)
            {
                outputFileInfo.Delete();
            }

            using var image = new MagickImage(fileInfo);
            var currentWidth = image.Page.Width;
            var maxWidth = (uint)_settings.MaxImageWidth;

            image.Quality = (uint)_settings.ImageQuality;

            if (currentWidth > maxWidth)
            {
                var geometry = new MagickGeometry { Width = maxWidth };
                image.Resize(geometry);
            }

            image.Write(outputFilePath);

            return Task.FromResult(outputFilePath);
        }

        private MagickFormat GetImageFormat()
        {
            return _settings.DefaultImageFormat switch
            {
                ImageFormat.Jpg => MagickFormat.Jpg,
                ImageFormat.WebP => MagickFormat.WebP,
                ImageFormat.Avif => MagickFormat.Avif,
                _ => MagickFormat.WebP
            };
        }

        private async Task<string> GenerateImageUrl(string systemPath)
        {
            return await GetUrlBySystemPath(systemPath);
        }

        private void RemoveFile(string filePath)
        {
            try
            {
                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                }
            }
            catch (Exception)
            {
                // Log error if needed, but don't fail the process
            }
        }

        private bool ValidateImageHeader(byte[] headerBytes, string fileName)
        {
            var imageHeaders = new Dictionary<string, byte[]>
            {
                {"JPG", new byte[] {0xFF, 0xD8, 0xFF}},
                {"JPEG", new byte[] {0xFF, 0xD8, 0xFF}},
                {"PNG", new byte[] {0x89, 0x50, 0x4E, 0x47}},
                {"TIF", new byte[] {0x49, 0x49, 0x2A, 0x00}},
                {"TIFF", new byte[] {0x49, 0x49, 0x2A, 0x00}},
                {"GIF", new byte[] {0x47, 0x49, 0x46, 0x38}},
                {"BMP", new byte[] {0x42, 0x4D}},
                {"ICO", new byte[] {0x00, 0x00, 0x01, 0x00}}
            };

            var fileExt = Path.GetExtension(fileName).TrimStart('.').ToUpper();

            try
            {
                if (!imageHeaders.ContainsKey(fileExt))
                    return false;

                var expectedHeader = imageHeaders[fileExt];
                var headerToCheck = headerBytes;

                if (fileExt == "JPG" || fileExt == "JPEG")
                {
                    headerToCheck = headerBytes.Take(3).ToArray();
                    expectedHeader = expectedHeader.Take(3).ToArray();
                }
                else if (fileExt == "BMP")
                {
                    headerToCheck = headerBytes.Take(2).ToArray();
                }

                return CompareArrays(expectedHeader, headerToCheck);
            }
            catch
            {
                return false;
            }
        }

        private static bool CompareArrays(byte[] array1, byte[] array2)
        {
            if (array1.Length != array2.Length)
                return false;

            for (int i = 0; i < array1.Length; i++)
            {
                if (array1[i] != array2[i])
                    return false;
            }

            return true;
        }

        private Data.Domains.Medias.Media CreateMediaEntity(ImageProcessResult result, ImageSaveOptions options)
        {
            var currentTime = DateTimeHelper.CurrentTime();
            return new Data.Domains.Medias.Media
            {
                Id = Guid.NewGuid(), // Tạo ID mới cho entity
                SystemPath = result.SystemPath,
                AltText = options.AltText ?? "Image",
                Caption = options.Caption ?? "Image",
                DisplayOrder = options.DisplayOrder,
                CreatedTime = currentTime,
                UpdatedTime = currentTime,
                CreatedBy = options.CreatedBy,
                CreatedByUserName = options.CreatedByUserName,
                UpdatedBy = options.UpdatedBy,
                UpdatedByUserName = options.UpdatedByUserName,
                // Business entity linking (chỉ những properties có trong Media entity)
                OrderId = options.OrderId ?? 0,
                OrderTrackId = options.OrderTrackId ?? Guid.Empty,
                OrderPackageId = options.OrderPackageId ?? 0
            };
        }

        #endregion
    }
}
