using Ikar.Server.Data.Entities;
using Ikar.Server.Infrastructure;
using Ikar.Server.Infrastructure.Auth;
using Ikar.Server.Infrastructure.Bots;
using Ikar.Server.Infrastructure.Hubs;
using Ikar.Server.Infrastructure.Push;
using Ikar.Shared;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace Ikar.Server.Tests;

public sealed class BotCreatorServiceTests
{
    [Fact]
    public async Task TryHandleIncomingMessage_RussianCreateCommand_CreatesBot()
    {
        await using var database = new SqliteTestDatabase();
        await using var dbContext = await database.CreateContextAsync();
        var service = CreateService(dbContext);
        await service.EnsureCreatorBotAsync();

        var owner = CreateUser("owner", "Owner");
        dbContext.Users.Add(owner);
        await dbContext.SaveChangesAsync();

        var creatorUser = await dbContext.Users.SingleAsync(x => x.NormalizedUsername == BotCreatorService.CreatorUsername.ToUpperInvariant());
        var chat = new Chat
        {
            Type = ChatType.Direct,
            CreatedById = owner.Id,
            LastActivityAt = DateTimeOffset.UtcNow,
            Members =
            [
                new ChatMember { UserId = owner.Id, IsOwner = true, Role = ChatMemberRole.Owner },
                new ChatMember { UserId = creatorUser.Id }
            ]
        };
        var message = new Message
        {
            Chat = chat,
            Sender = owner,
            Text = "/создать_бота Эхо | echobot | тестовый бот",
            SentAt = DateTimeOffset.UtcNow
        };
        chat.Messages.Add(message);
        dbContext.Chats.Add(chat);
        await dbContext.SaveChangesAsync();

        var handled = await service.TryHandleIncomingMessageAsync(message.Id);

        Assert.True(handled);
        var createdBot = await dbContext.Bots
            .Include(x => x.User)
            .SingleOrDefaultAsync(x => x.User.NormalizedUsername == "ECHOBOT");
        Assert.NotNull(createdBot);
        Assert.Equal(owner.Id, createdBot.OwnerUserId);
        Assert.Contains(dbContext.Messages, x => x.ChatId == chat.Id && x.SenderId == creatorUser.Id && x.Text.Contains("Токен"));
    }

    [Fact]
    public async Task UsersSearch_FindsCreatorBot()
    {
        await using var database = new SqliteTestDatabase();
        await using var dbContext = await database.CreateContextAsync();
        var service = CreateService(dbContext);
        await service.EnsureCreatorBotAsync();

        var owner = CreateUser("owner", "Owner");
        dbContext.Users.Add(owner);
        await dbContext.SaveChangesAsync();

        var controller = ControllerTestFixture.CreateUsersController(dbContext, owner.Id);
        var response = await controller.Search("botfatherbot", CancellationToken.None);

        var users = ControllerTestFixture.ValueOf(response);
        var creator = Assert.Single(users);
        Assert.True(creator.IsBot);
        Assert.Equal(BotCreatorService.CreatorUsername, creator.Username);
        Assert.Equal("Создатель ботов", creator.DisplayName);
    }

    private static BotCreatorService CreateService(Ikar.Server.Data.IkarDbContext dbContext)
    {
        var pushQueue = new PushDispatchQueue();
        return new BotCreatorService(
            dbContext,
            new BotTokenService(),
            new BotDeletionService(dbContext, ControllerTestFixture.CreateAttachmentStorageService(Path.GetTempPath())),
            new PresenceTracker(),
            new NoopHubContext<MessengerHub>(),
            pushQueue);
    }

    private static User CreateUser(string username, string displayName)
    {
        var user = new User
        {
            Username = username,
            NormalizedUsername = username.ToUpperInvariant(),
            DisplayName = displayName
        };
        user.PasswordHash = new PasswordHasher<User>().HashPassword(user, "password");
        return user;
    }
}
