using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Knot.Contracts.Admin.Abstractions;
using Knot.Contracts.Auth.Infrastructure.Persistence;
using Knot.Contracts.Conversations.Domain;
using Knot.Contracts.Conversations.Infrastructure.Persistence;
using Knot.Contracts.Messaging.Application.Abstractions;
using Knot.Shared.Kernel.Storage;
using Knot.Contracts.Stories.Infrastructure.Persistence;
using Knot.Modules.Admin.Application.Admin.DTOs;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Knot.Modules.Admin.Application.Admin.Queries;

public record CleanDryRunQuery() : IQuery<CleanDryRunResult>;

internal sealed class CleanDryRunQueryHandler : IQueryHandler<CleanDryRunQuery, CleanDryRunResult>
{
    private readonly Knot.Contracts.Messaging.Application.Abstractions.IMessageQueryService _messageService;
    private readonly IAuthDbContext _authDbContext;
    private readonly IChatsDbContext _chatsDbContext;
    private readonly IStoryCollection _storyCollection;
    private readonly IFileStorageService _fileStorage;

    public CleanDryRunQueryHandler(
        Knot.Contracts.Messaging.Application.Abstractions.IMessageQueryService messageService,
        IAuthDbContext authDbContext,
        IChatsDbContext chatsDbContext,
        IStoryCollection storyCollection,
        IFileStorageService fileStorage)
    {
        _messageService = messageService;
        _authDbContext = authDbContext;
        _chatsDbContext = chatsDbContext;
        _storyCollection = storyCollection;
        _fileStorage = fileStorage;
    }

    public async Task<Result<CleanDryRunResult>> Handle(CleanDryRunQuery request, CancellationToken ct)
    {
        try
        {
            var activeChatIds = _chatsDbContext.Chats.Select(c => c.Id).ToHashSet();

            var orphanedMessages = await _messageService.GetOrphanedMessagesAsync(activeChatIds, ct);
            var orphanedMediaCount = orphanedMessages.Count(m => m.MediaUrl != null);
            var orphanedMessageCount = orphanedMessages.Count;

            var allMessages = await _messageService.GetAllMessagesAsync(ct);
            var keptMessages = allMessages
                .Where(m => !orphanedMessages.Any(om => om.Id == m.Id) && !m.IsDeleted)
                .ToList();
            var allUsers = await _authDbContext.Users.ToListAsync(ct);
            var stories = await _storyCollection.GetAllAsync(ct);

            var validUrls = new HashSet<string>();

            var activeMessageUrls = keptMessages.Where(m => m.Media != null).SelectMany(m => m.Media!).Select(x => x.Url).Where(u => !string.IsNullOrEmpty(u));
            var activeChatUrls = _chatsDbContext.Chats.Select(c => c.Avatar).Where(u => !string.IsNullOrEmpty(u));
            var activeUserUrls = allUsers.Select(u => u.Avatar).Where(u => !string.IsNullOrEmpty(u));
            var activeStoryUrls = stories.Select(s => s.MediaUrl).Where(u => !string.IsNullOrEmpty(u));

            foreach (var u in activeMessageUrls) validUrls.Add(u!);
            foreach (var u in activeChatUrls) validUrls.Add(u!);
            foreach (var u in activeUserUrls) validUrls.Add(u!);
            foreach (var u in activeStoryUrls) validUrls.Add(u!);

            var validFileIds = validUrls
                .Where(u => u.Contains("/api/files/"))
                .Select(u => u.Split('/').Last())
                .ToHashSet();

            var allMinioFiles = await _fileStorage.ListFilesAsync();
            long orphanedFileSize = allMinioFiles
                .Where(f => !validFileIds.Contains(f.FileId))
                .Sum(f => f.Size);

            var expiredStoriesCount = stories.Count(s => s.ExpiresAt.HasValue && s.ExpiresAt.Value < DateTime.UtcNow);
            var expiredStoriesSize = stories.Where(s => s.ExpiresAt.HasValue && s.ExpiresAt.Value < DateTime.UtcNow).Sum(s => s.MediaUrl?.Length ?? 0);

            return Result.Success(new CleanDryRunResult(
                orphanedMessageCount,
                orphanedMediaCount,
                orphanedFileSize,
                expiredStoriesCount,
                expiredStoriesSize
            ));
        }
        catch (Exception ex)
        {
            return Result.Failure<CleanDryRunResult>(new Error("Admin.CleanDryRun.Error", $"Error during dry run: {ex.Message}"));
        }
    }
}
