using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using TeleWave.Application.Common.Interfaces;

namespace TeleWave.Infrastructure.Broadcast;

/// <summary>
/// Обслуживание, которое раньше не делалось вовсе: чистка отрендеренных заставок, на которые больше
/// никто не ссылается. Они копятся при каждой смене пары шоу и при каждой правке блока заставки,
/// а после перехода на длинное хранение расписания их станет заметно больше.
///
/// Идёт по расписанию отдельно от генерации: та держит advisory-лок канала, и подмешивать в неё
/// удаление по всей таблице не нужно.
/// </summary>
public sealed class MaintenanceBackgroundService(
    IServiceScopeFactory scopeFactory,
    ILogger<MaintenanceBackgroundService> logger
) : BackgroundService
{
    private static readonly TimeSpan Interval = TimeSpan.FromHours(6);
    private static readonly TimeSpan StartupDelay = TimeSpan.FromMinutes(2);

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            await Task.Delay(StartupDelay, stoppingToken);
        }
        catch (OperationCanceledException)
        {
            return;
        }

        using var timer = new PeriodicTimer(Interval);

        do
        {
            try
            {
                await TickAsync(stoppingToken);
            }
            catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
            {
                break;
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Ошибка обслуживания");
            }
        } while (await timer.WaitForNextTickAsync(stoppingToken));
    }

    private async Task TickAsync(CancellationToken cancellationToken)
    {
        await using var scope = scopeFactory.CreateAsyncScope();
        var db = scope.ServiceProvider.GetRequiredService<IAppDbContext>();

        // Осиротевшей считается заставка, чей ассет не встречается ни в одной записи расписания.
        // Границу по времени не ставим: окно хранения расписания уже определяет, что живо.
        var removed = await db
            .BumperAssets.Where(b => !db.ScheduleEntries.Any(e => e.MediaAssetId == b.MediaAssetId))
            .ExecuteDeleteAsync(cancellationToken);

        if (removed > 0)
            logger.LogInformation("Обслуживание: удалено осиротевших заставок — {Count}", removed);
    }
}
