using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Tables;
using MigraDoc.Rendering;
using PolyTrader.Modules.Accounting.Models;

namespace PolyTrader.Modules.Accounting.Logic
{
    /// <summary>
    /// PDF-Export der neutralen Abrechnung (A-4) via PDFsharp/MigraDoc (MIT, keine Umsatzschwelle).
    /// Kopf + Aggregat-Tabelle + Monatsvergleich + Transaktionsliste + Methodik-/Nachweis-Seite.
    /// Länderneutral (KEINE steuerliche Einordnung – die käme aus A-3). Beträge in der übergebenen
    /// Anzeige-Währung (USDC/USD/EUR); die Umrechnung erfolgt außerhalb (Services), hier nur Faktor.
    /// </summary>
    public static class PdfExporter
    {
        public static byte[] Render(
            PeriodStatement statement,
            IReadOnlyList<PeriodStatement> monthly,
            IReadOnlyList<LedgerEntry> periodEntries,
            string currencyCode, decimal currencyFactor, string currencyNote)
        {
            decimal V(decimal usdc) => Math.Round(usdc * currencyFactor, 2, MidpointRounding.AwayFromZero);
            string M(decimal usdc) => V(usdc).ToString("N2") + " " + currencyCode;

            // Schriftauflösung sicherstellen: der PDFsharp-Core-Build findet ohne Resolver keine
            // Systemschriften (siehe PdfFontResolver). Idempotent, kostet nach dem ersten Mal nichts.
            PdfFontResolver.EnsureRegistered();

            var doc = new Document();
            doc.Info.Title = "Buchhalterische Abrechnung";
            var style = doc.Styles["Normal"];
            // Wunschschrift; der Resolver weicht auf eine verfügbare aus, wenn es sie nicht gibt
            // (auf Linux z.B. DejaVu Sans).
            style.Font.Name = "Segoe UI";
            style.Font.Size = 9;

            var section = doc.AddSection();
            section.PageSetup.Orientation = MigraDoc.DocumentObjectModel.Orientation.Landscape;
            section.PageSetup.LeftMargin = Unit.FromCentimeter(1.5);
            section.PageSetup.RightMargin = Unit.FromCentimeter(1.5);

            // ---- Kopf ----
            var head = section.AddParagraph("Buchhalterische Abrechnung (neutral)");
            head.Format.Font.Size = 16; head.Format.Font.Bold = true;
            head.Format.SpaceAfter = Unit.FromMillimeter(2);

            var meta = section.AddParagraph();
            meta.Format.SpaceAfter = Unit.FromMillimeter(4);
            meta.AddText($"Konto: {(statement.AccountId?.ToString() ?? "alle Live-Konten")}");
            meta.AddLineBreak();
            meta.AddText($"Zeitraum: {statement.From:yyyy-MM-dd} bis {statement.To:yyyy-MM-dd}");
            meta.AddLineBreak();
            meta.AddText($"Währung: {currencyCode} ({currencyNote})");
            meta.AddLineBreak();
            meta.AddText($"Erstellt: {DateTime.Now:yyyy-MM-dd HH:mm}");

            // ---- Aggregat ----
            AddSectionTitle(section, "Zusammenfassung");
            var agg = NewTable(section, new[] { 8.0, 6.0 });
            AddKeyValue(agg, "Anfangssaldo", M(statement.OpeningBalanceUsdc));
            AddKeyValue(agg, "Einzahlungen", M(statement.Deposits));
            AddKeyValue(agg, "Auszahlungen", M(statement.Withdrawals));
            AddKeyValue(agg, "Handelsvolumen", M(statement.TradeVolume));
            AddKeyValue(agg, "Redeems", M(statement.Redeems));
            AddKeyValue(agg, "Rewards", M(statement.Rewards));
            AddKeyValue(agg, "Fees", M(statement.Fees));
            AddKeyValue(agg, "Netto-Handelsergebnis (Cash-Basis)", M(statement.NetTradingResultUsdc));
            AddKeyValue(agg, "Endsaldo", M(statement.ClosingBalanceUsdc));
            AddKeyValue(agg, "Anzahl Trades", statement.TradeCount.ToString());
            AddKeyValue(agg, "Anzahl Buchungen", statement.EntryCount.ToString());

            // ---- Monatsvergleich ----
            if (monthly.Count > 1)
            {
                AddSectionTitle(section, "Monatsvergleich");
                var mt = NewTable(section, new[] { 3.0, 3.5, 3.5, 3.5, 3.0, 3.0, 3.5, 3.5, 2.0 });
                HeaderRow(mt, "Monat", "Anfang", "Einz.", "Ausz.", "Volumen", "Rewards", "Fees", "Ergebnis", "Endsaldo");
                foreach (var m in monthly)
                    DataRow(mt, m.From.ToString("yyyy-MM"), M(m.OpeningBalanceUsdc), M(m.Deposits), M(m.Withdrawals),
                        M(m.TradeVolume), M(m.Rewards), M(m.Fees), M(m.NetTradingResultUsdc), M(m.ClosingBalanceUsdc));
            }

            // ---- Transaktionsliste ----
            AddSectionTitle(section, $"Transaktionen ({periodEntries.Count})");
            var lt = NewTable(section, new[] { 3.5, 3.0, 2.0, 6.0, 2.5, 2.5, 2.5, 5.5 });
            HeaderRow(lt, "Zeit (UTC)", "Typ", "Side", "Markt", "Size", "Preis", "Netto", "TxHash");
            foreach (var e in periodEntries.OrderBy(e => e.Timestamp))
                DataRow(lt, e.Timestamp.ToString("yyyy-MM-dd HH:mm"), e.EventType.ToString(), e.Side,
                    Trim(e.MarketSlug, 40), e.Size.ToString("0.###"), e.PriceUsdc.ToString("0.###"),
                    M(e.NetUsdc), Trim(e.TxHash, 22));

            // ---- Methodik / Nachweis ----
            AddSectionTitle(section, "Methodik & Nachweis");
            var method = section.AddParagraph();
            method.Format.Font.Size = 8;
            method.AddText("• Buchungsgrundlage sind ausschließlich unabhängige Polymarket-/On-Chain-Abrufe (nicht die Trading-DB), append-only.");
            method.AddLineBreak();
            method.AddText("• Netto-Handelsergebnis ist Cash-Basis (Erlöse − Kosten − Fees) und schließt Ein-/Auszahlungen aus.");
            method.AddLineBreak();
            method.AddText($"• Währungsumrechnung: {currencyNote} USDC ist die native Buchungswährung.");
            method.AddLineBreak();
            method.AddText("• Dies ist eine neutrale, prüfbare Aufstellung und KEINE Steuerberatung. Eine steuerliche Einordnung erfolgt getrennt.");
            method.AddLineBreak();
            method.AddText($"• Daten-Hash (SHA-256 über Ledger+Aggregat): {DataHash(statement, periodEntries)}");

            var renderer = new PdfDocumentRenderer { Document = doc };
            renderer.RenderDocument();
            using var ms = new MemoryStream();
            renderer.PdfDocument.Save(ms, false);
            return ms.ToArray();
        }

        // ---- MigraDoc-Helfer ----

        private static void AddSectionTitle(Section s, string text)
        {
            var p = s.AddParagraph(text);
            p.Format.Font.Size = 12; p.Format.Font.Bold = true;
            p.Format.SpaceBefore = Unit.FromMillimeter(4); p.Format.SpaceAfter = Unit.FromMillimeter(1);
        }

        private static Table NewTable(Section s, double[] widthsCm)
        {
            var t = s.AddTable();
            t.Borders.Width = 0.25; t.Borders.Color = Colors.LightGray;
            foreach (var w in widthsCm) t.AddColumn(Unit.FromCentimeter(w));
            return t;
        }

        private static void AddKeyValue(Table t, string key, string value)
        {
            var r = t.AddRow();
            r.Cells[0].AddParagraph(key);
            var vp = r.Cells[1].AddParagraph(value);
            vp.Format.Alignment = ParagraphAlignment.Right;
        }

        private static void HeaderRow(Table t, params string[] cells)
        {
            var r = t.AddRow();
            r.Shading.Color = Colors.WhiteSmoke;
            for (int i = 0; i < cells.Length; i++)
            {
                var p = r.Cells[i].AddParagraph(cells[i]);
                p.Format.Font.Bold = true;
            }
        }

        private static void DataRow(Table t, params string[] cells)
        {
            var r = t.AddRow();
            for (int i = 0; i < cells.Length; i++)
                r.Cells[i].AddParagraph(cells[i] ?? string.Empty);
        }

        private static string Trim(string s, int max) =>
            string.IsNullOrEmpty(s) ? string.Empty : (s.Length <= max ? s : s[..max] + "…");

        private static string DataHash(PeriodStatement s, IReadOnlyList<LedgerEntry> entries)
        {
            string material = CsvExporter.Statement(s) + CsvExporter.Ledger(entries);
            byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(material));
            return Convert.ToHexString(hash)[..16].ToLowerInvariant();
        }
    }
}
