using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using TeleWave.Application.Broadcast.Scheduling;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Programming.Planning;

namespace TeleWave.Infrastructure.Broadcast;

/// <summary>
/// Периодически достраивает расписание каждого включённого канала до горизонта. Расширение хвоста —
/// без удаления существующих записей, поэтому эфир не «дёргается».
/// </summary>
public sealed class SchedulingBackgroundService(
    IServiceScopeFactory scopeFactory,
    IOptions<SchedulerOptions> options,
    ILogger<SchedulingBackgroundService> logger
) : BackgroundService
{
    private readonly SchedulerOptions _options = options.Value;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // Небольшая задержка на старте — дать примениться миграциям/сидингу.
        try
        {
            await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
        }
        catch (OperationCanceledException)
        {
            return;
        }

        using var timer = new PeriodicTimer(
            TimeSpan.FromMinutes(Math.Max(1, _options.TickMinutes))
        );

        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 generator = scope.ServiceProvider.GetRequiredService<GridScheduleGenerator>();

        // Эфир строит только шаблон сетки: канал без шаблона вещать не может и молча пропускается.
        var channelIds = await db
            .Channels.Where(c => c.IsEnabled && c.TemplateId != null)
            .Select(c => c.Id)
            .ToListAsync(cancellationToken);

        var now = DateTimeOffset.UtcNow;
        foreach (var channelId in channelIds)
        {
            var report = await generator.GenerateAsync(
                channelId,
                now,
                rebuildFuture: false,
                cancellationToken
            );

            if (report.Added > 0)
                logger.LogInformation(
                    "Канал {ChannelId}: добавлено {Count} записей расписания",
                    channelId,
                    report.Added
                );

            foreach (var warning in report.Warnings)
                logger.LogWarning(
                    "Канал {ChannelId}, слот {SlotId}: {Warning} — {Details}",
                    channelId,
                    warning.SlotId,
                    warning.Kind,
                    warning.Details
                );
        }
    }
}
