using ClosedXML.Excel;
using CsvHelper;
using CsvHelper.Configuration;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using System.Globalization;
using System.Text;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Services;

namespace TakeoutSaaS.Infrastructure.App.Services;

/// <summary>
/// 账单导出服务实现（Excel/PDF/CSV）。
/// </summary>
public sealed class BillingExportService : IBillingExportService
{
    /// <summary>
    /// 初始化导出服务并配置 QuestPDF 许可证。
    /// </summary>
    public BillingExportService()
    {
        QuestPDF.Settings.License = LicenseType.Community;
    }

    /// <inheritdoc />
    public Task<byte[]> ExportToExcelAsync(IReadOnlyList<TenantBillingStatement> billings, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(billings);

        // 1. 创建工作簿与工作表
        using var workbook = new XLWorkbook();
        var worksheet = workbook.Worksheets.Add("Billings");

        // 2. 写入表头
        var headers = new[]
        {
            "Id", "TenantId", "StatementNo", "BillingType", "Status",
            "PeriodStart", "PeriodEnd", "AmountDue", "DiscountAmount", "TaxAmount", "TotalAmount",
            "AmountPaid", "Currency", "DueDate", "Notes", "LineItemsJson"
        };

        for (var i = 0; i < headers.Length; i++)
        {
            worksheet.Cell(1, i + 1).Value = headers[i];
        }

        // 3. 写入数据行
        for (var rowIndex = 0; rowIndex < billings.Count; rowIndex++)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var billing = billings[rowIndex];
            var totalAmount = billing.CalculateTotalAmount();
            var r = rowIndex + 2;

            worksheet.Cell(r, 1).Value = billing.Id;
            worksheet.Cell(r, 2).Value = billing.TenantId;
            worksheet.Cell(r, 3).Value = billing.StatementNo;
            worksheet.Cell(r, 4).Value = billing.BillingType.ToString();
            worksheet.Cell(r, 5).Value = billing.Status.ToString();
            worksheet.Cell(r, 6).Value = billing.PeriodStart.ToString("O", CultureInfo.InvariantCulture);
            worksheet.Cell(r, 7).Value = billing.PeriodEnd.ToString("O", CultureInfo.InvariantCulture);
            worksheet.Cell(r, 8).Value = billing.AmountDue;
            worksheet.Cell(r, 9).Value = billing.DiscountAmount;
            worksheet.Cell(r, 10).Value = billing.TaxAmount;
            worksheet.Cell(r, 11).Value = totalAmount;
            worksheet.Cell(r, 12).Value = billing.AmountPaid;
            worksheet.Cell(r, 13).Value = billing.Currency;
            worksheet.Cell(r, 14).Value = billing.DueDate.ToString("O", CultureInfo.InvariantCulture);
            worksheet.Cell(r, 15).Value = billing.Notes ?? string.Empty;
            worksheet.Cell(r, 16).Value = billing.LineItemsJson ?? string.Empty;
        }

        // 4. 自动调整列宽并输出
        worksheet.Columns().AdjustToContents();

        using var stream = new MemoryStream();
        workbook.SaveAs(stream);
        return Task.FromResult(stream.ToArray());
    }

    /// <inheritdoc />
    public Task<byte[]> ExportToPdfAsync(IReadOnlyList<TenantBillingStatement> billings, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(billings);

        // 1. 生成 PDF 文档（避免复杂表格，按条目输出）
        var document = Document.Create(container =>
        {
            container.Page(page =>
            {
                page.Size(PageSizes.A4);
                page.Margin(20);
                page.DefaultTextStyle(x => x.FontSize(10));

                page.Content().Column(column =>
                {
                    column.Spacing(6);

                    // 2. 标题
                    column.Item().Text("Billings Export").FontSize(16).SemiBold();

                    // 3. 逐条输出
                    for (var i = 0; i < billings.Count; i++)
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        var b = billings[i];
                        var total = b.CalculateTotalAmount();

                        column.Item().Border(1).BorderColor(Colors.Grey.Lighten2).Padding(8).Column(item =>
                        {
                            item.Spacing(2);
                            item.Item().Text($"StatementNo: {b.StatementNo}");
                            item.Item().Text($"TenantId: {b.TenantId}    BillingType: {b.BillingType}    Status: {b.Status}");
                            item.Item().Text($"Period: {b.PeriodStart:yyyy-MM-dd} ~ {b.PeriodEnd:yyyy-MM-dd}    DueDate: {b.DueDate:yyyy-MM-dd}");
                            item.Item().Text($"AmountDue: {b.AmountDue:0.##}    Discount: {b.DiscountAmount:0.##}    Tax: {b.TaxAmount:0.##}");
                            item.Item().Text($"Total: {total:0.##}    Paid: {b.AmountPaid:0.##}    Currency: {b.Currency}");

                            if (!string.IsNullOrWhiteSpace(b.Notes))
                            {
                                item.Item().Text($"Notes: {b.Notes}");
                            }
                        });
                    }
                });
            });
        });

        // 4. 输出字节
        var bytes = document.GeneratePdf();
        return Task.FromResult(bytes);
    }

    /// <inheritdoc />
    public async Task<byte[]> ExportToCsvAsync(IReadOnlyList<TenantBillingStatement> billings, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(billings);

        // 1. 使用 UTF-8 BOM，便于 Excel 直接打开
        await using var stream = new MemoryStream();
        await using var writer = new StreamWriter(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true), leaveOpen: true);

        var config = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
            HasHeaderRecord = true
        };

        await using var csv = new CsvWriter(writer, config);

        // 2. 写入表头
        csv.WriteField("Id");
        csv.WriteField("TenantId");
        csv.WriteField("StatementNo");
        csv.WriteField("BillingType");
        csv.WriteField("Status");
        csv.WriteField("PeriodStart");
        csv.WriteField("PeriodEnd");
        csv.WriteField("AmountDue");
        csv.WriteField("DiscountAmount");
        csv.WriteField("TaxAmount");
        csv.WriteField("TotalAmount");
        csv.WriteField("AmountPaid");
        csv.WriteField("Currency");
        csv.WriteField("DueDate");
        csv.WriteField("Notes");
        csv.WriteField("LineItemsJson");
        await csv.NextRecordAsync();

        // 3. 写入数据行
        foreach (var b in billings)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var total = b.CalculateTotalAmount();

            csv.WriteField(b.Id);
            csv.WriteField(b.TenantId);
            csv.WriteField(b.StatementNo);
            csv.WriteField(b.BillingType.ToString());
            csv.WriteField(b.Status.ToString());
            csv.WriteField(b.PeriodStart.ToString("O", CultureInfo.InvariantCulture));
            csv.WriteField(b.PeriodEnd.ToString("O", CultureInfo.InvariantCulture));
            csv.WriteField(b.AmountDue);
            csv.WriteField(b.DiscountAmount);
            csv.WriteField(b.TaxAmount);
            csv.WriteField(total);
            csv.WriteField(b.AmountPaid);
            csv.WriteField(b.Currency);
            csv.WriteField(b.DueDate.ToString("O", CultureInfo.InvariantCulture));
            csv.WriteField(b.Notes ?? string.Empty);
            csv.WriteField(b.LineItemsJson ?? string.Empty);

            await csv.NextRecordAsync();
        }

        // 4. Flush 并返回字节
        await writer.FlushAsync(cancellationToken);
        return stream.ToArray();
    }
}
