using System.Text.Json;
using Microsoft.Extensions.Logging;
using Quartz;
using UniVerse.Application.DTOs.Notifications;
using UniVerse.Application.Interfaces;

namespace UniVerse.Infrastructure.Notifications;

public class QuartzNotificationScheduler : INotificationScheduler
{
    private const string NotificationGroup = "notifications";

    private readonly ISchedulerFactory _schedulerFactory;
    private readonly ILogger<QuartzNotificationScheduler> _logger;

    public QuartzNotificationScheduler(ISchedulerFactory schedulerFactory, ILogger<QuartzNotificationScheduler> logger)
    {
        _schedulerFactory = schedulerFactory;
        _logger = logger;
    }

    public async Task<ScheduledNotificationResponse> ScheduleAsync(
        NotificationMessage message,
        DateTimeOffset sendAt,
        string? jobId = null,
        CancellationToken cancellationToken = default)
    {
        if (sendAt <= DateTimeOffset.UtcNow)
            throw new ArgumentException("Scheduled notification time must be in the future.", nameof(sendAt));

        var scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
        jobId ??= Guid.NewGuid().ToString("N");
        var jobKey = new JobKey(jobId, NotificationGroup);
        var payload = JsonSerializer.Serialize(message);

        var job = JobBuilder.Create<NotificationJob>()
            .WithIdentity(jobKey)
            .UsingJobData(NotificationJob.MessageDataKey, payload)
            .Build();

        var trigger = TriggerBuilder.Create()
            .WithIdentity($"{jobId}.trigger", NotificationGroup)
            .ForJob(job)
            .StartAt(sendAt)
            .Build();

        await scheduler.ScheduleJob(job, trigger, cancellationToken);
        _logger.LogInformation("Scheduled notification job {JobId} for {SendAt}", jobId, sendAt);

        return new ScheduledNotificationResponse(jobId, sendAt);
    }

    public async Task CancelAsync(string jobId, CancellationToken cancellationToken = default)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(jobId);

        var scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
        var deleted = await scheduler.DeleteJob(new JobKey(jobId, NotificationGroup), cancellationToken);
        if (deleted)
            _logger.LogInformation("Cancelled notification job {JobId}", jobId);
    }
}
