using System.Text.Json;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using Microsoft.EntityFrameworkCore;

namespace JobTrackerApi.Services;

public sealed class DailyExportHostedService(
    BackgroundTenantRunner tenants,
    ILogger<DailyExportHostedService> logger,
    IConfiguration configuration,
    AppPaths paths,
    IStartupReadiness startupReadiness,
    TimeProvider timeProvider) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await startupReadiness.WaitUntilReadyAsync(stoppingToken);
        if (!IsEnabled())
        {
            logger.LogInformation("Daily export worker disabled; both Workers:DailyExportEnabled and Exports:DailyEnabled must be true.");
            return;
        }

        var hour = configuration.GetValue("Exports:DailyHourLocal", 2);
        if (hour is < 0 or > 23) hour = 2;
        while (!stoppingToken.IsCancellationRequested)
        {
            var now = timeProvider.GetLocalNow().DateTime;
            var next = new DateTime(now.Year, now.Month, now.Day, hour, 0, 0);
            if (next <= now) next = next.AddDays(1);
            logger.LogInformation("Next daily export scheduled at {Next}.", next);
            await Task.Delay(next - now, timeProvider, stoppingToken);
            await RunOnceAsync(stoppingToken);
        }
    }

    public Task<BackgroundWorkerRunResult> RunOnceAsync(CancellationToken cancellationToken)
    {
        if (!IsEnabled()) return Task.FromResult(BackgroundWorkerRunResult.Disabled);
        return tenants.RunForJobOwnersAsync("daily-export", ExportOwnerAsync, cancellationToken);
    }

    private bool IsEnabled() =>
        configuration.GetValue("Workers:DailyExportEnabled", false) &&
        configuration.GetValue("Exports:DailyEnabled", true);

    private async Task ExportOwnerAsync(IServiceProvider services, CancellationToken cancellationToken)
    {
        var db = services.GetRequiredService<JobTrackerContext>();
        var owner = db.CurrentUserId ?? throw new InvalidOperationException("Daily export requires an explicit owner scope.");
        var now = timeProvider.GetLocalNow().DateTime;
        var jobs = await db.JobApplications.AsNoTracking().OrderByDescending(job => job.DateApplied).ToListAsync(cancellationToken);
        var jobIds = jobs.Select(job => job.Id).ToList();
        var export = new
        {
            Version = "dailyexport.v2",
            CreatedAt = now,
            OwnerUserId = owner,
            Companies = await db.Companies.AsNoTracking().OrderBy(company => company.Name).ToListAsync(cancellationToken),
            JobApplications = jobs,
            Correspondence = await db.Correspondences.AsNoTracking().Where(message => jobIds.Contains(message.JobApplicationId)).OrderBy(message => message.Date).ToListAsync(cancellationToken),
            Attachments = await db.Attachments.AsNoTracking().Where(attachment => jobIds.Contains(attachment.JobApplicationId)).OrderBy(attachment => attachment.UploadDate).ToListAsync(cancellationToken),
            Events = await db.JobEvents.AsNoTracking().Where(jobEvent => jobIds.Contains(jobEvent.JobApplicationId)).OrderBy(jobEvent => jobEvent.At).ToListAsync(cancellationToken),
            EmailSendAttempts = await db.EmailSendAttempts.AsNoTracking()
                .Where(attempt => jobIds.Contains(attempt.JobApplicationId))
                .OrderBy(attempt => attempt.CreatedAtUtc)
                .Select(attempt => new EmailSendAttemptExport(
                    attempt.Id,
                    attempt.JobApplicationId,
                    attempt.Provider,
                    attempt.ClientRequestId,
                    attempt.Status,
                    attempt.ProviderMessageId,
                    attempt.FailureCategory,
                    attempt.CreatedAtUtc,
                    attempt.StartedAtUtc,
                    attempt.CompletedAtUtc))
                .ToListAsync(cancellationToken),
            EmailDrafts = await db.EmailDrafts.AsNoTracking()
                .Where(draft => jobIds.Contains(draft.JobApplicationId))
                .OrderBy(draft => draft.UpdatedAtUtc)
                .Select(draft => new EmailDraftExport(
                    draft.Id,
                    draft.JobApplicationId,
                    draft.Provider,
                    draft.To,
                    draft.Subject,
                    draft.BodyText,
                    draft.ThreadId,
                    draft.ClientRequestId,
                    draft.Revision,
                    draft.CreatedAtUtc,
                    draft.UpdatedAtUtc))
                .ToListAsync(cancellationToken),
            Rules = await RulesEngine.GetSettings(db, cancellationToken),
        };

        var folder = paths.GetOwnerDailyExportsRoot(configuration["Exports:DailyFolder"], owner);
        Directory.CreateDirectory(folder);
        var finalPath = Path.Combine(folder, $"daily_export_{now:yyyyMMdd}.json");
        var temporaryPath = finalPath + $".{Guid.NewGuid():N}.tmp";
        try
        {
            await File.WriteAllTextAsync(temporaryPath, JsonSerializer.Serialize(export, new JsonSerializerOptions { WriteIndented = true }), cancellationToken);
            File.Move(temporaryPath, finalPath, overwrite: true);
        }
        finally
        {
            if (File.Exists(temporaryPath)) File.Delete(temporaryPath);
        }
    }
}
