using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Knot.Contracts.Conversations.Application.Abstractions;
using Knot.Contracts.Conversations.Domain;
using Knot.Modules.Conversations.Infrastructure.SignalR;
using Knot.Shared.Kernel;
using Knot.Shared.Kernel.Storage;
using MediatR;
using Microsoft.AspNetCore.SignalR;

namespace Knot.Modules.Conversations.Application.Chats.LeaveOrDelete;

public record LeaveOrDeleteChatCommand(Guid ChatId, Guid UserId) : ICommand<SuccessResponse>;

internal sealed class LeaveOrDeleteChatCommandHandler : ICommandHandler<LeaveOrDeleteChatCommand, SuccessResponse>
{
    private readonly IChatRepository _chatRepository;
    private readonly IMessageRepository _messageRepository;
    private readonly IFileStorageService _fileStorage;
    private readonly IChatsUnitOfWork _uow;
    private readonly IHubContext<ChatHub> _hubContext;

    public LeaveOrDeleteChatCommandHandler(
        IChatRepository chatRepository,

        IMessageRepository messageRepository,
        IFileStorageService fileStorage,
        IChatsUnitOfWork uow,
        IHubContext<ChatHub> hubContext)
    {
        _chatRepository = chatRepository;
        _messageRepository = messageRepository;
        _fileStorage = fileStorage;
        _uow = uow;
        _hubContext = hubContext;
    }

    public async Task<Result<SuccessResponse>> Handle(LeaveOrDeleteChatCommand request, CancellationToken cancellationToken)
    {
        var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
        if (chat == null)
        {
            return Result.Success(new SuccessResponse(true));
        }

        if (!chat.Members.Any(m => m.UserId == request.UserId))
        {
            return Result.Failure<SuccessResponse>(ChatErrors.Unauthorized);
        }

        // If it's a private chat or the last member leaving a group, delete everything
        bool shouldDeleteEverything = chat.Type != ChatType.Group || chat.Members.Count <= 1;

        if (chat.Type == ChatType.Group && !shouldDeleteEverything)
        {
            chat.RemoveMember(request.UserId);
            _chatRepository.Update(chat);
        }
        else
        {
            // DELETE ALL MESSAGES AND FILES FIRST
            await DeleteChatMediaAndMessagesAsync(chat.Id, cancellationToken);
            _chatRepository.Remove(chat);

            // Notify all remaining members that the chat was deleted

            foreach (var member in chat.Members)
            {
                await _hubContext.Clients.User(member.UserId.ToString())
                    .SendAsync("chat_deleted", chat.Id.ToString(), cancellationToken);
            }
        }

        await _uow.SaveChangesAsync(cancellationToken);

        return Result.Success(new SuccessResponse(true));
    }

    private async Task DeleteChatMediaAndMessagesAsync(Guid chatId, CancellationToken ct)
    {
        try

        {
            // Get all messages directly from Mongo (not paged)
            var messages = await _messageRepository.GetChatMessagesAsync(chatId, int.MaxValue, 0, ct);
            foreach (var msg in messages)
            {
                if (msg is Knot.Contracts.Messaging.Domain.MediaMessage mediaMsg)
                {
                    foreach (var media in mediaMsg.Media)
                    {
                        if (!string.IsNullOrEmpty(media.Url))
                        {
                            var fileId = ExtractFileId(media.Url);
                            if (!string.IsNullOrEmpty(fileId))
                            {
                                await _fileStorage.DeleteFileAsync(fileId);
                            }
                        }
                    }
                }
            }
            await _messageRepository.DeleteChatMessagesAsync(chatId, ct);
        }
        catch (Exception ex)
        {
            // Log if possible, but don't fail chat deletion
            Console.WriteLine($"[Cleanup] Error deleting chat media: {ex.Message}");
        }
    }

    private string? ExtractFileId(string url)
    {
        var lastSlash = url.LastIndexOf('/');
        if (lastSlash == -1) return null;
        var id = url[(lastSlash + 1)..];
        if (id.Contains('?')) id = id[..id.IndexOf('?')];
        return id;
    }
}
