using LiteCqrs;
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Broadcast;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;

namespace TeleWave.Application.Programming.Templates.GetTemplate;

public sealed class GetChannelTemplateQueryHandler(IAppDbContext dbContext)
    : IQueryHandler<GetChannelTemplateQuery, Result<ScheduleTemplateDto>>
{
    public async Task<Result<ScheduleTemplateDto>> Handle(
        GetChannelTemplateQuery query,
        CancellationToken cancellationToken
    )
    {
        var channel = await dbContext
            .Channels.AsNoTracking()
            .FirstOrDefaultAsync(c => c.Id == query.ChannelId, cancellationToken);
        if (channel is null)
            return Result.Failure<ScheduleTemplateDto>(ChannelErrors.NotFound);

        var template = await dbContext
            .ScheduleTemplates.AsNoTracking()
            .Include(t => t.Layers)
                .ThenInclude(l => l.Slots)
            .AsSplitQuery()
            .FirstOrDefaultAsync(t => t.ChannelId == channel.Id, cancellationToken);
        if (template is null)
            return Result.Failure<ScheduleTemplateDto>(ChannelErrors.TemplateNotFound);

        // Имена групп резолвим одним запросом: инспектор слота показывает их сразу, без второго обхода.
        var groupIds = template
            .Layers.SelectMany(l => l.Slots)
            .Select(s => s.GroupId)
            .Where(id => id is not null)
            .Select(id => id!.Value)
            .Distinct()
            .ToList();
        var groupNames = await dbContext
            .Groups.AsNoTracking()
            .Where(g => groupIds.Contains(g.Id))
            .ToDictionaryAsync(g => g.Id, g => g.Name, cancellationToken);

        var layers = template
            .Layers.OrderByDescending(l => l.Priority)
            .Select(layer => new GridLayerDto(
                layer.Id,
                layer.Name,
                layer.Priority,
                layer.IsEnabled,
                layer.IsBackground,
                LayerApplicability.FromJson(layer.ApplicabilityJson),
                layer
                    .Slots.OrderBy(s => s.Weekday ?? -1)
                    .ThenBy(s => s.TargetStart)
                    .Select(slot => new SlotDto(
                        slot.Id,
                        slot.LayerId,
                        slot.Weekday,
                        slot.TargetStart,
                        slot.TargetDurationMinutes,
                        slot.Title,
                        slot.Daypart,
                        slot.SlotKind,
                        slot.GroupId,
                        slot.GroupId is { } gid && groupNames.TryGetValue(gid, out var gname)
                            ? gname
                            : null,
                        SlotStrategy.FromJson(slot.StrategyJson),
                        RepeatSource.FromJson(slot.RepeatSourceJson),
                        slot.BlockMode,
                        slot.BlockValue,
                        slot.OverflowPolicy,
                        slot.IsAnchor,
                        slot.MaxDriftMinutes,
                        slot.SnapToMinutes,
                        slot.JunctionBetweenId,
                        slot.JunctionAfterId
                    ))
                    .ToList()
            ))
            .ToList();

        return Result.Success(
            new ScheduleTemplateDto(
                template.Id,
                template.ChannelId,
                template.Name,
                template.FallbackGroupId,
                template.DefaultJunctionId,
                PlanningRules.FromJson(template.RulesJson),
                template.Revision,
                template.AppliedRevision,
                template.HasPendingChanges,
                template.AppliedSnapshotJson is not null,
                channel.UtcOffsetMinutes,
                channel.DayStartTime,
                layers
            )
        );
    }
}
