using LiteCqrs;
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;
using TeleWave.Application.Library;
using TeleWave.Domain.Media;

namespace TeleWave.Application.Media.ManualInbox;

public sealed class ImportManualInboxCommandHandler(
    IAppDbContext dbContext,
    ManualInboxTaker taker,
    IMediaProcessingQueue queue
) : ICommandHandler<ImportManualInboxCommand, Result<ImportManualInboxResultDto>>
{
    public async Task<Result<ImportManualInboxResultDto>> Handle(
        ImportManualInboxCommand command,
        CancellationToken cancellationToken
    )
    {
        var show = await dbContext
            .Shows.Include(s => s.Episodes)
            .FirstOrDefaultAsync(s => s.Id == command.ShowId, cancellationToken);
        if (show is null)
            return Result.Failure<ImportManualInboxResultDto>(ShowErrors.NotFound);

        var available = taker.List();

        var failed = new List<ImportFailureDto>();
        var imported = 0;

        // Файлы разбираются по одному и независимо: один неподдерживаемый контейнер не должен
        // отменять весь пакет — в ручном разборе это как раз частый случай.
        foreach (var item in command.Items.DistinctBy(i => i.RelativePath, StringComparer.Ordinal))
        {
            if (!show.CanAddEpisode)
            {
                failed.Add(
                    new ImportFailureDto(
                        item.RelativePath,
                        ShowErrors.SingleAlreadyHasEpisode.Message
                    )
                );
                continue;
            }

            var taken = await taker.TakeAsync(available, item.RelativePath, cancellationToken);
            if (!taken.IsSuccess)
            {
                failed.Add(new ImportFailureDto(item.RelativePath, taken.Error.Message));
                continue;
            }

            var episode = show.AddEpisode(taken.Value);
            SetNumbers(episode, item, available[item.RelativePath].Name);

            queue.Enqueue(taken.Value);
            imported++;

            await taker.CleanupAsync(item.RelativePath, cancellationToken);
        }

        return Result.Success(new ImportManualInboxResultDto(imported, failed));
    }

    /// <summary>
    /// Номера серии: сначала то, что пришло с клиента (там пользователь видел разбор и мог поправить
    /// его регуляркой или ручным сезоном), иначе — разбор имени на сервере.
    /// </summary>
    private static void SetNumbers(
        Domain.Library.ShowEpisode episode,
        ManualImportItem item,
        string fileName
    )
    {
        if (item.Episode is { } episodeNumber)
        {
            episode.SetNumbers(item.Season ?? 1, episodeNumber);
            return;
        }

        if (EpisodeName.Parse(fileName) is { } parsed)
            episode.SetNumbers(item.Season ?? parsed.Season, parsed.Episode);
    }
}
