using DevExpress.Pdf;
using DXApp.TemplateKitProject.Models;

namespace DXApp.TemplateKitProject.Services;

public class PdfResultPackageService(
    IConfiguration configuration,
    ILogger<PdfResultPackageService> logger)
{
    public async Task<string?> CreateResultPackageAsync(
        byte[] originalPdfBytes,
        string originalFileName,
        ZugferdInvoice invoice)
    {
        // 1. Bericht-PDF suchen
        var reportPath = FindReportFile(originalFileName);
        if (reportPath is null)
        {
            logger.LogWarning(
                "Kein Ergebnisbericht gefunden für '{FileName}'.", originalFileName);
            return null;
        }

        logger.LogDebug(
            "Ergebnisbericht gefunden: '{ReportPath}'.", reportPath);

        // 2. Ausgabepfad bestimmen
        var outputDir = configuration["PdfResults:OutputDirectory"]
            ?? Path.Combine(Path.GetTempPath(), "PdfResults");
        Directory.CreateDirectory(outputDir);

        var baseName = Path.GetFileNameWithoutExtension(originalFileName);
        var outputPath = Path.Combine(outputDir, $"{baseName}_result.pdf");

        // 3. Original auf PDF/A-3b hochstufen + Bericht anhängen
        await Task.Run(() =>
        {
            using var inputStream = new MemoryStream(originalPdfBytes);

            // Schritt 1: PDF/A-3b Konvertierung
            PdfDocumentConverter converter;
            try
            {
                converter = new PdfDocumentConverter(inputStream);
                converter.Convert(PdfCompatibility.PdfA3b);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException(
                    "Konvertierung nach PDF/A-3b fehlgeschlagen. " +
                    "Die Originaldatei ist möglicherweise beschädigt oder nicht konvertierbar.", ex);
            }

            // Schritt 2: Konvertiertes PDF puffern
            using var convertedStream = new MemoryStream();
            converter.SaveDocument(convertedStream);
            convertedStream.Position = 0;

            // Schritt 3: Hauptdokument laden
            using var processor = new PdfDocumentProcessor();
            processor.LoadDocument(convertedStream);

            // Metadaten des Originals entfernen → Result-PDF gehört uns, nicht dem Ersteller
            processor.Document.Author   = "DXApp Verarbeitungssystem";
            processor.Document.Creator  = "DXApp";
            processor.Document.Producer = "DXApp";
            processor.Document.Title    = string.Empty;
            processor.Document.Subject  = string.Empty;
            processor.Document.Keywords = string.Empty;

            // Schritt 3a: Berichts-Seiten ans Ende anhängen
            int reportPageCount;
            using (var reportStream = new FileStream(reportPath, FileMode.Open, FileAccess.Read))
            {
                // Temporäres Dokument öffnen um Seitenanzahl zu ermitteln
                using var tempProcessor = new PdfDocumentProcessor();
                tempProcessor.LoadDocument(reportStream);
                reportPageCount = tempProcessor.Document.Pages.Count;

                logger.LogDebug(
                    "Hänge {PageCount} Seite(n) aus Bericht an das Dokument an.",
                    reportPageCount);

                // Stream zurücksetzen und ans Hauptdokument anhängen
                reportStream.Position = 0;
                processor.AppendDocument(reportStream);
            }

            // Schritt 4: Stempel auf Seite 1 zeichnen
            var firstPage = processor.Document.Pages[0];
            using (var graphics = processor.CreateGraphicsWorldSystem())
            {
                // Stempel-Position: oben rechts
                // Welt-Koordinaten: Ursprung oben links, X nach rechts, Y nach unten
                var stampX = (float)firstPage.CropBox.Width - 200;
                var stampY = 20f;
                var stampWidth = 175f;
                var stampHeight = 50f;

                // Hintergrund weiß
                using var whiteBrush = new DevExpress.Drawing.DXSolidBrush(
                    System.Drawing.Color.White);
                graphics.FillRectangle(whiteBrush,
                    new System.Drawing.RectangleF(stampX, stampY, stampWidth, stampHeight));

                // Rahmen grün
                using var greenPen = new DevExpress.Drawing.DXPen(
                    System.Drawing.Color.Green, 1.5f);
                graphics.DrawRectangle(greenPen,
                    new System.Drawing.RectangleF(stampX, stampY, stampWidth, stampHeight));

                // Text Zeile 1: VERARBEITET
                using var greenBrush = new DevExpress.Drawing.DXSolidBrush(
                    System.Drawing.Color.Green);
                var fontBold = new DevExpress.Drawing.DXFont(
                    "Arial", 11, DevExpress.Drawing.DXFontStyle.Bold);
                graphics.DrawString(
                    "✔ VERARBEITET",
                    fontBold,
                    greenBrush,
                    new System.Drawing.PointF(stampX + 8, stampY + 6));

                // Text Zeile 2: Datum/Uhrzeit
                using var grayBrush = new DevExpress.Drawing.DXSolidBrush(
                    System.Drawing.Color.DimGray);
                var fontNormal = new DevExpress.Drawing.DXFont("Arial", 9);
                graphics.DrawString(
                    DateTime.Now.ToString("dd.MM.yyyy HH:mm"),
                    fontNormal,
                    grayBrush,
                    new System.Drawing.PointF(stampX + 8, stampY + 28));

                graphics.AddToPageForeground(
                    processor.Document.Pages[0], 96, 96);
            }

            // Schritt 5: Speichern
            try
            {
                processor.SaveDocument(outputPath);
                
                logger.LogInformation(
                    "Result-PDF erstellt mit {TotalPages} Seiten (Original + {ReportPages} Berichtseiten).",
                    processor.Document.Pages.Count,
                    reportPageCount);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException(
                    $"Result-PDF konnte nicht gespeichert werden unter '{outputPath}'. " +
                    "Prüfe ob das Verzeichnis existiert und beschreibbar ist.", ex);
            }
        });

        logger.LogInformation(
            "Result-PDF gespeichert: '{OutputPath}'.", outputPath);

        return outputPath;
    }

    private string? FindReportFile(string originalFileName)
    {
        var inputDir = configuration["PdfResultReports:InputDirectory"]
            ?? Path.Combine(Path.GetTempPath(), "PdfResultReports");

        if (!Directory.Exists(inputDir))
        {
            logger.LogWarning("Berichtsverzeichnis nicht gefunden: '{Dir}'.", inputDir);
            return null;
        }

        // Konvention Option A: {originalname}_report.pdf
        var baseName = Path.GetFileNameWithoutExtension(originalFileName);
        var reportName = $"{baseName}_report.pdf";
        var reportPath = Path.Combine(inputDir, reportName);

        return File.Exists(reportPath) ? reportPath : null;
    }
}