using System.Globalization;
using System.Text;
using DevExpress.Office;
using DevExpress.XtraRichEdit;
using DevExpress.XtraRichEdit.API.Native;
using Microsoft.EntityFrameworkCore;
using ROLAC.API.Data;
using ROLAC.API.DTOs.Payee;
using ROLAC.API.Entities;

namespace ROLAC.API.Services;

/// <summary>
/// Produces recipient-facing 1099 outputs: a plain-paper Copy B 1099-NEC PDF (rendered with the
/// DevExpress RichEdit/Office API, mirroring <c>CheckPrintService</c>) and a filing-data CSV.
/// </summary>
public class Form1099FormService : I1099FormService
{
    private readonly IForm1099ReportService _report;
    private readonly IPayee1099Service _payees;
    private readonly AppDbContext _db;

    public Form1099FormService(IForm1099ReportService report, IPayee1099Service payees, AppDbContext db)
    {
        _report = report;
        _payees = payees;
        _db = db;
    }

    public async Task<(Stream stream, string contentType, string fileName)> RenderCopyBAsync(int payeeId, int taxYear)
    {
        var payee = await _payees.GetByIdAsync(payeeId)
            ?? throw new InvalidOperationException($"Payee {payeeId} not found.");

        var church = await _db.ChurchProfiles.AsNoTracking().OrderBy(x => x.Id).FirstOrDefaultAsync()
            ?? new ChurchProfile { Name = "Church" };

        // Box 1 (Nonemployee compensation) = sum of this payee's NEC-1 payments for the year.
        var detail = await _report.GetRecipientDetailAsync(payeeId, taxYear);
        var box1Nec = detail?.Payments
            .Where(payment => payment.BoxCode == Entities.Form1099.BoxNec1)
            .Sum(payment => payment.Amount) ?? 0m;

        using var server = new RichEditDocumentServer();
        var document = server.Document;
        document.BeginUpdate();
        try
        {
            document.Unit = DocumentUnit.Inch;
            var section = document.Sections[0];
            section.Page.Width = 8.5f;
            section.Page.Height = 11f;
            section.Margins.Left = section.Margins.Right = 0.8f;
            section.Margins.Top = section.Margins.Bottom = 0.8f;

            document.AppendHtmlText(BuildCopyBHtml(church, payee, taxYear, box1Nec));
        }
        finally
        {
            document.EndUpdate();
        }

        var stream = new MemoryStream();
        server.ExportToPdf(stream);
        stream.Position = 0;
        return (stream, "application/pdf", $"1099-NEC-{payeeId}-{taxYear}.pdf");
    }

    public async Task<(Stream stream, string contentType, string fileName)> ExportFilingCsvAsync(int taxYear)
    {
        var summary = await _report.GetAnnualSummaryAsync(taxYear);
        var builder = new StringBuilder();
        builder.AppendLine("LegalName,TinLast4,W9Status,Box1_NEC,Box1_Rents,Total,MeetsThreshold");
        foreach (var row in summary.Rows)
        {
            builder.AppendLine(string.Join(",",
                Csv(row.LegalName), Csv(row.TinLast4 ?? ""), Csv(row.W9Status),
                row.NecTotal.ToString(CultureInfo.InvariantCulture),
                row.RentsTotal.ToString(CultureInfo.InvariantCulture),
                row.GrandTotal.ToString(CultureInfo.InvariantCulture),
                row.MeetsThreshold ? "Y" : "N"));
        }

        var bytes = Encoding.UTF8.GetBytes(builder.ToString());
        return (new MemoryStream(bytes), "text/csv", $"1099-filing-{taxYear}.csv");

        static string Csv(string value) => value.Contains(',') || value.Contains('"')
            ? "\"" + value.Replace("\"", "\"\"") + "\"" : value;
    }

    private static string BuildCopyBHtml(ChurchProfile church, Payee1099Dto payee, int taxYear, decimal box1Nec)
    {
        var payerAddress = JoinAddress(church.Address, church.City, church.State, church.ZipCode);
        var recipientAddress = JoinAddress(
            JoinLines(payee.AddressLine1, payee.AddressLine2), payee.City, payee.State, payee.Zip);

        var payerEin  = string.IsNullOrWhiteSpace(church.PayerEin) ? "" : church.PayerEin;
        var maskedTin = string.IsNullOrWhiteSpace(payee.TinLast4) ? "" : $"***-**-{payee.TinLast4}";

        return
            "<div style=\"font-family:Arial;font-size:11pt;color:#111;\">" +
            $"<h2 style=\"text-align:center;margin:0;\">Form 1099-NEC &mdash; Copy B (For Recipient)</h2>" +
            $"<p style=\"text-align:center;margin:4px 0 16px 0;\"><b>Tax Year {taxYear}</b><br/>Nonemployee Compensation</p>" +

            "<table border=\"1\" cellspacing=\"0\" cellpadding=\"6\" width=\"100%\" style=\"border-collapse:collapse;\">" +

            "<tr><td width=\"50%\" valign=\"top\">" +
            "<b>PAYER&rsquo;s name, address</b><br/>" +
            $"{Encode(church.Name)}<br/>{payerAddress}" +
            "</td>" +
            "<td width=\"50%\" valign=\"top\">" +
            $"<b>PAYER&rsquo;s TIN (EIN)</b><br/>{Encode(payerEin)}" +
            "</td></tr>" +

            "<tr><td valign=\"top\">" +
            "<b>RECIPIENT&rsquo;s name, address</b><br/>" +
            $"{Encode(payee.LegalName)}<br/>{recipientAddress}" +
            "</td>" +
            "<td valign=\"top\">" +
            $"<b>RECIPIENT&rsquo;s TIN</b><br/>{Encode(maskedTin)}" +
            "</td></tr>" +

            "<tr><td colspan=\"2\">" +
            "<b>Box 1 &mdash; Nonemployee compensation</b><br/>" +
            $"<span style=\"font-size:14pt;\"><b>{Encode(FormatCurrency(box1Nec))}</b></span>" +
            "</td></tr>" +

            "</table>" +

            "<p style=\"font-size:8pt;color:#555;margin-top:12px;\">" +
            "This is important tax information and is being furnished to the recipient. " +
            "Recipient&rsquo;s taxpayer identification number is shown masked for security." +
            "</p>" +
            "</div>";
    }

    private static string Encode(string? text) => System.Net.WebUtility.HtmlEncode(text ?? "");

    private static string FormatCurrency(decimal amount) =>
        amount.ToString("C2", CultureInfo.GetCultureInfo("en-US"));

    private static string? JoinLines(string? line1, string? line2)
    {
        var parts = new[] { line1, line2 }.Where(part => !string.IsNullOrWhiteSpace(part));
        var joined = string.Join(", ", parts);
        return string.IsNullOrWhiteSpace(joined) ? null : joined;
    }

    // Builds an HTML address block; each text part is HTML-encoded and the line break (<br/>) is literal.
    private static string JoinAddress(string? address, string? city, string? state, string? zip)
    {
        var cityLine = string.Join(", ",
            new[] { city, string.Join(" ", new[] { state, zip }.Where(part => !string.IsNullOrWhiteSpace(part))) }
            .Where(part => !string.IsNullOrWhiteSpace(part)));
        var lines = new[] { address, cityLine }
            .Where(part => !string.IsNullOrWhiteSpace(part))
            .Select(Encode);
        return string.Join("<br/>", lines);
    }
}
