using Ikar.Server.Data.Entities;
using Ikar.Shared;
using Microsoft.AspNetCore.Mvc;

namespace Ikar.Server.Tests;

public sealed class StoryReplyControllerTests
{
    [Fact]
    public async Task SendMessage_WithStoryReplyStoryId_PersistsStoryReplySnapshot()
    {
        await using var database = new SqliteTestDatabase();
        await using var dbContext = await database.CreateContextAsync();
        var author = CreateUser("author");
        var viewer = CreateUser("viewer");
        var chat = CreateDirectChat(author, viewer);
        var story = new Story
        {
            Id = Guid.NewGuid(),
            AuthorUserId = author.Id,
            AuthorUser = author,
            Text = "Morning update from the road",
            Visibility = StoryVisibility.Everyone,
            CreatedAt = DateTimeOffset.UtcNow.AddMinutes(-5),
            ExpiresAt = DateTimeOffset.UtcNow.AddHours(1)
        };
        dbContext.AddRange(author, viewer, chat, story);
        await dbContext.SaveChangesAsync();

        var controller = ControllerTestFixture.CreateChatsController(dbContext, viewer.Id);
        var response = await controller.SendMessage(
            chat.Id,
            new SendMessageRequest("Looks good", StoryReplyStoryId: story.Id),
            CancellationToken.None);

        var dto = ControllerTestFixture.ValueOf(response);
        Assert.Equal("Looks good", dto.Text);
        Assert.NotNull(dto.StoryReply);
        Assert.Equal(story.Id, dto.StoryReply!.StoryId);
        Assert.Equal(author.Id, dto.StoryReply.AuthorUserId);
        Assert.Equal("Morning update from the road", dto.StoryReply.PreviewText);
        Assert.Equal("text", dto.StoryReply.AttachmentKind);

        var storedMessage = Assert.Single(dbContext.Messages.Where(message => message.ChatId == chat.Id));
        Assert.Equal(story.Id, storedMessage.StoryReplyStoryId);
        Assert.Equal(author.Id, storedMessage.StoryReplyAuthorUserId);
        Assert.Equal("Morning update from the road", storedMessage.StoryReplyPreviewText);
        Assert.Equal("text", storedMessage.StoryReplyAttachmentKind);
    }

    [Fact]
    public async Task SendMessage_WithStoryReplyStoryId_RejectsWrongDirectChat()
    {
        await using var database = new SqliteTestDatabase();
        await using var dbContext = await database.CreateContextAsync();
        var author = CreateUser("author");
        var viewer = CreateUser("viewer");
        var other = CreateUser("other");
        var wrongChat = CreateDirectChat(viewer, other);
        var story = new Story
        {
            Id = Guid.NewGuid(),
            AuthorUserId = author.Id,
            AuthorUser = author,
            Text = "Morning update",
            Visibility = StoryVisibility.Everyone,
            CreatedAt = DateTimeOffset.UtcNow.AddMinutes(-5),
            ExpiresAt = DateTimeOffset.UtcNow.AddHours(1)
        };
        dbContext.AddRange(author, viewer, other, wrongChat, story);
        await dbContext.SaveChangesAsync();

        var controller = ControllerTestFixture.CreateChatsController(dbContext, viewer.Id);
        var response = await controller.SendMessage(
            wrongChat.Id,
            new SendMessageRequest("Looks good", StoryReplyStoryId: story.Id),
            CancellationToken.None);

        Assert.IsType<BadRequestObjectResult>(response.Result);
    }

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

    private static Chat CreateDirectChat(User first, User second)
    {
        var chat = new Chat
        {
            Id = Guid.NewGuid(),
            Type = ChatType.Direct,
            CreatedById = first.Id,
            CreatedBy = first
        };
        chat.Members.Add(new ChatMember { ChatId = chat.Id, Chat = chat, UserId = first.Id, User = first });
        chat.Members.Add(new ChatMember { ChatId = chat.Id, Chat = chat, UserId = second.Id, User = second });
        return chat;
    }
}
