using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PowderCoating.Application.Configuration;
using PowderCoating.Application.Interfaces;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;

namespace PowderCoating.Application.Services;

/// <summary>
/// Manages catalog item images in Azure Blob Storage.  Each upload produces a full-size original and a
/// 200×200 JPEG thumbnail.  Both blobs are stored under <c>{companyId}/catalog/{itemId}/</c> so that
/// paths are isolated per tenant and per item — existing blobs for the same item are replaced atomically
/// (delete-then-upload) to avoid orphaned files accumulating over time.
/// </summary>
public class CatalogImageService : ICatalogImageService
{
    private readonly IAzureBlobStorageService _blobService;
    private readonly StorageSettings _settings;
    private readonly ILogger<CatalogImageService> _logger;

    private static readonly string[] AllowedExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
    private const long MaxFileSizeBytes = 10 * 1024 * 1024; // 10 MB
    private const int ThumbnailSize = 200;

    public CatalogImageService(
        IAzureBlobStorageService blobService,
        IOptions<StorageSettings> settings,
        ILogger<CatalogImageService> logger)
    {
        _blobService = blobService;
        _settings = settings.Value;
        _logger = logger;
    }

    /// <summary>
    /// Validates the upload, removes any existing blobs, stores the original, generates a 200×200 JPEG
    /// thumbnail, stores the thumbnail, and returns both blob paths.  The thumbnail is always stored as
    /// JPEG regardless of the source format for predictable browser rendering and smaller file sizes.
    /// </summary>
    public async Task<(bool Success, string ImagePath, string ThumbnailPath, string ErrorMessage)> UploadAsync(
        IFormFile file,
        int itemId,
        int companyId,
        string? existingImagePath,
        string? existingThumbnailPath)
    {
        var (isValid, ext, validationError) = BlobFileHelper.ValidateUpload(file, AllowedExtensions, MaxFileSizeBytes);
        if (!isValid)
            return (false, string.Empty, string.Empty, validationError);

        var container = _settings.Containers.CatalogImages;
        var blobId = Guid.NewGuid().ToString("N");
        var imagePath = $"{companyId}/catalog/{itemId}/{blobId}{ext}";
        var thumbPath = $"{companyId}/catalog/{itemId}/thumb_{blobId}.jpg";

        // Delete existing blobs before uploading replacements.
        await DeleteAsync(existingImagePath, existingThumbnailPath);

        // Upload original.
        using var originalStream = file.OpenReadStream();
        var uploadResult = await _blobService.UploadAsync(container, imagePath, originalStream, file.ContentType);
        if (!uploadResult.Success)
            return (false, string.Empty, string.Empty, uploadResult.ErrorMessage);

        // Generate and upload thumbnail.
        using var thumbStream = await GenerateThumbnailAsync(file);
        if (thumbStream == null)
        {
            // Thumbnail generation failed; clean up the original and bail out.
            await _blobService.DeleteAsync(container, imagePath);
            return (false, string.Empty, string.Empty, "Failed to generate thumbnail.");
        }

        var thumbResult = await _blobService.UploadAsync(container, thumbPath, thumbStream, "image/jpeg");
        if (!thumbResult.Success)
        {
            await _blobService.DeleteAsync(container, imagePath);
            return (false, string.Empty, string.Empty, thumbResult.ErrorMessage);
        }

        _logger.LogInformation("Catalog image uploaded for item {ItemId}: {ImagePath}", itemId, imagePath);
        return (true, imagePath, thumbPath, string.Empty);
    }

    /// <inheritdoc/>
    public async Task<(bool Success, byte[] Content, string ContentType, string ErrorMessage)> DownloadAsync(string blobPath)
    {
        return await _blobService.DownloadAsync(_settings.Containers.CatalogImages, blobPath);
    }

    /// <inheritdoc/>
    public async Task DeleteAsync(string? imagePath, string? thumbnailPath)
    {
        var container = _settings.Containers.CatalogImages;
        if (!string.IsNullOrEmpty(imagePath))
            await _blobService.DeleteAsync(container, imagePath);
        if (!string.IsNullOrEmpty(thumbnailPath))
            await _blobService.DeleteAsync(container, thumbnailPath);
    }

    /// <summary>
    /// Decodes the uploaded image with ImageSharp, resizes it to fit within a 200×200 square while
    /// preserving aspect ratio, and encodes the result as JPEG.  Returns null if decoding fails so the
    /// caller can surface a clean error without propagating an ImageSharp exception.
    /// </summary>
    private async Task<MemoryStream?> GenerateThumbnailAsync(IFormFile file)
    {
        try
        {
            using var inputStream = file.OpenReadStream();
            using var image = await Image.LoadAsync(inputStream);

            image.Mutate(ctx => ctx.Resize(new ResizeOptions
            {
                Size = new Size(ThumbnailSize, ThumbnailSize),
                Mode = ResizeMode.Max
            }));

            var ms = new MemoryStream();
            await image.SaveAsync(ms, new JpegEncoder { Quality = 85 });
            ms.Position = 0;
            return ms;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Thumbnail generation failed for file {FileName}", file.FileName);
            return null;
        }
    }
}
