using JobTrackerApi.Data;
using Microsoft.EntityFrameworkCore;

namespace JobTrackerApi.Services;

public sealed record BackgroundWorkerRunResult(bool Enabled, int Owners, int Succeeded, int Failed)
{
    public static readonly BackgroundWorkerRunResult Disabled = new(false, 0, 0, 0);
}

public sealed class BackgroundTenantRunner(
    IServiceScopeFactory scopes,
    ILogger<BackgroundTenantRunner> logger)
{
    public async Task<BackgroundWorkerRunResult> RunForJobOwnersAsync(
        string worker,
        Func<IServiceProvider, CancellationToken, Task> work,
        CancellationToken cancellationToken)
    {
        await using var enumerationScope = scopes.CreateAsyncScope();
        var ownerIds = await enumerationScope.ServiceProvider.GetRequiredService<JobTrackerContext>()
            .JobApplications.IgnoreQueryFilters().AsNoTracking()
            .Where(job => job.OwnerUserId != null)
            .Select(job => job.OwnerUserId!)
            .ToListAsync(cancellationToken);
        var owners = ownerIds.Where(owner => !string.IsNullOrWhiteSpace(owner))
            .Distinct(StringComparer.Ordinal)
            .Order(StringComparer.Ordinal)
            .ToList();

        var succeeded = 0;
        var failed = 0;
        foreach (var owner in owners)
        {
            await using var ownerScope = scopes.CreateAsyncScope();
            using var ownerContext = ownerScope.ServiceProvider.GetRequiredService<CurrentUserService>()
                .UseBackgroundUser(owner);
            try
            {
                await work(ownerScope.ServiceProvider, cancellationToken);
                succeeded++;
            }
            catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
            {
                throw;
            }
            catch (Exception ex)
            {
                failed++;
                logger.LogWarning("{Worker} owner pass failed with {FailureCategory}; private owner data was omitted from the log.", worker, ex.GetType().Name);
            }
        }

        logger.LogInformation("{Worker} pass complete: {Owners} owners, {Succeeded} succeeded, {Failed} failed.", worker, owners.Count, succeeded, failed);
        return new BackgroundWorkerRunResult(true, owners.Count, succeeded, failed);
    }
}
