using System.IO;
using Microsoft.Extensions.Configuration;

namespace PhotoHost
{
    public class ImageService
    {
        private readonly string _photoFolder; // Folder containing the images
        private readonly Dictionary<string, ImageFile> _imagesData; // Store images data with filename as key
        private const int MaxRetryAttempts = 5; // Maximum retry attempts for reading a file
        private const int RetryDelayMilliseconds = 500; // Delay between retry attempts

        public ImageService(IConfiguration configuration)
        {
            _photoFolder = configuration["PhotoFolder"] ?? throw new ArgumentNullException(nameof(configuration)); // Corrected exception parameter
            _imagesData = new Dictionary<string, ImageFile>();
            LoadImages(); // Load all images at the start
            StartMonitoring(); // Start monitoring for changes
        }

        // Load all images from the configured folder at startup
        private void LoadImages()
        {
            var imageFiles = Directory.GetFiles(_photoFolder, "*.*", SearchOption.TopDirectoryOnly)
                                      .Where(f => f.EndsWith(".jpg") || f.EndsWith(".jpeg") || f.EndsWith(".png"))
                                      .ToArray();

            foreach (var file in imageFiles)
            {
                TryLoadImage(file);
            }
        }

        // Start monitoring the folder for any changes (add, modify, delete, rename)
        private void StartMonitoring()
        {
            var watcher = new FileSystemWatcher(_photoFolder);
            watcher.Filter = "*.*";

            watcher.Created += OnImageAdded; // Handle file creation
            watcher.Deleted += OnImageRemoved; // Handle file deletion
            watcher.Changed += OnImageModified; // Handle file modification
            watcher.Renamed += OnImageRenamed; // Handle file renaming

            watcher.EnableRaisingEvents = true;
        }

        // Event handler for adding a new image
        private void OnImageAdded(object sender, FileSystemEventArgs e)
        {
            if (IsImageFile(e.FullPath))
            {
                TryLoadImage(e.FullPath);
            }
        }

        // Event handler for removing an image
        private void OnImageRemoved(object sender, FileSystemEventArgs e)
        {
            var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(e.FullPath);
            _imagesData.Remove(fileNameWithoutExtension);
        }

        // Event handler for modifying an image
        private void OnImageModified(object sender, FileSystemEventArgs e)
        {
            if (IsImageFile(e.FullPath))
            {
                TryLoadImage(e.FullPath);
            }
        }

        // Event handler for renaming an image
        private void OnImageRenamed(object sender, RenamedEventArgs e)
        {
            var oldFileNameWithoutExtension = Path.GetFileNameWithoutExtension(e.OldFullPath);
            var newFileNameWithoutExtension = Path.GetFileNameWithoutExtension(e.FullPath);

            // Use TryGetValue to avoid double lookup in the dictionary
            if (_imagesData.TryGetValue(oldFileNameWithoutExtension, out var imageFile))
            {
                _imagesData.Remove(oldFileNameWithoutExtension);
                _imagesData[newFileNameWithoutExtension] = imageFile;
            }
        }

        // Try to load an image file with retry mechanism
        private void TryLoadImage(string filePath)
        {
            var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
            var extension = Path.GetExtension(filePath);

            byte[]? imageData = RetryReadFile(filePath);
            if (imageData != null)
            {
                _imagesData[fileNameWithoutExtension] = new ImageFile(imageData, extension);
            }
        }

        // Retry reading a file multiple times if it's locked by another process
        private byte[]? RetryReadFile(string filePath)
        {
            for (int attempt = 1; attempt <= MaxRetryAttempts; attempt++)
            {
                try
                {
                    // Try reading the file
                    return File.ReadAllBytes(filePath);
                }
                catch (IOException ex)
                {
                    // Handle IO exceptions (file locked or in use)
                    if (attempt < MaxRetryAttempts)
                    {
                        // Wait and retry if attempts are left
                        Thread.Sleep(RetryDelayMilliseconds);
                    }
                    else
                    {
                        // Log and return null after all retries
                        Console.WriteLine($"IO error reading file {filePath}: {ex.Message}");
                        return null;
                    }
                }
                catch (Exception ex)
                {
                    // Handle other unexpected exceptions
                    Console.WriteLine($"Unexpected error reading file {filePath}: {ex.Message}");
                    return null;
                }
            }

            return null;
        }

        // Check if the file is an image file based on its extension
        private static bool IsImageFile(string filePath)
        {
            var extension = Path.GetExtension(filePath).ToLower();
            return extension == ".jpg" || extension == ".jpeg" || extension == ".png";
        }

        // Retrieve an image by its filename (without extension)
        public ImageFile? GetImage(string fileNameWithoutExtension)
        {
            _imagesData.TryGetValue(fileNameWithoutExtension, out var imageFile);
            return imageFile;
        }
    }
}
