using AutoMapper;
using DocumentOperator.Application.CheckPdfAttachments.Queries;
using DocumentOperator.Application.Common.DTOs;
using DocumentOperator.Application.Common.Interfaces;

using FluentAssertions;
using Moq;

namespace DocumentOperator.Tests.Unit.Application.CheckPdfAttachments;

/// <summary>
/// Unit tests for CheckPdfAttachmentsQueryHandler.
/// Tests handler logic with mocked dependencies (IPdfProcessor, IMapper).
/// </summary>
public class CheckPdfAttachmentsQueryHandlerTests
{
    private readonly Mock<IPdfProcessor> _mockPdfProcessor;
    private readonly Mock<IMapper> _mockMapper;
    private readonly CheckPdfAttachmentsQueryHandler _sut;

    public CheckPdfAttachmentsQueryHandlerTests()
    {
        _mockPdfProcessor = new Mock<IPdfProcessor>();
        _mockMapper = new Mock<IMapper>();
        _sut = new CheckPdfAttachmentsQueryHandler(_mockPdfProcessor.Object, _mockMapper.Object);
    }

    [Fact]
    public async Task Handle_WithPdfBytes_CallsProcessorAndMapper()
    {
        // Arrange
        byte[] pdfBytes = "fake-pdf-content"u8.ToArray();
        var query = new CheckPdfAttachmentsQuery { PdfStream = new MemoryStream(pdfBytes) };

        var domainResult = new AttachmentInfo(
            hasAttachments: true,
            attachmentCount: 2,
            attachments:
            [
                new("invoice.xml", "text/xml", 1024),
                new("metadata.json", "application/json", 512)
            ]
        );

        var expectedDto = new AttachmentCheckResult
        {
            HasAttachments = true,
            AttachmentCount = 2,
            Attachments =
            [
                new() { FileName = "invoice.xml", MimeType = "text/xml", Size = 1024 },
                new() { FileName = "metadata.json", MimeType = "application/json", Size = 512 }
            ]
        };

        _mockPdfProcessor.Setup(p => p.CheckAttachmentsAsync(It.IsAny<Stream>())).ReturnsAsync(domainResult);
        _mockMapper.Setup(m => m.Map<AttachmentCheckResult>(domainResult)).Returns(expectedDto);

        // Act
        var result = await _sut.Handle(query, CancellationToken.None);

        // Assert
        result.Should().NotBeNull();
        result.Should().BeEquivalentTo(expectedDto);

        _mockPdfProcessor.Verify(p => p.CheckAttachmentsAsync(It.IsAny<Stream>()), Times.Once);
        _mockMapper.Verify(m => m.Map<AttachmentCheckResult>(domainResult), Times.Once);
    }

    [Fact]
    public async Task Handle_WithBase64Pdf_DecodesAndCallsProcessor()
    {
        // Arrange
        byte[] pdfBytes = "fake-pdf-content"u8.ToArray();
        var query = new CheckPdfAttachmentsQuery { PdfStream = new MemoryStream(pdfBytes) };

        var domainResult = new AttachmentInfo(false, 0, []);
        var expectedDto = new AttachmentCheckResult { HasAttachments = false, AttachmentCount = 0, Attachments = [] };

        _mockPdfProcessor.Setup(p => p.CheckAttachmentsAsync(It.IsAny<Stream>())).ReturnsAsync(domainResult);
        _mockMapper.Setup(m => m.Map<AttachmentCheckResult>(domainResult)).Returns(expectedDto);

        // Act
        var result = await _sut.Handle(query, CancellationToken.None);

        // Assert
        result.Should().NotBeNull();
        result.HasAttachments.Should().BeFalse();
        result.AttachmentCount.Should().Be(0);

        _mockPdfProcessor.Verify(p => p.CheckAttachmentsAsync(It.IsAny<Stream>()), Times.Once);
    }

    [Fact]
    public async Task Handle_WithEmptyAttachments_ReturnsEmptyList()
    {
        // Arrange
        byte[] pdfBytes = "fake-pdf-content"u8.ToArray();
        var query = new CheckPdfAttachmentsQuery { PdfStream = new MemoryStream(pdfBytes) };

        var domainResult = new AttachmentInfo(false, 0, []);
        var expectedDto = new AttachmentCheckResult
        {
            HasAttachments = false,
            AttachmentCount = 0,
            Attachments = []
        };

        _mockPdfProcessor.Setup(p => p.CheckAttachmentsAsync(It.IsAny<Stream>())).ReturnsAsync(domainResult);
        _mockMapper.Setup(m => m.Map<AttachmentCheckResult>(domainResult)).Returns(expectedDto);

        // Act
        var result = await _sut.Handle(query, CancellationToken.None);

        // Assert
        result.HasAttachments.Should().BeFalse();
        result.AttachmentCount.Should().Be(0);
        result.Attachments.Should().BeEmpty();
    }
}


