using Microsoft.EntityFrameworkCore;
using Quartz;
using OTSSignsOrchestrator.Server.Data;
using OTSSignsOrchestrator.Server.Data.Entities;
using OTSSignsOrchestrator.Server.Reports;
using OTSSignsOrchestrator.Server.Services;

namespace OTSSignsOrchestrator.Server.Jobs;

/// <summary>
/// Quartz job with two triggers:
///   - Weekly (Monday 08:00 UTC): fleet health PDF → operator email list
///   - Monthly (1st of month 08:00 UTC): billing CSV + fleet health PDF → operators;
///     per-customer usage PDF → each active customer's admin email
/// </summary>
[DisallowConcurrentExecution]
public sealed class ScheduledReportJob : IJob
{
    /// <summary>Quartz job data key indicating whether this is a monthly trigger.</summary>
    public const string IsMonthlyKey = "IsMonthly";

    private readonly IServiceProvider _services;
    private readonly ILogger<ScheduledReportJob> _logger;

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

    public async Task Execute(IJobExecutionContext context)
    {
        var isMonthly = context.MergedJobDataMap.GetBoolean(IsMonthlyKey);

        _logger.LogInformation("ScheduledReportJob fired — isMonthly={IsMonthly}", isMonthly);

        await using var scope = _services.CreateAsyncScope();
        var billing = scope.ServiceProvider.GetRequiredService<BillingReportService>();
        var pdfService = scope.ServiceProvider.GetRequiredService<FleetHealthPdfService>();
        var emailService = scope.ServiceProvider.GetRequiredService<EmailService>();
        var db = scope.ServiceProvider.GetRequiredService<OrchestratorDbContext>();

        // Get operator email list (admin operators)
        var operatorEmails = await db.Operators
            .AsNoTracking()
            .Where(o => o.Role == OperatorRole.Admin)
            .Select(o => o.Email)
            .ToListAsync(context.CancellationToken);

        if (operatorEmails.Count == 0)
        {
            _logger.LogWarning("No admin operators found — skipping report email dispatch");
            return;
        }

        // ── Weekly: fleet health PDF for past 7 days ───────────────────────
        var to = DateOnly.FromDateTime(DateTime.UtcNow);
        var weekFrom = to.AddDays(-7);

        byte[] fleetPdf;
        try
        {
            fleetPdf = await pdfService.GenerateFleetHealthPdfAsync(weekFrom, to);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to generate weekly fleet health PDF");
            return;
        }

        foreach (var email in operatorEmails)
        {
            var success = await emailService.SendReportEmailAsync(
                email,
                $"fleet-health-{weekFrom:yyyy-MM-dd}-{to:yyyy-MM-dd}.pdf",
                fleetPdf,
                "application/pdf");

            if (!success)
                _logger.LogError("Failed to send weekly fleet health PDF to {Email}", email);
        }

        // ── Monthly: billing CSV + per-customer usage PDFs ─────────────────
        if (!isMonthly) return;

        var monthStart = new DateOnly(to.Year, to.Month, 1).AddMonths(-1);
        var monthEnd = monthStart.AddMonths(1).AddDays(-1);

        // Billing CSV
        byte[]? billingCsv = null;
        try
        {
            billingCsv = await billing.GenerateBillingCsvAsync(monthStart, monthEnd);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to generate monthly billing CSV");
        }

        // Monthly fleet health PDF
        byte[]? monthlyFleetPdf = null;
        try
        {
            monthlyFleetPdf = await pdfService.GenerateFleetHealthPdfAsync(monthStart, monthEnd);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to generate monthly fleet health PDF");
        }

        // Send monthly reports to operators
        foreach (var email in operatorEmails)
        {
            if (billingCsv is not null)
            {
                var ok = await emailService.SendReportEmailAsync(
                    email,
                    $"billing-{monthStart:yyyy-MM-dd}-{monthEnd:yyyy-MM-dd}.csv",
                    billingCsv,
                    "text/csv");
                if (!ok)
                    _logger.LogError("Failed to send monthly billing CSV to {Email}", email);
            }

            if (monthlyFleetPdf is not null)
            {
                var ok = await emailService.SendReportEmailAsync(
                    email,
                    $"fleet-health-{monthStart:yyyy-MM-dd}-{monthEnd:yyyy-MM-dd}.pdf",
                    monthlyFleetPdf,
                    "application/pdf");
                if (!ok)
                    _logger.LogError("Failed to send monthly fleet health PDF to {Email}", email);
            }
        }

        // Per-customer usage PDFs → customer admin emails
        var activeCustomers = await db.Customers
            .AsNoTracking()
            .Where(c => c.Status == CustomerStatus.Active)
            .ToListAsync(context.CancellationToken);

        foreach (var customer in activeCustomers)
        {
            try
            {
                var usagePdf = await pdfService.GenerateCustomerUsagePdfAsync(
                    customer.Id, monthStart, monthEnd);

                var ok = await emailService.SendReportEmailAsync(
                    customer.AdminEmail,
                    $"usage-{customer.Abbreviation}-{monthStart:yyyy-MM-dd}-{monthEnd:yyyy-MM-dd}.pdf",
                    usagePdf,
                    "application/pdf");

                if (!ok)
                    _logger.LogError("Failed to send usage PDF to {Email} for customer {Abbrev}",
                        customer.AdminEmail, customer.Abbreviation);
                else
                    _logger.LogInformation("Sent usage PDF to {Email} for customer {Abbrev}",
                        customer.AdminEmail, customer.Abbreviation);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to generate/send usage PDF for customer {Abbrev}",
                    customer.Abbreviation);
            }
        }

        _logger.LogInformation("Monthly report dispatch complete — {CustomerCount} customer reports processed",
            activeCustomers.Count);
    }
}
