using System.Text;
using System.Xml;
using System.Xml.Linq;
using IWS5.Server.Enums;
using Syncfusion.Drawing;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;

namespace IWS5.Server.Utils.Converter.PdfConverter;

public class XmlFileToPdfConverter : IFileConverter
{
    private static readonly HashSet<string> Extensions = new(StringComparer.OrdinalIgnoreCase)
    {
        nameof(ConvertibleFileTypes.xml).ToLowerInvariant()
    };

    public bool CanConvert(string sourceFileExtension, string targetFileExtension) => Extensions.Contains(sourceFileExtension) && 
        string.Equals(targetFileExtension, nameof(ConvertibleFileTypes.pdf), StringComparison.OrdinalIgnoreCase);

    public async Task<byte[]> ConvertAsync(byte[] inputFile)
    {
        try
        {
            // Convert byte array to XML string
            string xmlContent = Encoding.UTF8.GetString(inputFile);

            // Validate XML content
            if (string.IsNullOrWhiteSpace(xmlContent))
            {
                throw new ArgumentException("XML content cannot be null or empty.");
            }

            // Format the XML for better readability
            string formattedXml = FormatXml(xmlContent);

            using var document = new PdfDocument();

            // Set up page and graphics
            PdfPage page = document.Pages.Add();
            PdfGraphics graphics = page.Graphics;

            // Define fonts and layout settings
            PdfFont font = new PdfStandardFont(PdfFontFamily.Courier, 10);
            PdfSolidBrush brush = new PdfSolidBrush(Color.Black);

            float margin = 40;
            float pageWidth = page.GetClientSize().Width - 2 * margin;
            float yPosition = margin;
            float lineHeight = font.Height + 2;

            // Split formatted XML into lines
            string[] lines = formattedXml.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (string line in lines)
            {
                // Check if we need a new page
                if (yPosition + lineHeight > page.GetClientSize().Height - margin)
                {
                    page = document.Pages.Add();
                    graphics = page.Graphics;
                    yPosition = margin;
                }

                // Handle long lines by wrapping them
                SizeF textSize = font.MeasureString(line);
                if (textSize.Width > pageWidth)
                {
                    var wrappedLines = WrapText(line, font, pageWidth);
                    foreach (string wrappedLine in wrappedLines)
                    {
                        if (yPosition + lineHeight > page.GetClientSize().Height - margin)
                        {
                            page = document.Pages.Add();
                            graphics = page.Graphics;
                            yPosition = margin;
                        }

                        graphics.DrawString(wrappedLine, font, brush, new PointF(margin, yPosition));
                        yPosition += lineHeight;
                    }
                }
                else
                {
                    graphics.DrawString(line, font, brush, new PointF(margin, yPosition));
                    yPosition += lineHeight;
                }
            }

            using var pdfStream = new MemoryStream();
            document.Save(pdfStream);
            document.Close(true);

            var result = pdfStream.ToArray();

            if (result.Length == 0)
            {
                throw new InvalidOperationException("XML conversion resulted in empty document.");
            }

            return await Task.FromResult(result);
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException($"Error converting XML to PDF: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// Formats XML content for better readability with proper indentation.
    /// </summary>
    private static string FormatXml(string xmlContent)
    {
        try
        {
            var doc = XDocument.Parse(xmlContent);
            return doc.ToString();
        }
        catch (XmlException)
        {
            // If XML parsing fails, return original content
            return xmlContent;
        }
    }

    /// <summary>
    /// Wraps long text lines to fit within the specified width.
    /// </summary>
    private static List<string> WrapText(string text, PdfFont font, float maxWidth)
    {
        var wrappedLines = new List<string>();
        var words = text.Split(' ');
        var currentLine = new StringBuilder();

        foreach (string word in words)
        {
            string testLine = currentLine.Length == 0 ? word : $"{currentLine} {word}";

            SizeF testSize = font.MeasureString(testLine);
            if (testSize.Width <= maxWidth)
            {
                currentLine.Append(currentLine.Length == 0 ? word : $" {word}");
            }
            else
            {
                if (currentLine.Length > 0)
                {
                    wrappedLines.Add(currentLine.ToString());
                    currentLine.Clear();
                }

                currentLine.Append(word);
            }
        }

        if (currentLine.Length > 0)
        {
            wrappedLines.Add(currentLine.ToString());
        }

        return wrappedLines;
    }
}