using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using PowderCoating.Application.Configuration;
using PowderCoating.Application.Interfaces;
using PowderCoating.Application.Services;

namespace PowderCoating.UnitTests;

public class QuotePhotoServiceTests
{
    [Fact]
    public async Task SaveTempPhotoAsync_ReturnsError_WhenFileMissing()
    {
        var service = CreateService();

        var result = await service.SaveTempPhotoAsync(null!, companyId: 1);

        Assert.False(result.Success);
        Assert.Equal("No file provided.", result.ErrorMessage);
    }

    [Fact]
    public async Task SaveTempPhotoAsync_ReturnsError_WhenFileTooLarge()
    {
        var service = CreateService();
        var file = CreateFormFile("huge.jpg", 10 * 1024 * 1024 + 1);

        var result = await service.SaveTempPhotoAsync(file, companyId: 1);

        Assert.False(result.Success);
        Assert.Equal("File exceeds the 10 MB limit.", result.ErrorMessage);
    }

    [Fact]
    public async Task SaveTempPhotoAsync_ReturnsError_WhenExtensionNotAllowed()
    {
        var service = CreateService();
        var file = CreateFormFile("photo.bmp");

        var result = await service.SaveTempPhotoAsync(file, companyId: 1);

        Assert.False(result.Success);
        Assert.Equal("File type not allowed. Allowed: .jpg, .jpeg, .png, .gif, .webp.", result.ErrorMessage);
    }

    [Fact]
    public async Task SaveTempPhotoAsync_ReturnsBlobError_WhenUploadFails()
    {
        var blobService = new Mock<IAzureBlobStorageService>();
        blobService
            .Setup(x => x.UploadAsync("quoteimages", It.IsAny<string>(), It.IsAny<Stream>(), "image/png"))
            .ReturnsAsync((false, "blob upload failed"));

        var service = CreateService(blobService: blobService);
        var file = CreateFormFile("photo.png");

        var result = await service.SaveTempPhotoAsync(file, companyId: 1);

        Assert.False(result.Success);
        Assert.Equal("blob upload failed", result.ErrorMessage);
    }

    [Fact]
    public async Task SaveTempPhotoAsync_UploadsToTempPath_WhenValid()
    {
        var blobService = new Mock<IAzureBlobStorageService>();
        blobService
            .Setup(x => x.UploadAsync("quoteimages", It.IsAny<string>(), It.IsAny<Stream>(), "image/jpeg"))
            .ReturnsAsync((true, string.Empty));

        var service = CreateService(blobService: blobService);
        var file = CreateFormFile("photo.jpg");

        var result = await service.SaveTempPhotoAsync(file, companyId: 5);

        Assert.True(result.Success);
        Assert.False(string.IsNullOrWhiteSpace(result.TempId));
        Assert.StartsWith($"temp/{result.TempId}/", result.FilePath);
        Assert.EndsWith(".jpg", result.FilePath);
    }

    [Fact]
    public async Task PromoteTempPhotoAsync_ReturnsError_WhenTempPhotoMissing()
    {
        var blobService = new Mock<IAzureBlobStorageService>();
        blobService
            .Setup(x => x.ListBlobsByPrefixAsync("quoteimages", "temp/temp123/"))
            .ReturnsAsync(Array.Empty<string>());

        var service = CreateService(blobService: blobService);

        var result = await service.PromoteTempPhotoAsync("temp123", quoteId: 10, companyId: 3);

        Assert.False(result.Success);
        Assert.Equal("Temp photo not found.", result.ErrorMessage);
    }

    [Fact]
    public async Task PromoteTempPhotoAsync_ReturnsError_WhenTempDownloadFails()
    {
        var blobService = new Mock<IAzureBlobStorageService>();
        blobService
            .Setup(x => x.ListBlobsByPrefixAsync("quoteimages", "temp/temp123/"))
            .ReturnsAsync(new[] { "temp/temp123/original.png" });
        blobService
            .Setup(x => x.DownloadAsync("quoteimages", "temp/temp123/original.png"))
            .ReturnsAsync((false, Array.Empty<byte>(), string.Empty, "download failed"));

        var service = CreateService(blobService: blobService);

        var result = await service.PromoteTempPhotoAsync("temp123", quoteId: 10, companyId: 3);

        Assert.False(result.Success);
        Assert.Equal("Failed to read temp photo.", result.ErrorMessage);
    }

    [Fact]
    public async Task PromoteTempPhotoAsync_ReturnsError_WhenPermanentUploadFails()
    {
        var blobService = new Mock<IAzureBlobStorageService>();
        blobService
            .Setup(x => x.ListBlobsByPrefixAsync("quoteimages", "temp/temp123/"))
            .ReturnsAsync(new[] { "temp/temp123/original.webp" });
        blobService
            .Setup(x => x.DownloadAsync("quoteimages", "temp/temp123/original.webp"))
            .ReturnsAsync((true, new byte[] { 1, 2, 3 }, "image/webp", string.Empty));
        blobService
            .Setup(x => x.UploadAsync("quoteimages", It.IsAny<string>(), It.IsAny<Stream>(), "image/webp"))
            .ReturnsAsync((false, "upload failed"));

        var service = CreateService(blobService: blobService);

        var result = await service.PromoteTempPhotoAsync("temp123", quoteId: 10, companyId: 3);

        Assert.False(result.Success);
        Assert.Equal("Failed to save permanent photo.", result.ErrorMessage);
    }

    [Fact]
    public async Task PromoteTempPhotoAsync_PromotesAndDeletesTempBlob_WhenSuccessful()
    {
        var blobService = new Mock<IAzureBlobStorageService>();
        blobService
            .Setup(x => x.ListBlobsByPrefixAsync("quoteimages", "temp/temp123/"))
            .ReturnsAsync(new[] { "temp/temp123/original.png" });
        blobService
            .Setup(x => x.DownloadAsync("quoteimages", "temp/temp123/original.png"))
            .ReturnsAsync((true, new byte[] { 1, 2, 3 }, "image/png", string.Empty));
        blobService
            .Setup(x => x.UploadAsync("quoteimages", It.IsAny<string>(), It.IsAny<Stream>(), "image/png"))
            .ReturnsAsync((true, string.Empty));
        blobService
            .Setup(x => x.DeleteAsync("quoteimages", "temp/temp123/original.png"))
            .ReturnsAsync((true, string.Empty));

        var service = CreateService(blobService: blobService);

        var result = await service.PromoteTempPhotoAsync("temp123", quoteId: 10, companyId: 3);

        Assert.True(result.Success);
        Assert.StartsWith("3/quote-photos/10/", result.FilePath);
        Assert.EndsWith(".png", result.FilePath);
        blobService.Verify(x => x.DeleteAsync("quoteimages", "temp/temp123/original.png"), Times.Once);
    }

    [Fact]
    public async Task ReadTempPhotosAsync_ReturnsOnlySuccessfulDownloads()
    {
        var blobService = new Mock<IAzureBlobStorageService>();
        blobService
            .Setup(x => x.ListBlobsByPrefixAsync("quoteimages", "temp/temp123/"))
            .ReturnsAsync(new[] { "temp/temp123/one.jpg", "temp/temp123/two.jpg" });
        blobService
            .Setup(x => x.DownloadAsync("quoteimages", "temp/temp123/one.jpg"))
            .ReturnsAsync((true, new byte[] { 1 }, "image/jpeg", string.Empty));
        blobService
            .Setup(x => x.DownloadAsync("quoteimages", "temp/temp123/two.jpg"))
            .ReturnsAsync((false, Array.Empty<byte>(), string.Empty, "failed"));

        var service = CreateService(blobService: blobService);

        var result = await service.ReadTempPhotosAsync("temp123");

        Assert.Single(result);
        Assert.Equal("one.jpg", result[0].FileName);
        Assert.Equal("image/jpeg", result[0].ContentType);
    }

    [Fact]
    public async Task CleanupTempAsync_ContinuesDeleting_WhenOneDeleteThrows()
    {
        var blobService = new Mock<IAzureBlobStorageService>();
        blobService
            .Setup(x => x.ListBlobsByPrefixAsync("quoteimages", "temp/temp123/"))
            .ReturnsAsync(new[] { "temp/temp123/one.jpg", "temp/temp123/two.jpg" });
        blobService
            .Setup(x => x.DeleteAsync("quoteimages", "temp/temp123/one.jpg"))
            .ThrowsAsync(new InvalidOperationException("boom"));
        blobService
            .Setup(x => x.DeleteAsync("quoteimages", "temp/temp123/two.jpg"))
            .ReturnsAsync((true, string.Empty));

        var service = CreateService(blobService: blobService);

        await service.CleanupTempAsync("temp123");

        blobService.Verify(x => x.DeleteAsync("quoteimages", "temp/temp123/one.jpg"), Times.Once);
        blobService.Verify(x => x.DeleteAsync("quoteimages", "temp/temp123/two.jpg"), Times.Once);
    }

    private static QuotePhotoService CreateService(Mock<IAzureBlobStorageService>? blobService = null)
    {
        var settings = Options.Create(new StorageSettings
        {
            Containers = new StorageContainers
            {
                QuoteImages = "quoteimages"
            }
        });

        return new QuotePhotoService(
            (blobService ?? new Mock<IAzureBlobStorageService>()).Object,
            settings,
            Mock.Of<ILogger<QuotePhotoService>>());
    }

    private static IFormFile CreateFormFile(string fileName, long? lengthOverride = null)
    {
        var dataLength = lengthOverride.HasValue
            ? (int)Math.Min(lengthOverride.Value, 1024)
            : 16;
        var bytes = Enumerable.Repeat((byte)65, dataLength).ToArray();
        var stream = new MemoryStream(bytes);

        return new FormFile(stream, 0, lengthOverride ?? bytes.Length, "file", fileName);
    }
}
