using Microsoft.EntityFrameworkCore;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using OTSSignsOrchestrator.Server.Data;
using OTSSignsOrchestrator.Server.Data.Entities;

namespace OTSSignsOrchestrator.Server.Reports;

public class FleetHealthPdfService
{
    private readonly IServiceProvider _services;
    private readonly ILogger<FleetHealthPdfService> _logger;

    // OTS Signs brand colours
    private static readonly XColor BrandAccent = XColor.FromArgb(0x3B, 0x82, 0xF6);
    private static readonly XColor HeaderBg = XColor.FromArgb(0x1E, 0x29, 0x3B);
    private static readonly XColor TextDark = XColor.FromArgb(0x1F, 0x28, 0x37);
    private static readonly XColor TextMuted = XColor.FromArgb(0x6B, 0x72, 0x80);
    private static readonly XColor TableBorder = XColor.FromArgb(0xD1, 0xD5, 0xDB);
    private static readonly XColor RowAlt = XColor.FromArgb(0xF9, 0xFA, 0xFB);

    private const double PageWidth = 595;  // A4 points
    private const double PageHeight = 842;
    private const double MarginX = 50;
    private const double ContentWidth = PageWidth - 2 * MarginX;

    public FleetHealthPdfService(IServiceProvider services, ILogger<FleetHealthPdfService> logger)
    {
        _services = services;
        _logger = logger;
    }

    /// <summary>
    /// Generates a fleet-wide health PDF report for the given date range.
    /// Includes: branded header, fleet summary stats, per-instance health table,
    /// Authentik uptime percentage.
    /// </summary>
    public async Task<byte[]> GenerateFleetHealthPdfAsync(DateOnly from, DateOnly to)
    {
        await using var scope = _services.CreateAsyncScope();
        var db = scope.ServiceProvider.GetRequiredService<OrchestratorDbContext>();

        var fromDt = from.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
        var toDt = to.ToDateTime(TimeOnly.MaxValue, DateTimeKind.Utc);

        var instances = await db.Instances
            .AsNoTracking()
            .Include(i => i.Customer)
            .ToListAsync();

        var healthEvents = await db.HealthEvents
            .AsNoTracking()
            .Where(h => h.OccurredAt >= fromDt && h.OccurredAt <= toDt)
            .ToListAsync();

        var screenSnapshots = await db.ScreenSnapshots
            .AsNoTracking()
            .Where(s => s.SnapshotDate >= from && s.SnapshotDate <= to)
            .ToListAsync();

        var authentikMetrics = await db.AuthentikMetrics
            .AsNoTracking()
            .Where(m => m.CheckedAt >= fromDt && m.CheckedAt <= toDt)
            .ToListAsync();

        // Compute stats
        var totalInstances = instances.Count;
        var totalScreens = screenSnapshots
            .GroupBy(s => s.InstanceId)
            .Sum(g => g.OrderByDescending(s => s.SnapshotDate).First().ScreenCount);

        var healthBreakdown = instances.GroupBy(i => i.HealthStatus)
            .ToDictionary(g => g.Key, g => g.Count());

        var authentikUptime = ComputeAuthentikUptime(authentikMetrics);

        var latestHealthByInstance = healthEvents
            .GroupBy(h => h.InstanceId)
            .ToDictionary(g => g.Key, g => g.OrderByDescending(h => h.OccurredAt).First());

        var p1CountByInstance = healthEvents
            .Where(h => h.Status == HealthEventStatus.Critical)
            .GroupBy(h => h.InstanceId)
            .ToDictionary(g => g.Key, g => g.Count());

        // Build PDF
        using var doc = new PdfDocument();
        doc.Info.Title = "OTS Signs — Fleet Health Report";
        doc.Info.Author = "OTS Signs Orchestrator";
        var page = doc.AddPage();
        page.Width = PageWidth;
        page.Height = PageHeight;
        var gfx = XGraphics.FromPdfPage(page);

        var y = DrawBrandedHeader(gfx, "Fleet Health Report", from, to);

        // Summary stats
        var fontBold = new XFont("Helvetica", 12, XFontStyle.Bold);
        var fontNormal = new XFont("Helvetica", 10);

        y = DrawSectionTitle(gfx, "Fleet Summary", y);

        var summaryData = new (string Label, string Value)[]
        {
            ("Total Instances", totalInstances.ToString()),
            ("Total Screens", totalScreens.ToString()),
            ("Healthy", healthBreakdown.GetValueOrDefault(HealthStatus.Healthy).ToString()),
            ("Degraded", healthBreakdown.GetValueOrDefault(HealthStatus.Degraded).ToString()),
            ("Critical", healthBreakdown.GetValueOrDefault(HealthStatus.Critical).ToString()),
            ("Unknown", healthBreakdown.GetValueOrDefault(HealthStatus.Unknown).ToString()),
            ("Authentik Uptime", $"{authentikUptime:F1}%"),
        };

        foreach (var (label, value) in summaryData)
        {
            gfx.DrawString(label + ":", fontBold, new XSolidBrush(TextDark),
                new XRect(MarginX, y, 150, 16), XStringFormats.TopLeft);
            gfx.DrawString(value, fontNormal, new XSolidBrush(TextDark),
                new XRect(MarginX + 160, y, 150, 16), XStringFormats.TopLeft);
            y += 18;
        }

        y += 10;

        // Per-instance health table
        y = DrawSectionTitle(gfx, "Per-Instance Health", y);

        var columns = new (string Header, double Width)[]
        {
            ("Abbrev", 60), ("Status", 70), ("Last Check", 110), ("P1 Inc.", 50), ("URL", ContentWidth - 290),
        };

        y = DrawTableHeader(gfx, columns, y);

        var rowIndex = 0;
        foreach (var instance in instances.OrderBy(i => i.Customer.Abbreviation))
        {
            if (y > PageHeight - 60)
            {
                page = doc.AddPage();
                page.Width = PageWidth;
                page.Height = PageHeight;
                gfx = XGraphics.FromPdfPage(page);
                y = 50;
                y = DrawTableHeader(gfx, columns, y);
                rowIndex = 0;
            }

            latestHealthByInstance.TryGetValue(instance.Id, out var latestHealth);
            p1CountByInstance.TryGetValue(instance.Id, out var p1Count);

            var cellValues = new[]
            {
                instance.Customer.Abbreviation,
                instance.HealthStatus.ToString(),
                latestHealth?.OccurredAt.ToString("yyyy-MM-dd HH:mm") ?? "—",
                p1Count.ToString(),
                instance.XiboUrl,
            };

            y = DrawTableRow(gfx, columns, cellValues, y, rowIndex % 2 == 1);
            rowIndex++;
        }

        _logger.LogInformation("Generated fleet health PDF: {Instances} instances, period {From} to {To}",
            totalInstances, from, to);

        return SavePdf(doc);
    }

    /// <summary>
    /// Generates a per-customer usage PDF for the given date range.
    /// </summary>
    public async Task<byte[]> GenerateCustomerUsagePdfAsync(Guid customerId, DateOnly from, DateOnly to)
    {
        await using var scope = _services.CreateAsyncScope();
        var db = scope.ServiceProvider.GetRequiredService<OrchestratorDbContext>();

        var customer = await db.Customers
            .AsNoTracking()
            .Include(c => c.Instances)
            .FirstOrDefaultAsync(c => c.Id == customerId)
            ?? throw new InvalidOperationException($"Customer {customerId} not found.");

        var snapshots = await db.ScreenSnapshots
            .AsNoTracking()
            .Where(s => s.Instance.CustomerId == customerId
                && s.SnapshotDate >= from
                && s.SnapshotDate <= to)
            .OrderBy(s => s.SnapshotDate)
            .ToListAsync();

        using var doc = new PdfDocument();
        doc.Info.Title = $"OTS Signs — Usage Report — {customer.CompanyName}";
        doc.Info.Author = "OTS Signs Orchestrator";
        var page = doc.AddPage();
        page.Width = PageWidth;
        page.Height = PageHeight;
        var gfx = XGraphics.FromPdfPage(page);

        var y = DrawBrandedHeader(gfx, $"Customer Usage Report — {customer.CompanyName}", from, to);

        // Customer details
        y = DrawSectionTitle(gfx, "Customer Details", y);
        var fontBold = new XFont("Helvetica", 10, XFontStyle.Bold);
        var fontNormal = new XFont("Helvetica", 10);

        var details = new (string Label, string Value)[]
        {
            ("Company", customer.CompanyName),
            ("Abbreviation", customer.Abbreviation),
            ("Plan", customer.Plan.ToString()),
            ("Screen Quota", customer.ScreenCount.ToString()),
            ("Status", customer.Status.ToString()),
        };

        var primaryInstance = customer.Instances.FirstOrDefault();
        if (primaryInstance is not null)
            details = [.. details, ("Instance URL", primaryInstance.XiboUrl)];

        foreach (var (label, value) in details)
        {
            gfx.DrawString(label + ":", fontBold, new XSolidBrush(TextDark),
                new XRect(MarginX, y, 120, 16), XStringFormats.TopLeft);
            gfx.DrawString(value, fontNormal, new XSolidBrush(TextDark),
                new XRect(MarginX + 130, y, 350, 16), XStringFormats.TopLeft);
            y += 18;
        }

        y += 10;

        // Screen count history
        y = DrawSectionTitle(gfx, "Screen Count History", y);

        if (snapshots.Count == 0)
        {
            var fontItalic = new XFont("Helvetica", 10, XFontStyle.Italic);
            gfx.DrawString("No screen snapshot data available for this period.",
                fontItalic, new XSolidBrush(TextMuted),
                new XRect(MarginX, y, ContentWidth, 16), XStringFormats.TopLeft);
            y += 20;
        }
        else
        {
            var byDate = snapshots
                .GroupBy(s => s.SnapshotDate)
                .OrderBy(g => g.Key)
                .Select(g => (Date: g.Key, Count: g.Sum(s => s.ScreenCount)))
                .ToList();

            // Table
            var cols = new (string Header, double Width)[] { ("Date", 150), ("Screen Count", 150) };
            y = DrawTableHeader(gfx, cols, y);
            var rowIdx = 0;
            foreach (var (date, count) in byDate)
            {
                if (y > PageHeight - 60)
                {
                    page = doc.AddPage();
                    page.Width = PageWidth;
                    page.Height = PageHeight;
                    gfx = XGraphics.FromPdfPage(page);
                    y = 50;
                    y = DrawTableHeader(gfx, cols, y);
                    rowIdx = 0;
                }
                y = DrawTableRow(gfx, cols, [date.ToString("yyyy-MM-dd"), count.ToString()], y, rowIdx % 2 == 1);
                rowIdx++;
            }

            y += 20;

            // Simple bar chart
            if (y + byDate.Count * 14 + 30 > PageHeight - 40)
            {
                page = doc.AddPage();
                page.Width = PageWidth;
                page.Height = PageHeight;
                gfx = XGraphics.FromPdfPage(page);
                y = 50;
            }

            y = DrawSectionTitle(gfx, "Daily Screen Count", y);
            var maxCount = byDate.Max(d => d.Count);
            if (maxCount > 0)
            {
                var barMaxWidth = ContentWidth - 120;
                var fontSmall = new XFont("Helvetica", 8);
                var barBrush = new XSolidBrush(BrandAccent);

                foreach (var (date, count) in byDate)
                {
                    var barWidth = Math.Max(2, (double)count / maxCount * barMaxWidth);
                    gfx.DrawString(date.ToString("MM-dd"), fontSmall, new XSolidBrush(TextDark),
                        new XRect(MarginX, y, 50, 12), XStringFormats.TopLeft);
                    gfx.DrawRectangle(barBrush, MarginX + 55, y + 1, barWidth, 10);
                    gfx.DrawString(count.ToString(), fontSmall, new XSolidBrush(TextDark),
                        new XRect(MarginX + 60 + barWidth, y, 50, 12), XStringFormats.TopLeft);
                    y += 14;
                }
            }
        }

        _logger.LogInformation("Generated customer usage PDF for {CustomerId} ({Abbrev}), period {From} to {To}",
            customerId, customer.Abbreviation, from, to);

        return SavePdf(doc);
    }

    // ── Drawing helpers ─────────────────────────────────────────────────────

    private static double DrawBrandedHeader(XGraphics gfx, string title, DateOnly from, DateOnly to)
    {
        var y = 40.0;

        var brandFont = new XFont("Helvetica", 22, XFontStyle.Bold);
        gfx.DrawString("OTS Signs", brandFont, new XSolidBrush(BrandAccent),
            new XRect(MarginX, y, ContentWidth, 28), XStringFormats.TopLeft);
        y += 30;

        var titleFont = new XFont("Helvetica", 14, XFontStyle.Bold);
        gfx.DrawString(title, titleFont, new XSolidBrush(TextDark),
            new XRect(MarginX, y, ContentWidth, 20), XStringFormats.TopLeft);
        y += 22;

        var dateFont = new XFont("Helvetica", 9);
        gfx.DrawString($"Period: {from:yyyy-MM-dd} to {to:yyyy-MM-dd}  |  Generated: {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC",
            dateFont, new XSolidBrush(TextMuted),
            new XRect(MarginX, y, ContentWidth, 14), XStringFormats.TopLeft);
        y += 18;

        // Separator
        gfx.DrawLine(new XPen(BrandAccent, 1.5), MarginX, y, PageWidth - MarginX, y);
        y += 15;

        return y;
    }

    private static double DrawSectionTitle(XGraphics gfx, string title, double y)
    {
        var font = new XFont("Helvetica", 13, XFontStyle.Bold);
        gfx.DrawString(title, font, new XSolidBrush(TextDark),
            new XRect(MarginX, y, ContentWidth, 18), XStringFormats.TopLeft);
        return y + 22;
    }

    private static double DrawTableHeader(XGraphics gfx, (string Header, double Width)[] columns, double y)
    {
        var font = new XFont("Helvetica", 9, XFontStyle.Bold);
        var brush = new XSolidBrush(XColors.White);
        var bgBrush = new XSolidBrush(HeaderBg);
        var x = MarginX;

        var totalWidth = columns.Sum(c => c.Width);
        gfx.DrawRectangle(bgBrush, x, y, totalWidth, 18);

        foreach (var (header, width) in columns)
        {
            gfx.DrawString(header, font, brush, new XRect(x + 4, y + 3, width - 8, 14), XStringFormats.TopLeft);
            x += width;
        }

        return y + 18;
    }

    private static double DrawTableRow(XGraphics gfx, (string Header, double Width)[] columns, string[] values, double y, bool alternate)
    {
        var font = new XFont("Helvetica", 8);
        var textBrush = new XSolidBrush(TextDark);
        var x = MarginX;
        var totalWidth = columns.Sum(c => c.Width);

        if (alternate)
            gfx.DrawRectangle(new XSolidBrush(RowAlt), x, y, totalWidth, 16);

        gfx.DrawLine(new XPen(TableBorder, 0.5), x, y + 16, x + totalWidth, y + 16);

        for (int i = 0; i < columns.Length && i < values.Length; i++)
        {
            gfx.DrawString(values[i], font, textBrush,
                new XRect(x + 4, y + 2, columns[i].Width - 8, 14), XStringFormats.TopLeft);
            x += columns[i].Width;
        }

        return y + 16;
    }

    private static double ComputeAuthentikUptime(List<AuthentikMetrics> metrics)
    {
        if (metrics.Count == 0) return 100.0;
        var healthy = metrics.Count(m => m.Status == AuthentikMetricsStatus.Healthy);
        return (double)healthy / metrics.Count * 100;
    }

    private static byte[] SavePdf(PdfDocument doc)
    {
        using var stream = new MemoryStream();
        doc.Save(stream, false);
        return stream.ToArray();
    }
}
