using Ikar.Server.Data.Entities;
using Ikar.Server.Infrastructure;
using Ikar.Shared;

namespace Ikar.Server.Tests;

public sealed class ChannelDtoMapperTests
{
    [Fact]
    public void ChannelDetails_HideOtherSubscribersFromRegularSubscriber()
    {
        var owner = CreateUser("owner", "Owner");
        var subscriber = CreateUser("subscriber", "Subscriber");
        var hiddenSubscriber = CreateUser("hidden", "Hidden Subscriber");
        var channel = CreateChannel(owner, subscriber, hiddenSubscriber);

        var subscriberDetails = channel.ToDetailsDto(subscriber.Id, new PresenceTracker());
        var ownerDetails = channel.ToDetailsDto(owner.Id, new PresenceTracker());

        Assert.Equal(3, subscriberDetails.SubscriberCount);
        Assert.Equal("Product news and release notes.", subscriberDetails.Description);
        Assert.Equal(1, subscriberDetails.AdminCount);
        Assert.False(subscriberDetails.CanPostAsChannel);
        Assert.False(subscriberDetails.CanSendMessages);
        Assert.Contains(subscriberDetails.Members!, member => member.User.Id == owner.Id);
        Assert.Contains(subscriberDetails.Members!, member => member.User.Id == subscriber.Id);
        Assert.DoesNotContain(subscriberDetails.Members!, member => member.User.Id == hiddenSubscriber.Id);
        Assert.Equal(3, ownerDetails.Members!.Count);
        Assert.True(ownerDetails.CanPostAsChannel);
    }

    [Fact]
    public void PublicChannelDetails_AllowPreviewAndSubscribe_ForNonMember()
    {
        var owner = CreateUser("owner", "Owner");
        var subscriber = CreateUser("subscriber", "Subscriber");
        var viewer = CreateUser("viewer", "Viewer");
        var channel = CreateChannel(owner, subscriber);
        channel.PublicUsername = "ikar_updates";

        var details = channel.ToDetailsDto(viewer.Id, new PresenceTracker());

        Assert.Equal(2, details.SubscriberCount);
        Assert.False(details.IsSubscribed);
        Assert.True(details.CanSubscribe);
        Assert.False(details.CanSendMessages);
        Assert.False(details.CanPostAsChannel);
        var visibleMember = Assert.Single(details.Members!);
        Assert.Equal(owner.Id, visibleMember.User.Id);
    }

    [Fact]
    public void ChannelPost_MasksSenderForRegularSubscriber_AndKeepsViewCount()
    {
        var owner = CreateUser("owner", "Owner");
        var subscriber = CreateUser("subscriber", "Subscriber");
        var channel = CreateChannel(owner, subscriber);
        var post = new Message
        {
            Id = Guid.NewGuid(),
            ChatId = channel.Id,
            Chat = channel,
            SenderId = owner.Id,
            Sender = owner,
            Text = "Channel post",
            PostedAsChannel = true,
            SentAt = DateTimeOffset.UtcNow
        };
        post.Views.Add(new MessageView
        {
            MessageId = post.Id,
            ViewerUserId = subscriber.Id,
            ViewedAt = DateTimeOffset.UtcNow
        });

        var subscriberDto = post.ToDto(new PresenceTracker(), subscriber.Id);
        var ownerDto = post.ToDto(new PresenceTracker(), owner.Id);

        Assert.True(subscriberDto.PostedAsChannel);
        Assert.Equal(channel.Id, subscriberDto.Sender.Id);
        Assert.Equal("Ikar Updates", subscriberDto.DisplayAuthorName);
        Assert.Equal(1, subscriberDto.ViewCount);
        Assert.Equal(owner.Id, ownerDto.Sender.Id);
    }

    private static Chat CreateChannel(User owner, params User[] subscribers)
    {
        var channel = new Chat
        {
            Id = Guid.NewGuid(),
            Type = ChatType.Channel,
            Title = "Ikar Updates",
            Description = "Product news and release notes.",
            CreatedById = owner.Id,
            CreatedBy = owner,
            CreatedAt = DateTimeOffset.UtcNow
        };
        channel.Members.Add(new ChatMember
        {
            ChatId = channel.Id,
            Chat = channel,
            UserId = owner.Id,
            User = owner,
            IsOwner = true,
            Role = ChatMemberRole.Owner,
            CanPinMessages = true,
            CanDeleteMessages = true,
            CanInviteUsers = true,
            CanPostMessages = true
        });

        foreach (var subscriber in subscribers)
        {
            channel.Members.Add(new ChatMember
            {
                ChatId = channel.Id,
                Chat = channel,
                UserId = subscriber.Id,
                User = subscriber,
                Role = ChatMemberRole.Member,
                CanPinMessages = false,
                CanDeleteMessages = false,
                CanInviteUsers = false,
                CanPostMessages = false
            });
        }

        return channel;
    }

    private static User CreateUser(string username, string displayName) =>
        new()
        {
            Id = Guid.NewGuid(),
            Username = username,
            NormalizedUsername = username.ToUpperInvariant(),
            DisplayName = displayName,
            PasswordHash = "test"
        };
}
