using System.IO.Compression;
using System.Text;
using System.Text.RegularExpressions;
using AuthorBuddy.Web.Models;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;

namespace AuthorBuddy.Web.Services;

public partial class ExportService : IExportService
{
    private readonly IProjectService _projectService;

    public ExportService(IProjectService projectService)
    {
        _projectService = projectService;
        QuestPDF.Settings.License = LicenseType.Community;
    }

    public async Task<byte[]> ExportAsync(int projectId, List<string> filePaths, ExportFormat format)
    {
        var project = await _projectService.GetProjectAsync(projectId);
        var projectName = project?.Name ?? "Projekt";

        var files = new List<(string name, string content)>();
        foreach (var path in filePaths)
        {
            if (File.Exists(path))
            {
                var content = await File.ReadAllTextAsync(path, Encoding.UTF8);
                var name = Path.GetFileName(path);
                files.Add((name, content));
            }
        }

        return format switch
        {
            ExportFormat.Html => GenerateHtml(projectName, files),
            ExportFormat.Markdown => GenerateMarkdown(projectName, files),
            ExportFormat.Zip => GenerateZip(files),
            ExportFormat.Docx => GenerateDocx(projectName, files),
            ExportFormat.Pdf => GeneratePdf(projectName, files),
            _ => throw new ArgumentOutOfRangeException(nameof(format))
        };
    }

    private static byte[] GenerateHtml(string projectName, List<(string name, string content)> files)
    {
        var sb = new StringBuilder();
        sb.AppendLine("<!DOCTYPE html>");
        sb.AppendLine("<html lang=\"de\">");
        sb.AppendLine("<head>");
        sb.AppendLine("<meta charset=\"utf-8\">");
        sb.AppendLine("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">");
        sb.AppendLine($"<title>{EscapeHtml(projectName)} – Export</title>");
        sb.AppendLine("<style>");
        sb.AppendLine(GetHtmlCss());
        sb.AppendLine("</style>");
        sb.AppendLine("</head>");
        sb.AppendLine("<body>");
        sb.AppendLine($"<header class=\"doc-header\"><h1>{EscapeHtml(projectName)}</h1><p class=\"doc-date\">Exportiert: {DateTime.Now:dd.MM.yyyy HH:mm}</p></header>");

        foreach (var (name, content) in files)
        {
            sb.AppendLine($"<section class=\"doc-section\"><h2>{EscapeHtml(name)}</h2><div class=\"doc-content\">{RenderMarkdownAsHtml(content)}</div></section>");
        }

        sb.AppendLine("</body>");
        sb.AppendLine("</html>");
        return Encoding.UTF8.GetBytes(sb.ToString());
    }

    private static byte[] GenerateMarkdown(string projectName, List<(string name, string content)> files)
    {
        var sb = new StringBuilder();
        sb.AppendLine($"# {projectName}");
        sb.AppendLine();
        sb.AppendLine($"> Exportiert: {DateTime.Now:dd.MM.yyyy HH:mm}");
        sb.AppendLine();
        sb.AppendLine("---");
        sb.AppendLine();

        foreach (var (name, content) in files)
        {
            sb.AppendLine($"## {name}");
            sb.AppendLine();
            sb.AppendLine(content.TrimEnd());
            sb.AppendLine();
            sb.AppendLine("---");
            sb.AppendLine();
        }

        return Encoding.UTF8.GetBytes(sb.ToString());
    }

    private static byte[] GenerateZip(List<(string name, string content)> files)
    {
        using var ms = new MemoryStream();
        using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
        {
            foreach (var (name, content) in files)
            {
                var entry = archive.CreateEntry(name, CompressionLevel.Optimal);
                using var writer = new StreamWriter(entry.Open(), Encoding.UTF8);
                writer.Write(content);
            }
        }
        return ms.ToArray();
    }

    private static byte[] GenerateDocx(string projectName, List<(string name, string content)> files)
    {
        using var ms = new MemoryStream();
        using var doc = WordprocessingDocument.Create(ms, WordprocessingDocumentType.Document);
        var mainPart = doc.AddMainDocumentPart();
        mainPart.Document = new DocumentFormat.OpenXml.Wordprocessing.Document();
        var body = mainPart.Document.AppendChild(new Body());

        body.AppendChild(new Paragraph(
            new Run(new RunProperties(new Bold()), new Text(projectName)))
        { ParagraphProperties = new ParagraphProperties(new Justification { Val = JustificationValues.Center }) });

        body.AppendChild(new Paragraph(
            new Run(new RunProperties(new DocumentFormat.OpenXml.Wordprocessing.Color { Val = "666666" }, new FontSize { Val = "20" }),
                new Text($"Exportiert: {DateTime.Now:dd.MM.yyyy HH:mm}"))));

        foreach (var (name, content) in files)
        {
            body.AppendChild(new Paragraph(
                new Run(new RunProperties(new Bold(), new FontSize { Val = "32" }), new Text(name))
            ));

            var lines = content.Split('\n');
            foreach (var raw in lines)
            {
                var line = raw.TrimEnd('\r');
                if (string.IsNullOrWhiteSpace(line))
                {
                    body.AppendChild(new Paragraph());
                    continue;
                }

                if (line.StartsWith("### "))
                {
                    body.AppendChild(new Paragraph(
                        new Run(new RunProperties(new Bold(), new FontSize { Val = "28" }), new Text(line[4..]))
                    ));
                }
                else if (line.StartsWith("## "))
                {
                    body.AppendChild(new Paragraph(
                        new Run(new RunProperties(new Bold(), new FontSize { Val = "32" }), new Text(line[3..]))
                    ));
                }
                else if (line.StartsWith("# "))
                {
                    body.AppendChild(new Paragraph(
                        new Run(new RunProperties(new Bold(), new FontSize { Val = "36" }), new Text(line[2..]))
                    ));
                }
                else if (line.StartsWith("- ") || line.StartsWith("* "))
                {
                    body.AppendChild(new Paragraph(
                        new Run(new Text($"  • {line[2..]}"))
                    ));
                }
                else if (line.Length > 2 && char.IsDigit(line[0]) && line[1] == '.' && line[2] == ' ')
                {
                    body.AppendChild(new Paragraph(
                        new Run(new Text($"  {line}"))
                    ));
                }
                else
                {
                    body.AppendChild(new Paragraph(
                        new Run(new Text(line))
                    ));
                }
            }
        }

        doc.Save();
        return ms.ToArray();
    }

    private static byte[] GeneratePdf(string projectName, List<(string name, string content)> files)
    {
        var doc = QuestPDF.Fluent.Document.Create(container =>
        {
            container.Page(page =>
            {
                page.Size(PageSizes.A4);
                page.Margin(40);
                page.DefaultTextStyle(x => x.FontSize(11).FontFamily("Times New Roman"));

                page.Header().Column(col =>
                {
                    col.Item().AlignCenter().Text(projectName).Bold().FontSize(22);
                    col.Item().AlignCenter().Text($"Exportiert: {DateTime.Now:dd.MM.yyyy HH:mm}").FontSize(10).FontColor(Colors.Grey.Medium);
                });

                page.Content().Column(col =>
                {
                    foreach (var (name, content) in files)
                    {
                        col.Item().PaddingTop(12).Text(name).Bold().FontSize(16);
                        col.Item().PaddingLeft(4).Element(c => RenderMarkdownToPdf(c, content));
                    }
                });

                page.Footer().AlignCenter().Text(x =>
                {
                    x.Span("Seite ").FontSize(9);
                    x.CurrentPageNumber().FontSize(9);
                });
            });
        });

        using var ms = new MemoryStream();
        doc.GeneratePdf(ms);
        return ms.ToArray();
    }

    private static void RenderMarkdownToPdf(IContainer container, string md)
    {
        var lines = md.Split('\n');
        container.Column(col =>
        {
            foreach (var raw in lines)
            {
                var line = raw.TrimEnd('\r');
                if (string.IsNullOrWhiteSpace(line))
                {
                    col.Item().Height(8);
                    continue;
                }

                if (line.StartsWith("### "))
                {
                    col.Item().PaddingTop(6).Text(line[4..]).Bold().FontSize(13);
                }
                else if (line.StartsWith("## "))
                {
                    col.Item().PaddingTop(8).Text(line[3..]).Bold().FontSize(15);
                }
                else if (line.StartsWith("# "))
                {
                    col.Item().PaddingTop(10).Text(line[2..]).Bold().FontSize(17);
                }
                else if (line.StartsWith("- ") || line.StartsWith("* "))
                {
                    col.Item().PaddingLeft(12).Text($"  • {line[2..]}").FontSize(11);
                }
                else if (line is "---" or "***" or "___")
                {
                    col.Item().Height(1).Background(Colors.Grey.Lighten2);
                }
                else if (line.Length > 2 && char.IsDigit(line[0]) && line[1] == '.' && line[2] == ' ')
                {
                    col.Item().PaddingLeft(12).Text($"  {line}").FontSize(11);
                }
                else
                {
                    col.Item().Text(line).FontSize(11);
                }
            }
        });
    }

    private static string EscapeHtml(string text)
        => text.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace("\"", "&quot;");

    private static string RenderMarkdownAsHtml(string md)
    {
        var lines = md.Split('\n');
        var sb = new StringBuilder();
        var inList = false;

        foreach (var raw in lines)
        {
            var line = raw.TrimEnd('\r');

            if (line.StartsWith("###### "))
            {
                CloseList(ref inList, sb);
                sb.AppendLine($"<h6>{EscapeHtml(line[7..])}</h6>");
                continue;
            }
            if (line.StartsWith("##### "))
            {
                CloseList(ref inList, sb);
                sb.AppendLine($"<h5>{EscapeHtml(line[6..])}</h5>");
                continue;
            }
            if (line.StartsWith("#### "))
            {
                CloseList(ref inList, sb);
                sb.AppendLine($"<h4>{EscapeHtml(line[5..])}</h4>");
                continue;
            }
            if (line.StartsWith("### "))
            {
                CloseList(ref inList, sb);
                sb.AppendLine($"<h3>{EscapeHtml(line[4..])}</h3>");
                continue;
            }
            if (line.StartsWith("## "))
            {
                CloseList(ref inList, sb);
                sb.AppendLine($"<h2>{EscapeHtml(line[3..])}</h2>");
                continue;
            }
            if (line.StartsWith("# "))
            {
                CloseList(ref inList, sb);
                sb.AppendLine($"<h1>{EscapeHtml(line[2..])}</h1>");
                continue;
            }

            if (line.StartsWith("- ") || line.StartsWith("* "))
            {
                if (!inList)
                {
                    inList = true;
                    sb.AppendLine("<ul>");
                }
                sb.AppendLine($"<li>{RenderInline(line[2..])}</li>");
                continue;
            }

            if (line.Length > 2 && char.IsDigit(line[0]) && (line[1] == '.' || line[1] == ')') && line[2] == ' ')
            {
                if (!inList)
                {
                    inList = true;
                    sb.AppendLine("<ol>");
                }
                sb.AppendLine($"<li>{RenderInline(line[3..])}</li>");
                continue;
            }

            if (line is "---" or "***" or "___")
            {
                CloseList(ref inList, sb);
                sb.AppendLine("<hr>");
                continue;
            }

            if (string.IsNullOrWhiteSpace(line))
            {
                CloseList(ref inList, sb);
                continue;
            }

            CloseList(ref inList, sb);
            sb.AppendLine($"<p>{RenderInline(line)}</p>");
        }

        CloseList(ref inList, sb);
        return sb.ToString();
    }

    private static void CloseList(ref bool inList, StringBuilder sb)
    {
        if (inList)
        {
            sb.AppendLine(sb[sb.Length - 1] == '>' ? "</ol>" : "</ul>");
            inList = false;
        }
    }

    private static string RenderInline(string text)
    {
        var result = EscapeHtml(text);
        result = BoldRegex().Replace(result, "<strong>$1</strong>");
        result = ItalicRegex().Replace(result, "<em>$1</em>");
        result = UnderlineRegex().Replace(result, "<u>$1</u>");
        result = CodeRegex().Replace(result, "<code>$1</code>");
        result = LinkRegex().Replace(result, "<a href=\"$2\">$1</a>");
        return result;
    }

    private static string GetHtmlCss()
    {
        return """
            * { margin: 0; padding: 0; box-sizing: border-box; }
            body { font-family: Georgia, 'Times New Roman', serif; font-size: 12pt; line-height: 1.6; color: #222; max-width: 210mm; margin: 0 auto; padding: 20mm 30mm; background: #fff; }
            .doc-header { text-align: center; margin-bottom: 30px; padding-bottom: 15px; border-bottom: 2px solid #333; }
            .doc-header h1 { font-size: 22pt; margin-bottom: 4px; }
            .doc-date { font-size: 10pt; color: #666; }
            .doc-section { margin-bottom: 25px; page-break-before: auto; }
            .doc-section h2 { font-size: 16pt; margin-bottom: 10px; padding-bottom: 3px; border-bottom: 1px solid #ccc; color: #333; }
            .doc-content h1 { font-size: 18pt; margin: 15px 0 8px; }
            .doc-content h2 { font-size: 15pt; margin: 12px 0 6px; }
            .doc-content h3 { font-size: 13pt; margin: 10px 0 5px; }
            .doc-content h4 { font-size: 12pt; margin: 8px 0 4px; }
            .doc-content h5 { font-size: 11pt; margin: 6px 0 3px; }
            .doc-content h6 { font-size: 10pt; margin: 6px 0 3px; color: #555; }
            .doc-content p { margin: 6px 0; text-align: justify; }
            .doc-content ul, .doc-content ol { margin: 6px 0 6px 20px; }
            .doc-content li { margin: 2px 0; }
            .doc-content hr { margin: 15px 0; border: none; border-top: 1px solid #ccc; }
            .doc-content code { font-family: 'Consolas', 'Courier New', monospace; font-size: 10pt; background: #f4f4f4; padding: 1px 4px; border-radius: 2px; }
            .doc-content a { color: #1a73e8; text-decoration: underline; }
            .doc-content strong { font-weight: bold; }
            .doc-content em { font-style: italic; }
            @media print { body { padding: 0; } .doc-section { page-break-before: always; } .doc-section:first-of-type { page-break-before: auto; } }
            """;
    }

    [GeneratedRegex(@"\*\*(.+?)\*\*")]
    private static partial Regex BoldRegex();
    [GeneratedRegex(@"\*(.+?)\*")]
    private static partial Regex ItalicRegex();
    [GeneratedRegex(@"<u>(.+?)</u>")]
    private static partial Regex UnderlineRegex();
    [GeneratedRegex(@"`(.+?)`")]
    private static partial Regex CodeRegex();
    [GeneratedRegex(@"\[([^\]]+)\]\(([^)]+)\)")]
    private static partial Regex LinkRegex();
}
