using JSMR.Application.Jobs;
using JSMR.Domain.Entities;
using JSMR.Domain.Enums;
using Microsoft.EntityFrameworkCore;

namespace JSMR.Infrastructure.Data.Repositories.Jobs;

public sealed class JobRepository(AppDbContext dbContext) : IJobRepository
{
    public async Task<Job> AddAsync(Job job, CancellationToken cancellationToken)
    {
        await dbContext.Jobs.AddAsync(job, cancellationToken);

        return job;
    }

    public Task<Job?> GetByIdAsync(int id, CancellationToken cancellationToken)
        => dbContext.Jobs.FirstOrDefaultAsync(x => x.Id == id, cancellationToken);

    public async Task<IReadOnlyList<Job>> GetRecentAsync(int take, CancellationToken cancellationToken)
        => await dbContext.Jobs
            .OrderByDescending(x => x.CreatedUtc)
            .Take(take)
            .ToListAsync(cancellationToken);

    public Task<bool> AnyRunningAsync(CancellationToken cancellationToken)
        => dbContext.Jobs.AnyAsync(x => x.Status == JobStatus.Running, cancellationToken);

    public async Task<Job?> TryClaimNextQueuedAsync(string workerName, CancellationToken cancellationToken)
    {
        Job? next = await dbContext.Jobs
            .Where(x => x.Status == JobStatus.Queued)
            .OrderBy(x => x.CreatedUtc)
            .FirstOrDefaultAsync(cancellationToken);

        if (next is null)
            return null;

        next.Status = JobStatus.Running;
        next.StartedUtc = DateTime.UtcNow;
        next.HeartbeatUtc = DateTime.UtcNow;
        next.WorkerName = workerName;
        next.AttemptCount += 1;

        await dbContext.SaveChangesAsync(cancellationToken);

        return next;
    }

    public Task SaveChangesAsync(CancellationToken ct)
        => dbContext.SaveChangesAsync(ct);
}