using IWS5.Server.Enums;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Parsing;

namespace IWS5.Server.Utils.Merger;

public class PdfMerger : IFileMerger
{
    public bool CanMerge(string filesExtension) => 
        string.Equals(filesExtension, nameof(ConvertibleFileTypes.pdf), StringComparison.OrdinalIgnoreCase);

    /// <summary>
    /// Merges multiple PDF files from byte arrays into a single PDF file
    /// </summary>
    /// <param name="pdfFiles">Array of byte arrays representing PDF files to merge</param>
    /// <returns>Byte array of the merged PDF file</returns>
    /// <exception cref="ArgumentNullException">Thrown when pdfFiles is null</exception>
    /// <exception cref="ArgumentException">Thrown when pdfFiles array is empty</exception>
    public byte[] Merge(params byte[][] pdfFiles)
    {
        if (pdfFiles == null)
            throw new ArgumentNullException(nameof(pdfFiles), "PDF files array cannot be null");
        
        if (pdfFiles.Length == 0)
            throw new ArgumentException("PDF files array cannot be empty", nameof(pdfFiles));

        using var mergedDocument = new PdfDocument();
        
        // Keep all streams and documents alive until after save
        var sourceStreams = new List<MemoryStream>();
        var sourceDocuments = new List<PdfLoadedDocument>();
        
        try
        {
            foreach (var pdfBytes in pdfFiles)
            {
                if (pdfBytes == null || pdfBytes.Length == 0)
                    continue;

                var sourceStream = new MemoryStream(pdfBytes);
                var sourceDocument = new PdfLoadedDocument(sourceStream);
                
                sourceStreams.Add(sourceStream);
                sourceDocuments.Add(sourceDocument);
                
                mergedDocument.ImportPageRange(sourceDocument, 0, sourceDocument.Pages.Count - 1);
            }

            if (mergedDocument.Pages.Count == 0)
                throw new InvalidOperationException("No valid PDF files found to merge");

            using var outputStream = new MemoryStream();
            mergedDocument.Save(outputStream);
            return outputStream.ToArray();
        }
        finally
        {
            // Dispose all source documents and streams
            foreach (var doc in sourceDocuments)
            {
                doc?.Dispose();
            }
            foreach (var stream in sourceStreams)
            {
                stream?.Dispose();
            }
        }
    }
}