using System.Globalization;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;

namespace Books.Api.Invoicing.Pdf;

/// <summary>
/// Generates professional Danish invoice PDFs using QuestPDF.
/// </summary>
public class InvoicePdfGenerator : IInvoicePdfGenerator
{
    // Danish color scheme - professional and minimal
    private static readonly string PrimaryColor = "#2C3E50";      // Dark blue-gray
    private static readonly string AccentColor = "#3498DB";       // Blue accent
    private static readonly string LightGray = "#ECF0F1";         // Light background
    private static readonly string TextColor = "#2C3E50";         // Main text
    private static readonly string MutedColor = "#7F8C8D";        // Secondary text

    // Danish culture for number formatting
    private static readonly CultureInfo DanishCulture = new("da-DK");

    public byte[] Generate(InvoicePdfData data)
    {
        var document = Document.Create(container =>
        {
            container.Page(page =>
            {
                page.Size(PageSizes.A4);
                page.MarginTop(2, Unit.Centimetre);
                page.MarginBottom(2, Unit.Centimetre);
                page.MarginHorizontal(2, Unit.Centimetre);
                page.DefaultTextStyle(x => x.FontSize(10).FontColor(TextColor));

                page.Header().Element(c => ComposeHeader(c, data));
                page.Content().Element(c => ComposeContent(c, data));
                page.Footer().Element(c => ComposeFooter(c, data));
            });
        });

        return document.GeneratePdf();
    }

    private void ComposeHeader(IContainer container, InvoicePdfData data)
    {
        container.Row(row =>
        {
            // Left: Company info
            row.RelativeItem(2).Column(col =>
            {
                col.Item().Text(data.CompanyName)
                    .FontSize(16).Bold().FontColor(PrimaryColor);

                if (!string.IsNullOrEmpty(data.CompanyAddress))
                    col.Item().Text(data.CompanyAddress);

                var cityLine = FormatCityLine(data.CompanyPostalCode, data.CompanyCity);
                if (!string.IsNullOrEmpty(cityLine))
                    col.Item().Text(cityLine);

                if (!string.IsNullOrEmpty(data.CompanyCvr))
                    col.Item().Text($"CVR: {data.CompanyCvr}").FontSize(9).FontColor(MutedColor);
            });

            // Right: FAKTURA/KREDITNOTA title + number
            row.RelativeItem(1).AlignRight().Column(col =>
            {
                var documentTitle = data.IsCreditNote ? "KREDITNOTA" : "FAKTURA";
                col.Item().Text(documentTitle)
                    .FontSize(28).Bold().FontColor(AccentColor);
                col.Item().Text($"Nr. {data.InvoiceNumber}")
                    .FontSize(12).FontColor(MutedColor);

                // For credit notes: show reference to original invoice
                if (data.IsCreditNote && !string.IsNullOrEmpty(data.OriginalInvoiceNumber))
                {
                    col.Item().Text($"Ref. faktura: {data.OriginalInvoiceNumber}")
                        .FontSize(10).FontColor(MutedColor);
                }
            });
        });
    }

    private void ComposeContent(IContainer container, InvoicePdfData data)
    {
        container.PaddingVertical(20).Column(col =>
        {
            // Customer address block + Invoice metadata (side by side)
            col.Item().Row(row =>
            {
                // Customer address
                row.RelativeItem().Column(custCol =>
                {
                    custCol.Item().Text("Faktureres til:").FontSize(9).FontColor(MutedColor);
                    custCol.Item().PaddingTop(5).Column(addrCol =>
                    {
                        addrCol.Item().Text(data.CustomerName).Bold();

                        if (!string.IsNullOrEmpty(data.CustomerAddress))
                            addrCol.Item().Text(data.CustomerAddress);

                        var cityLine = FormatCityLine(data.CustomerPostalCode, data.CustomerCity);
                        if (!string.IsNullOrEmpty(cityLine))
                            addrCol.Item().Text(cityLine);

                        if (!string.IsNullOrEmpty(data.CustomerCvr))
                            addrCol.Item().Text($"CVR: {data.CustomerCvr}").FontSize(9);
                    });
                });

                // Invoice/Credit note metadata
                row.RelativeItem().AlignRight().Table(table =>
                {
                    table.ColumnsDefinition(cols =>
                    {
                        cols.RelativeColumn();
                        cols.RelativeColumn();
                    });

                    var dateLabel = data.IsCreditNote ? "Kreditnotadato:" : "Fakturadato:";
                    AddMetadataRow(table, dateLabel, data.InvoiceDate.ToString("dd-MM-yyyy"));

                    if (!data.IsCreditNote)
                    {
                        AddMetadataRow(table, "Forfaldsdato:", data.DueDate.ToString("dd-MM-yyyy"));
                        AddMetadataRow(table, "Betalingsbetingelser:", $"Netto {data.PaymentTermsDays} dage");
                    }

                    if (!string.IsNullOrEmpty(data.CustomerNumber))
                        AddMetadataRow(table, "Kundenr.:", data.CustomerNumber);

                    if (!string.IsNullOrEmpty(data.Reference))
                        AddMetadataRow(table, "Reference:", data.Reference);

                    // For credit notes: show credit reason if available
                    if (data.IsCreditNote && !string.IsNullOrEmpty(data.CreditReason))
                        AddMetadataRow(table, "Årsag:", data.CreditReason);
                });
            });

            col.Item().PaddingVertical(20);

            // Line items table
            col.Item().Element(c => ComposeLineItemsTable(c, data));

            col.Item().PaddingVertical(10);

            // VAT summary and totals (right-aligned)
            col.Item().Row(row =>
            {
                row.RelativeItem(); // Spacer
                row.ConstantItem(250).Element(c => ComposeTotals(c, data));
            });

            // Notes section
            if (!string.IsNullOrEmpty(data.Notes))
            {
                col.Item().PaddingTop(20).Column(notesCol =>
                {
                    notesCol.Item().Text("Bemærkninger:").FontSize(9).FontColor(MutedColor);
                    notesCol.Item().PaddingTop(5).Text(data.Notes);
                });
            }
        });
    }

    private void ComposeLineItemsTable(IContainer container, InvoicePdfData data)
    {
        container.Table(table =>
        {
            // Column definitions
            table.ColumnsDefinition(cols =>
            {
                cols.ConstantColumn(50);    // Antal
                cols.RelativeColumn(3);     // Beskrivelse
                cols.ConstantColumn(80);    // Enhedspris
                cols.ConstantColumn(50);    // Rabat %
                cols.ConstantColumn(50);    // Moms %
                cols.ConstantColumn(80);    // Beløb
            });

            // Header row
            table.Header(header =>
            {
                header.Cell().Background(LightGray).Padding(5)
                    .Text("Antal").FontSize(9).Bold();
                header.Cell().Background(LightGray).Padding(5)
                    .Text("Beskrivelse").FontSize(9).Bold();
                header.Cell().Background(LightGray).Padding(5).AlignRight()
                    .Text("Enhedspris").FontSize(9).Bold();
                header.Cell().Background(LightGray).Padding(5).AlignRight()
                    .Text("Rabat").FontSize(9).Bold();
                header.Cell().Background(LightGray).Padding(5).AlignRight()
                    .Text("Moms").FontSize(9).Bold();
                header.Cell().Background(LightGray).Padding(5).AlignRight()
                    .Text("Beløb").FontSize(9).Bold();
            });

            // Data rows
            foreach (var line in data.Lines)
            {
                table.Cell().BorderBottom(1).BorderColor(LightGray).Padding(5)
                    .Text(FormatQuantity(line.Quantity, line.Unit));
                table.Cell().BorderBottom(1).BorderColor(LightGray).Padding(5)
                    .Text(line.Description);
                table.Cell().BorderBottom(1).BorderColor(LightGray).Padding(5).AlignRight()
                    .Text(FormatCurrency(line.UnitPrice, data.Currency));
                table.Cell().BorderBottom(1).BorderColor(LightGray).Padding(5).AlignRight()
                    .Text(line.DiscountPercent > 0 ? $"{line.DiscountPercent:N0}%" : "-");
                table.Cell().BorderBottom(1).BorderColor(LightGray).Padding(5).AlignRight()
                    .Text(FormatVatCode(line.VatCode));
                table.Cell().BorderBottom(1).BorderColor(LightGray).Padding(5).AlignRight()
                    .Text(FormatCurrency(line.AmountExVat, data.Currency));
            }
        });
    }

    private void ComposeTotals(IContainer container, InvoicePdfData data)
    {
        container.Column(col =>
        {
            // Subtotal
            col.Item().Row(row =>
            {
                row.RelativeItem().Text("Subtotal ex. moms:");
                row.ConstantItem(100).AlignRight().Text(FormatCurrency(data.AmountExVat, data.Currency));
            });

            // VAT summary lines
            foreach (var vatLine in data.VatSummary)
            {
                col.Item().Row(row =>
                {
                    var vatLabel = vatLine.VatRate > 0
                        ? $"Moms {FormatVatCode(vatLine.VatCode)} ({vatLine.VatRate:P0}):"
                        : $"Moms {FormatVatCode(vatLine.VatCode)}:";
                    row.RelativeItem().Text(vatLabel);
                    row.ConstantItem(100).AlignRight().Text(FormatCurrency(vatLine.VatAmount, data.Currency));
                });
            }

            col.Item().PaddingVertical(5).LineHorizontal(1).LineColor(PrimaryColor);

            // Grand total
            col.Item().Row(row =>
            {
                row.RelativeItem().Text("Total inkl. moms:").Bold().FontSize(12);
                row.ConstantItem(100).AlignRight()
                    .Text(FormatCurrency(data.AmountTotal, data.Currency)).Bold().FontSize(12);
            });
        });
    }

    private static void ComposeFooter(IContainer container, InvoicePdfData data)
    {
        container.Column(col =>
        {
            col.Item().LineHorizontal(1).LineColor(LightGray);
            col.Item().PaddingTop(10).Row(row =>
            {
                row.RelativeItem().Column(payCol =>
                {
                    if (data.IsCreditNote)
                    {
                        // Credit note footer - different message
                        payCol.Item().Text("Kreditnota").FontSize(9).Bold();
                        payCol.Item().PaddingTop(5)
                            .Text("Denne kreditnota modregnes automatisk i udestående fakturaer.")
                            .FontSize(8).FontColor(MutedColor);

                        if (!string.IsNullOrEmpty(data.OriginalInvoiceNumber))
                        {
                            payCol.Item().Text($"Vedrører faktura: {data.OriginalInvoiceNumber}")
                                .FontSize(8).FontColor(MutedColor);
                        }
                    }
                    else
                    {
                        // Invoice footer - payment info with bank details
                        payCol.Item().Text("Betalingsoplysninger:").FontSize(9).Bold();

                        if (!string.IsNullOrEmpty(data.CompanyBankName))
                            payCol.Item().Text($"Bank: {data.CompanyBankName}").FontSize(9);

                        if (!string.IsNullOrEmpty(data.CompanyBankRegNo) &&
                            !string.IsNullOrEmpty(data.CompanyBankAccountNo))
                        {
                            payCol.Item().Text($"Reg.nr.: {data.CompanyBankRegNo}  Kontonr.: {data.CompanyBankAccountNo}").FontSize(9);
                        }

                        if (!string.IsNullOrEmpty(data.CompanyIban))
                            payCol.Item().Text($"IBAN: {data.CompanyIban}").FontSize(9);

                        if (!string.IsNullOrEmpty(data.CompanyBic))
                            payCol.Item().Text($"BIC/SWIFT: {data.CompanyBic}").FontSize(9);

                        payCol.Item().PaddingTop(5)
                            .Text($"Ved betaling angiv venligst fakturanr.: {data.InvoiceNumber}")
                            .FontSize(8).FontColor(MutedColor);
                    }
                });

                // Page number
                row.RelativeItem().AlignRight().AlignBottom()
                    .Text(x =>
                    {
                        x.Span("Side ").FontSize(9);
                        x.CurrentPageNumber().FontSize(9);
                        x.Span(" af ").FontSize(9);
                        x.TotalPages().FontSize(9);
                    });
            });
        });
    }

    #region Formatting Helpers

    private static void AddMetadataRow(TableDescriptor table, string label, string value)
    {
        table.Cell().Text(label).FontSize(9).FontColor(MutedColor);
        table.Cell().AlignRight().Text(value).FontSize(9);
    }

    private static string FormatCityLine(string? postalCode, string? city)
    {
        var parts = new[] { postalCode, city }.Where(p => !string.IsNullOrEmpty(p));
        return string.Join(" ", parts);
    }

    private string FormatCurrency(decimal amount, string currency)
    {
        // Danish format: 1.234,56 kr
        var formatted = amount.ToString("N2", DanishCulture);
        return currency == "DKK" ? $"{formatted} kr" : $"{formatted} {currency}";
    }

    private static string FormatQuantity(decimal quantity, string? unit)
    {
        var formatted = quantity % 1 == 0
            ? quantity.ToString("N0")
            : quantity.ToString("N2");
        return unit != null ? $"{formatted} {unit}" : formatted;
    }

    private static string FormatVatCode(string vatCode) => vatCode switch
    {
        "U25" or "I25" => "25%",
        "UEU" or "IEU" => "EU 0%",
        "UEXP" or "IEXP" => "Eksport",
        "INGEN" => "0%",
        _ => vatCode
    };

    #endregion
}
