using System.Text.RegularExpressions;
using Knot.Contracts.Auth.Application.Abstractions;
using Knot.Contracts.Auth.Domain;
using Knot.Contracts.Messaging.Domain;
using Knot.Contracts.Conversations.Application.Abstractions;
using Knot.Contracts.Conversations.Domain;
using Knot.Shared.Kernel;
using Knot.Shared.Kernel.Storage;
using MediatR;

namespace Knot.Modules.Conversations.Application.Users.Commands.DeleteUser;

public record DeleteUserCommand(Guid UserId) : ICommand;

internal sealed class DeleteUserCommandHandler : ICommandHandler<DeleteUserCommand>
{
    private readonly IUserRepository _userRepository;
    private readonly IUserChatSettingsRepository _userChatSettingsRepository;
    private readonly IUserFolderSettingsRepository _userFolderSettingsRepository;
    private readonly Knot.Contracts.Messaging.Application.Abstractions.IMessageRepository _messageRepository;
    private readonly IFileStorageService _fileStorage;
    private readonly IChatsUnitOfWork _unitOfWork;
    private readonly IAuthUnitOfWork _authUnitOfWork;

    public DeleteUserCommandHandler(
        IUserRepository userRepository,
        IUserChatSettingsRepository userChatSettingsRepository,
        IUserFolderSettingsRepository userFolderSettingsRepository,
        Knot.Contracts.Messaging.Application.Abstractions.IMessageRepository messageRepository,
        IFileStorageService fileStorage,
        IChatsUnitOfWork unitOfWork,
        IAuthUnitOfWork authUnitOfWork)
    {
        _userRepository = userRepository;
        _userChatSettingsRepository = userChatSettingsRepository;
        _userFolderSettingsRepository = userFolderSettingsRepository;
        _messageRepository = messageRepository;
        _fileStorage = fileStorage;
        _unitOfWork = unitOfWork;
        _authUnitOfWork = authUnitOfWork;
    }

    public async Task<Result> Handle(DeleteUserCommand request, CancellationToken cancellationToken)
    {
        var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
        if (user == null) return Result.Failure(Error.NotFound("User.NotFound", "User not found"));

        // 1. Delete Messages and their files in MongoDB
        var allMessages = await _messageRepository.GetAllMessagesAsync(cancellationToken);
        var userMessages = allMessages.Where(m => m.SenderId == request.UserId).ToList();

        foreach (var msg in userMessages)
        {
            if (msg is MediaMessage mediaMsg)
            {
                foreach (var media in mediaMsg.Media)
                {
                    if (!string.IsNullOrEmpty(media.Url))
                    {
                        bool isUsedElsewhere = allMessages.Any(m => m.Id != msg.Id &&
                            m is MediaMessage mm && mm.Media.Any(ame => !string.IsNullOrEmpty(ame.Url) && ame.Url == media.Url));

                        if (!isUsedElsewhere)
                        {
                            var fileId = ExtractFileId(media.Url);
                            if (!string.IsNullOrEmpty(fileId))
                            {
                                await _fileStorage.DeleteFileAsync(fileId);
                            }
                        }
                    }
                }
            }
        }

        await _messageRepository.DeleteUserMessagesAsync(request.UserId, cancellationToken);

        // 2. Delete Folder Settings (MongoDB)
        await _userFolderSettingsRepository.RemoveByUserIdAsync(request.UserId, cancellationToken);

        // 3. Delete Chat Settings (Postgres)
        var chatSettings = await _userChatSettingsRepository.GetByUserIdAsync(request.UserId, cancellationToken);
        _userChatSettingsRepository.RemoveRange(chatSettings);

        // 4. Delete User (Postgres - Auth Module)
        await _userRepository.RemoveAsync(user, cancellationToken);

        // Commit all
        await _unitOfWork.SaveChangesAsync(cancellationToken);
        await _authUnitOfWork.SaveChangesAsync(cancellationToken);

        return Result.Success();
    }

    private string? ExtractFileId(string url)
    {
        var match = Regex.Match(url, @"/([^/]+)$");
        return match.Success ? match.Groups[1].Value : null;
    }
}
