using System.Reflection;
using DocumentOperator.Application.Common.Interfaces;
using DocumentOperator.Domain.Common.Exceptions;
using DocumentOperator.Domain.Models.ValueObjects;
using DocumentOperator.Infrastructure.Services.PdfProcessing;
using FluentAssertions;

namespace DocumentOperator.Tests.Unit.Infrastructure.Services.PdfProcessing;

/// <summary>
/// Unit tests for DevExpressPdfProcessor.
/// Tests PDF validation and metadata extraction.
/// </summary>
public class DevExpressPdfProcessorTests
{
    private readonly IPdfProcessor _sut; // SUT = System Under Test

    public DevExpressPdfProcessorTests()
    {
        // Arrange: Create instance (wird später implementiert)
        _sut = new DevExpressPdfProcessor();
    }

    #region Helper Methods

    /// <summary>
    /// Loads a test PDF from embedded resources.
    /// </summary>
    /// <param name="filename">Name of the PDF file (e.g., "valid.pdf")</param>
    /// <returns>PDF content as byte array</returns>
    private static byte[] LoadTestPdf(string filename)
    {
        var assembly = Assembly.GetExecutingAssembly();
        var resourceName = $"DocumentOperator.Tests.TestData.Pdfs.{filename}";

        using var stream = assembly.GetManifestResourceStream(resourceName);

        if (stream == null)
        {
            throw new FileNotFoundException(
                $"Embedded resource '{resourceName}' not found. " +
                $"Available resources: {string.Join(", ", assembly.GetManifestResourceNames())}");
        }

        using var memoryStream = new MemoryStream();
        stream.CopyTo(memoryStream);
        return memoryStream.ToArray();
    }

    #endregion

    #region ValidateAsync Tests

    [Fact]
    public async Task ValidateAsync_ValidPdf_ReturnsPdfMetadata()
    {
        // Arrange
        byte[] pdfBytes = LoadTestPdf("valid.pdf");

        // Act
        var metadata = await _sut.ValidateAsync(pdfBytes);

        // Assert
        metadata.Should().NotBeNull("a valid PDF should return metadata");
        metadata.PageCount.Should().BeGreaterThan(0, "PDF must have at least one page");
        metadata.FileSizeBytes.Should().Be(pdfBytes.Length, "file size should match input");
        metadata.PdfVersion.Should().NotBeNullOrEmpty("PDF version should be detected");
    }

    [Fact]
    public async Task ValidateAsync_ValidPdf_ReturnsCorrectPageCount()
    {
        // Arrange
        byte[] pdfBytes = LoadTestPdf("valid.pdf");

        // Act
        var metadata = await _sut.ValidateAsync(pdfBytes);

        // Assert
        // Deine valid.pdf hat wahrscheinlich 1-5 Seiten - passe an!
        metadata.PageCount.Should().BeInRange(1, 100, "test PDF should have reasonable page count");
    }

    [Fact]
    public async Task ValidateAsync_NullBytes_ThrowsPdfProcessingException()
    {
        // Arrange
        byte[]? pdfBytes = null;

        // Act
        Func<Task> act = async () => await _sut.ValidateAsync(pdfBytes!);

        // Assert
        await act.Should().ThrowAsync<PdfProcessingException>()
            .WithMessage("*null*", "null input should be rejected");
    }

    [Fact]
    public async Task ValidateAsync_EmptyBytes_ThrowsPdfProcessingException()
    {
        // Arrange
        byte[] pdfBytes = Array.Empty<byte>();

        // Act
        Func<Task> act = async () => await _sut.ValidateAsync(pdfBytes);

        // Assert
        await act.Should().ThrowAsync<PdfProcessingException>()
            .WithMessage("*empty*", "empty input should be rejected");
    }

    [Fact]
    public async Task ValidateAsync_CorruptedPdf_ThrowsPdfProcessingException()
    {
        // Arrange
        byte[] pdfBytes = "This is not a valid PDF content"u8.ToArray();

        // Act
        Func<Task> act = async () => await _sut.ValidateAsync(pdfBytes);

        // Assert
        await act.Should().ThrowAsync<PdfProcessingException>()
            .WithMessage("*valid pdf*", "corrupted PDF should throw exception");
    }

    #endregion

    #region Metadata Tests

    [Fact]
    public async Task ValidateAsync_ValidPdf_FileSizeMBCalculatedCorrectly()
    {
        // Arrange
        byte[] pdfBytes = LoadTestPdf("valid.pdf");

        // Act
        var metadata = await _sut.ValidateAsync(pdfBytes);

        // Assert
        double expectedSizeMB = pdfBytes.Length / 1024.0 / 1024.0;
        metadata.FileSizeMB.Should().BeApproximately(expectedSizeMB, 0.01,
            "FileSizeMB should be calculated correctly from bytes");
    }

    #endregion

    #region Attachment Detection Tests

    [Fact]
    public async Task ValidateAsync_ValidPdf_DetectsAttachmentsCorrectly()
    {
        // Arrange
        byte[] pdfBytes = LoadTestPdf("valid.pdf");

        // Act
        var metadata = await _sut.ValidateAsync(pdfBytes);

        // Assert
        // Note: valid.pdf actually contains /EmbeddedFiles reference (15 0 R)
        // This test just verifies that attachment detection doesn't crash
        // The exact count depends on the PDF content
        metadata.Should().NotBeNull();
    }

    [Fact]
    public async Task ValidateAsync_PdfWithMultipleAttachments_ReturnsCorrectCount()
    {
        // Arrange
        byte[] pdfBytes = LoadTestPdf("pdfWithMoreThanOneAttachment.pdf");

        // Act
        var metadata = await _sut.ValidateAsync(pdfBytes);

        // Assert
        metadata.HasAttachments.Should().BeTrue("PDF has multiple attachments");
        metadata.AttachmentCount.Should().BeGreaterThan(1, "PDF has more than 1 attachment");
    }

    // Note: Testing PDF with attachments requires a real ZUGFeRD PDF file
    // as DevExpress PdfDocumentProcessor doesn't expose a simple API to create attachments.
    // This test will be added when a ZUGFeRD test PDF is available.
    // For now, we verify that attachment detection works (returns false for PDFs without attachments).

    #endregion
}
