using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Knot.Contracts.Messaging.Application.Abstractions;
using Knot.Contracts.Messaging.Domain;
using Knot.Contracts.Conversations.Application.Abstractions;
using Knot.Modules.Conversations.Application.DTOs;
using Knot.Contracts.Conversations.Domain;
using Knot.Shared.Kernel;
using MediatR;

namespace Knot.Modules.Conversations.Application.Messages.GetSharedMedia;

public record GetSharedMediaQuery(Guid UserId, Guid ChatId, string? Type) : IQuery<List<SharedMediaDto>>;

internal sealed class GetSharedMediaQueryHandler : IQueryHandler<GetSharedMediaQuery, List<SharedMediaDto>>
{
    private readonly IMessageRepository _messageRepository;
    private readonly IUserDisplayNameProvider _userProvider;
    private readonly IChatRepository _chatRepository;

    public GetSharedMediaQueryHandler(IMessageRepository messageRepository, IUserDisplayNameProvider userProvider, IChatRepository chatRepository)
    {
        _messageRepository = messageRepository;
        _userProvider = userProvider;
        _chatRepository = chatRepository;
    }

    public async Task<Result<List<SharedMediaDto>>> Handle(GetSharedMediaQuery request, CancellationToken cancellationToken)
    {
        var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
        if (chat == null || !chat.Members.Any(member => member.UserId == request.UserId))
        {
            return Result.Failure<List<SharedMediaDto>>(ChatErrors.ChatsForbidden);
        }

        var messages = await _messageRepository.GetChatMessagesAsync(request.ChatId, ChatConstants.MaxSharedMediaQueryLimit, 0, cancellationToken);
        messages = messages.Where(message => !message.IsDeletedForUser(request.UserId)).ToList();

        var result = new List<SharedMediaDto>();
        var filterType = request.Type?.ToLower();

        var userIds = messages.Select(message => message.SenderId).Distinct();
        var senders = await _userProvider.GetUsersInfoAsync(userIds, cancellationToken);

        foreach (var message in messages)
        {
            var mediaMessage = message as MediaMessage;
            var textMessage = message as TextMessage;
            var storyMessage = message as StoryMessage;

            if (filterType == "links")
            {
                var messageContent = message.Content;
                var linkRegex = new Regex(@"https?://[^\s]+", RegexOptions.IgnoreCase);
                var contentLinks = !string.IsNullOrEmpty(messageContent) ? linkRegex.Matches(messageContent).Select(match => match.Value).ToList() : new List<string>();
                
                var mediaLinks = (mediaMessage?.Media ?? Enumerable.Empty<Media>())
                    .Where(media => media.Type?.ToString().ToLower() == "link" && !string.IsNullOrEmpty(media.Url))
                    .Select(media => media.Url!)
                    .ToList();
                var allLinks = contentLinks.Concat(mediaLinks).Distinct().ToList();

                if (allLinks.Any())
                {
                    senders.TryGetValue(message.SenderId, out var sender);
                    result.Add(new SharedMediaDto(
                        message.Id,
                        messageContent,
                        message.CreatedAt,
                        allLinks,
                        sender != null ? new MessageSenderDto(sender.Id, sender.Username, sender.DisplayName, sender.Avatar) : null,
                        null, null, null, null, null, null, null, null
                    ));
                }
                continue;
            }

            var messageMedia = mediaMessage?.Media;
            if (messageMedia == null || !messageMedia.Any())
            {
                continue;
            }

            var filteredMedia = messageMedia.Where(media =>
            {
                var mType = media.Type?.ToLower() ?? "file";
                var filename = media.Filename?.ToLower() ?? "";
                var url = media.Url?.ToLower() ?? "";
                
                var isGif = mType == "gif" || 
                            (mType == "image" && (filename.EndsWith(".mp4") || filename.EndsWith(".gif") || url.EndsWith(".gif") || filename.Contains("gif"))) ||
                            (mType == "video" && (filename.Contains("animation") || filename.Contains("gif")));

                if (filterType == "gifs") return isGif;
                if (filterType == "media") return (mType == "image" || mType == "video") && !isGif;
                if (filterType == "files") return (mType == "file" || mType == "audio") && !isGif && mType != "image" && mType != "video";
                if (filterType == "links") return mType == "link";

                return true;
            }).ToList();

            if (filteredMedia.Any())
            {
                senders.TryGetValue(message.SenderId, out var sender);
                result.Add(new SharedMediaDto(
                    message.Id,
                    message.Content,
                    message.CreatedAt,
                    null,
                    sender != null ? new MessageSenderDto(sender.Id, sender.Username, sender.DisplayName, sender.Avatar) : null,
                    message.ReplyToId,
                    textMessage?.Quote,
                    storyMessage?.StoryId,
                    storyMessage?.StoryMediaUrl,
                    storyMessage?.StoryMediaType,
                    message.IsEdited,
                    message.Type,
                    filteredMedia.Select(media => new MediaDto(media.Id, media.Type, media.Url, media.Filename, media.Size, media.Duration)).ToList()
                ));
            }
        }

        return Result.Success(result.OrderByDescending(x => x.CreatedAt).ToList());
    }
}



