using DocumentOperator.API.Controllers; // For CheckPdfAttachmentsRequest DTO
using DocumentOperator.Application.Common.DTOs;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using System.Net;
using System.Net.Http.Json;
using Xunit;

namespace DocumentOperator.Tests.Integration.API;

/// <summary>
/// Integration tests for PdfAttachmentController.
/// Tests /api/pdf/attachments/check endpoint with both multipart and Base64 input.
/// </summary>
public class PdfAttachmentControllerTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;
    private readonly HttpClient _client;

    public PdfAttachmentControllerTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
        _client = _factory.CreateClient();
    }

    #region Helper Methods

    /// <summary>
    /// Loads a test PDF from embedded resources.
    /// </summary>
    private static async Task<byte[]> LoadTestPdfAsync(string filename)
    {
        var assembly = typeof(PdfAttachmentControllerTests).Assembly;
        var resourceName = $"DocumentOperator.Tests.TestData.Pdfs.{filename}";

        using var stream = assembly.GetManifestResourceStream(resourceName);
        if (stream == null)
        {
            throw new InvalidOperationException($"Test resource '{resourceName}' not found");
        }

        using var ms = new MemoryStream();
        await stream.CopyToAsync(ms);
        return ms.ToArray();
    }

    #endregion

    #region Base64 JSON Tests

    [Fact]
    public async Task POST_CheckAttachments_Base64_PdfWithoutAttachments_Returns200()
    {
        // Arrange
        byte[] pdfBytes = await LoadTestPdfAsync("pdfWithSwissQRCode.pdf");
        string base64Pdf = Convert.ToBase64String(pdfBytes);
        var request = new CheckPdfAttachmentsRequest { Base64Pdf = base64Pdf };

        // Act
        var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
        result.Should().NotBeNull();
        result!.HasAttachments.Should().BeFalse();
        result.AttachmentCount.Should().Be(0);
        result.Attachments.Should().BeEmpty();
    }

    [Fact]
    public async Task POST_CheckAttachments_Base64_PdfWithMultipleAttachments_Returns200()
    {
        // Arrange
        byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");
        string base64Pdf = Convert.ToBase64String(pdfBytes);
        var request = new CheckPdfAttachmentsRequest { Base64Pdf = base64Pdf };

        // Act
        var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
        result.Should().NotBeNull();
        result!.HasAttachments.Should().BeTrue();
        result.AttachmentCount.Should().Be(6, "PDF has exactly 6 attachments");
        result.Attachments.Should().HaveCount(6);

        // Verify each attachment has required properties
        foreach (var attachment in result.Attachments)
        {
            attachment.FileName.Should().NotBeNullOrEmpty();
            attachment.MimeType.Should().NotBeNullOrEmpty();
            attachment.Size.Should().BeGreaterThan(0);
        }
    }

    [Fact]
    public async Task POST_CheckAttachments_Base64_InvalidBase64_Returns400()
    {
        // Arrange
        var request = new CheckPdfAttachmentsRequest { Base64Pdf = "invalid-base64!!!" };

        // Act
        var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);

        var problemDetails = await response.Content.ReadAsStringAsync();
        // FormatException message contains "Base-64" (with hyphen)
        problemDetails.Should().MatchRegex("(?i)base.?64", "should contain Base64 validation error");
    }

    [Fact]
    public async Task POST_CheckAttachments_Base64_EmptyPdf_Returns400()
    {
        // Arrange
        var request = new CheckPdfAttachmentsRequest { Base64Pdf = string.Empty };

        // Act
        var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);

        var problemDetails = await response.Content.ReadAsStringAsync();
        // Empty Base64 causes FormatException or empty stream error
        problemDetails.Should().MatchRegex("(Base64|empty|stream)", "should contain validation error message");
    }

    #endregion

    #region Multipart/Form-Data Tests

    [Fact]
    public async Task POST_CheckAttachments_Multipart_PdfWithoutAttachments_Returns200()
    {
        // Arrange
        byte[] pdfBytes = await LoadTestPdfAsync("pdfWithSwissQRCode.pdf");

        using var content = new MultipartFormDataContent();
        var fileContent = new ByteArrayContent(pdfBytes);
        fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
        content.Add(fileContent, "file", "pdfWithSwissQRCode.pdf");

        // Act
        var response = await _client.PostAsync("/api/pdf/attachments/check", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
        result.Should().NotBeNull();
        result!.HasAttachments.Should().BeFalse();
        result.AttachmentCount.Should().Be(0);
        result.Attachments.Should().BeEmpty();
    }

    [Fact]
    public async Task POST_CheckAttachments_Multipart_PdfWithMultipleAttachments_Returns200()
    {
        // Arrange
        byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");

        using var content = new MultipartFormDataContent();
        var fileContent = new ByteArrayContent(pdfBytes);
        fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
        content.Add(fileContent, "file", "pdfWithMoreThanOneAttachment.pdf");

        // Act
        var response = await _client.PostAsync("/api/pdf/attachments/check", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
        result.Should().NotBeNull();
        result!.HasAttachments.Should().BeTrue();
        result.AttachmentCount.Should().Be(6);
        result.Attachments.Should().HaveCount(6);

        // Verify first attachment details
        var firstAttachment = result.Attachments.First();
        firstAttachment.FileName.Should().NotBeNullOrEmpty();
        firstAttachment.MimeType.Should().NotBeNullOrEmpty();
        firstAttachment.Size.Should().BeGreaterThan(0);
    }

    [Fact]
    public async Task POST_CheckAttachments_Multipart_MissingFile_Returns400()
    {
        // Arrange
        using var content = new MultipartFormDataContent(); // No file added

        // Act
        var response = await _client.PostAsync("/api/pdf/attachments/check", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
    }

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

        using var content = new MultipartFormDataContent();
        var fileContent = new ByteArrayContent(corruptedBytes);
        fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
        content.Add(fileContent, "file", "corrupted.pdf");

        // Act
        var response = await _client.PostAsync("/api/pdf/attachments/check", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.InternalServerError);

        var problemDetails = await response.Content.ReadAsStringAsync();
        // Note: Generic error message for security reasons (doesn't expose internal details)
        problemDetails.Should().Contain("error");
    }

    #endregion

    #region Edge Cases

    [Fact]
    public async Task POST_CheckAttachments_Base64_PdfWithSwissQrCode_Returns200()
    {
        // Arrange
        byte[] pdfBytes = await LoadTestPdfAsync("pdfWithSwissQRCode.pdf");
        string base64Pdf = Convert.ToBase64String(pdfBytes);
        var request = new CheckPdfAttachmentsRequest { Base64Pdf = base64Pdf };

        // Act
        var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
        result.Should().NotBeNull();
        result!.HasAttachments.Should().BeFalse("Swiss QR PDF has no attachments");
        result.AttachmentCount.Should().Be(0);
    }

    #endregion

    #region Extract Attachments Tests (Multipart)

    [Fact]
    public async Task POST_ExtractAttachments_Multipart_ValidPdfWithAttachments_Returns200WithZip()
    {
        // Arrange
        byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");

        using var content = new MultipartFormDataContent();
        var fileContent = new ByteArrayContent(pdfBytes);
        fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
        content.Add(fileContent, "file", "test.pdf");

        // Act
        var response = await _client.PostAsync("/api/pdf/attachments/extract", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        response.Content.Headers.ContentType?.MediaType.Should().Be("application/zip");
        response.Content.Headers.ContentDisposition?.FileName.Should().Be("attachments.zip");

        byte[] zipBytes = await response.Content.ReadAsByteArrayAsync();
        zipBytes.Should().NotBeEmpty("ZIP should contain data");

        // Verify ZIP structure
        using var zipStream = new MemoryStream(zipBytes);
        using var zipArchive = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Read);
        
        zipArchive.Entries.Should().HaveCount(6, "PDF contains 6 attachments");
    }

    [Fact]
    public async Task POST_ExtractAttachments_Multipart_EmptyPdf_Returns400()
    {
        // Arrange
        using var content = new MultipartFormDataContent();
        var fileContent = new ByteArrayContent(Array.Empty<byte>());
        fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
        content.Add(fileContent, "file", "empty.pdf");

        // Act
        var response = await _client.PostAsync("/api/pdf/attachments/extract", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
    }

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

        using var content = new MultipartFormDataContent();
        var fileContent = new ByteArrayContent(corruptedBytes);
        fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
        content.Add(fileContent, "file", "corrupted.pdf");

        // Act
        var response = await _client.PostAsync("/api/pdf/attachments/extract", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
    }

    #endregion

    #region Extract Attachments Tests (Base64)

    [Fact]
    public async Task POST_ExtractAttachments_Base64_ValidPdfWithAttachments_Returns200WithZip()
    {
        // Arrange
        byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");
        string base64Pdf = Convert.ToBase64String(pdfBytes);
        var request = new ExtractPdfAttachmentsRequest { Base64Pdf = base64Pdf };

        // Act
        var response = await _client.PostAsJsonAsync("/api/pdf/attachments/extract", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        response.Content.Headers.ContentType?.MediaType.Should().Be("application/zip");
        response.Content.Headers.ContentDisposition?.FileName.Should().Be("attachments.zip");

        byte[] zipBytes = await response.Content.ReadAsByteArrayAsync();
        zipBytes.Should().NotBeEmpty("ZIP should contain data");

        // Verify ZIP structure
        using var zipStream = new MemoryStream(zipBytes);
        using var zipArchive = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Read);
        
        zipArchive.Entries.Should().HaveCount(6, "PDF contains 6 attachments");
    }

    [Fact]
    public async Task POST_ExtractAttachments_Base64_InvalidBase64_Returns400()
    {
        // Arrange
        var request = new ExtractPdfAttachmentsRequest { Base64Pdf = "invalid-base64!!!" };

        // Act
        var response = await _client.PostAsJsonAsync("/api/pdf/attachments/extract", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);

        var problemDetails = await response.Content.ReadAsStringAsync();
        // FormatException message contains "Base-64" (with hyphen)
        problemDetails.Should().MatchRegex("(?i)base.?64", "should contain Base64 validation error");
    }

    [Fact]
    public async Task POST_ExtractAttachments_Base64_EmptyPdf_Returns400()
    {
        // Arrange
        var request = new ExtractPdfAttachmentsRequest { Base64Pdf = string.Empty };

        // Act
        var response = await _client.PostAsJsonAsync("/api/pdf/attachments/extract", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);

        var problemDetails = await response.Content.ReadAsStringAsync();
        problemDetails.Should().MatchRegex("(Base64|empty|stream)", "should contain validation error message");
    }

    #endregion
}
