using JSMR.Application.VoiceWorks.Commands.Delete;
using JSMR.Application.VoiceWorks.Commands.SetFavorite;
using JSMR.Application.VoiceWorks.Ports;
using JSMR.Domain.Entities;
using Microsoft.EntityFrameworkCore;

namespace JSMR.Infrastructure.Data.Repositories.VoiceWorks;

public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter
{
    public async Task<SetVoiceWorkFavoriteResponse> SetFavoriteAsync(SetVoiceWorkFavoriteRequest request, CancellationToken cancellationToken)
    {
        VoiceWork voiceWork = await GetVoiceWorkAsync(request.VoiceWorkId, cancellationToken);
        voiceWork.Favorite = request.IsFavorite;

        await dbContext.SaveChangesAsync(cancellationToken);

        return new SetVoiceWorkFavoriteResponse(request.VoiceWorkId, request.IsFavorite);
    }

    public async Task<DeleteVoiceWorkResponse> DeleteAsync(DeleteVoiceWorkRequest request, CancellationToken cancellationToken)
    {
        Dictionary<int, DeleteVoiceWorkStatus> results = request.VoiceWorkIds.Select(x => x)
            .ToDictionary(x => x, x => DeleteVoiceWorkStatus.NotFound);

        VoiceWork[] voiceWorks = await dbContext.VoiceWorks
            .Where(voiceWork => request.VoiceWorkIds.Contains(voiceWork.VoiceWorkId))
            .Include(x => x.Circle)
            .ToArrayAsync(cancellationToken);

        List<VoiceWork> voiceWorksToDelete = [];

        foreach (VoiceWork voiceWork in voiceWorks)
        {
            if (results.ContainsKey(voiceWork.VoiceWorkId) == false)
            {
                results[voiceWork.VoiceWorkId] = DeleteVoiceWorkStatus.NotFound;
                continue;
            }

            if (voiceWork.Circle is null)
            {
                results[voiceWork.VoiceWorkId] = DeleteVoiceWorkStatus.NotFound;
                continue;
            }

            if (voiceWork.IsValid == true && voiceWork.Circle.Spam == false)
            {
                results[voiceWork.VoiceWorkId] = DeleteVoiceWorkStatus.NotAllowed;
                continue;
            }

            voiceWorksToDelete.Add(voiceWork);
            results[voiceWork.VoiceWorkId] = DeleteVoiceWorkStatus.Deleted;
        }

        int[] voiceWorkIdsToDelete = [.. voiceWorksToDelete.Select(x => x.VoiceWorkId)];

        if (voiceWorkIdsToDelete.Length > 0)
        {
            dbContext.VoiceWorks.RemoveRange(voiceWorksToDelete);
            await dbContext.SaveChangesAsync(cancellationToken);

            await dbContext.VoiceWorkSearches
                .Where(x => voiceWorkIdsToDelete.Contains(x.VoiceWorkId))
                .ExecuteDeleteAsync(cancellationToken);
        }

        return new DeleteVoiceWorkResponse(results);
    }

    private async Task<VoiceWork> GetVoiceWorkAsync(int voiceWorkId, CancellationToken cancellationToken)
    {
        return await dbContext.VoiceWorks.FirstOrDefaultAsync(voiceWork => voiceWork.VoiceWorkId == voiceWorkId, cancellationToken)
            ?? throw new KeyNotFoundException($"Voice Work {voiceWorkId} not found.");
    }
}