using System;
using System.Threading.Tasks;
using Markdig;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;

namespace MarkdownEditor.Services;

/// <summary>
/// Service for exporting markdown content to PDF format.
/// </summary>
public class PdfExportService : IPdfExportService
{
    /// <inheritdoc/>
    public Task ExportToPdfAsync(string markdownContent, string filePath, string? title = null)
    {
        return Task.Run(() =>
        {
            // Configure QuestPDF license (Community license for open source projects)
            QuestPDF.Settings.License = LicenseType.Community;

            // Parse markdown to document
            var pipeline = new MarkdownPipelineBuilder()
                .UseAdvancedExtensions()
                .Build();

            var document = Markdown.Parse(markdownContent, pipeline);

            // Generate PDF
            Document.Create(container =>
            {
                container.Page(page =>
                {
                    page.Size(PageSizes.Letter);
                    page.Margin(50);
                    page.DefaultTextStyle(x => x.FontSize(12).FontFamily("Arial"));

                    page.Content()
                        .Column(column =>
                        {
                            column.Spacing(10);
                            RenderMarkdownDocument(column, document);
                        });

                    page.Footer()
                        .AlignCenter()
                        .Text(x =>
                        {
                            x.Span("Page ");
                            x.CurrentPageNumber();
                            x.Span(" of ");
                            x.TotalPages();
                        });
                });
            }).GeneratePdf(filePath);
        });
    }

    private void RenderMarkdownDocument(ColumnDescriptor column, MarkdownDocument document)
    {
        foreach (var block in document)
        {
            RenderBlock(column, block);
        }
    }

    private void RenderBlock(ColumnDescriptor column, Block block)
    {
        switch (block)
        {
            case HeadingBlock heading:
                RenderHeading(column, heading);
                break;

            case ParagraphBlock paragraph:
                RenderParagraph(column, paragraph);
                break;

            case CodeBlock code:
                RenderCodeBlock(column, code);
                break;

            case ListBlock list:
                RenderList(column, list);
                break;

            case QuoteBlock quote:
                RenderQuote(column, quote);
                break;

            case ThematicBreakBlock:
                column.Item().LineHorizontal(1).LineColor(Colors.Grey.Lighten2);
                break;
        }
    }

    private void RenderHeading(ColumnDescriptor column, HeadingBlock heading)
    {
        var fontSize = heading.Level switch
        {
            1 => 24,
            2 => 20,
            3 => 16,
            4 => 14,
            5 => 12,
            _ => 10
        };

        column.Item()
            .PaddingTop(10)
            .DefaultTextStyle(x => x.FontSize(fontSize).Bold())
            .Text(text =>
            {
                RenderInlines(text, heading.Inline);
            });
    }

    private void RenderParagraph(ColumnDescriptor column, ParagraphBlock paragraph)
    {
        column.Item()
            .DefaultTextStyle(x => x.LineHeight(1.5f))
            .Text(text =>
            {
                if (paragraph.Inline != null)
                {
                    RenderInlines(text, paragraph.Inline);
                }
            });
    }

    private void RenderCodeBlock(ColumnDescriptor column, CodeBlock code)
    {
        var codeText = string.Join("\n", code.Lines);

        column.Item()
            .Background(Colors.Grey.Lighten3)
            .Padding(10)
            .Text(codeText)
            .FontFamily("Courier New")
            .FontSize(10);
    }

    private void RenderList(ColumnDescriptor column, ListBlock list)
    {
        int itemNumber = 1;

        foreach (var item in list)
        {
            if (item is ListItemBlock listItem)
            {
                column.Item().Row(row =>
                {
                    // Bullet or number
                    row.ConstantItem(30).Text(list.IsOrdered ? $"{itemNumber}." : "•").FontSize(12);

                    // Content
                    row.RelativeItem().Column(itemColumn =>
                    {
                        foreach (var block in listItem)
                        {
                            RenderBlock(itemColumn, block);
                        }
                    });
                });

                if (list.IsOrdered)
                    itemNumber++;
            }
        }
    }

    private void RenderQuote(ColumnDescriptor column, QuoteBlock quote)
    {
        column.Item()
            .BorderLeft(4)
            .BorderColor(Colors.Blue.Lighten2)
            .PaddingLeft(15)
            .Column(quoteColumn =>
            {
                foreach (var block in quote)
                {
                    RenderBlock(quoteColumn, block);
                }
            });
    }

    private void RenderInlines(TextDescriptor text, ContainerInline? inline)
    {
        if (inline == null) return;

        foreach (var child in inline)
        {
            switch (child)
            {
                case LiteralInline literal:
                    text.Span(literal.Content.ToString());
                    break;

                case EmphasisInline emphasis:
                    var emphasisText = GetInlineText(emphasis);
                    if (emphasis.DelimiterCount == 2) // Bold
                        text.Span(emphasisText).Bold();
                    else // Italic
                        text.Span(emphasisText).Italic();
                    break;

                case CodeInline code:
                    text.Span(code.Content)
                        .FontFamily("Courier New")
                        .FontSize(10)
                        .BackgroundColor(Colors.Grey.Lighten3);
                    break;

                case LinkInline link:
                    var linkText = GetInlineText(link);
                    var url = link.Url ?? "";
                    text.Hyperlink(linkText, url)
                        .FontColor(Colors.Blue.Medium)
                        .Underline();
                    break;

                case LineBreakInline:
                    text.Span("\n");
                    break;
            }
        }
    }

    private string GetInlineText(ContainerInline container)
    {
        var result = "";
        foreach (var child in container)
        {
            if (child is LiteralInline literal)
                result += literal.Content.ToString();
            else if (child is ContainerInline containerChild)
                result += GetInlineText(containerChild);
        }
        return result;
    }
}
