using Microsoft.EntityFrameworkCore;
using UniVerse.Application.DTOs.Tags;
using UniVerse.Domain.Entities;
using UniVerse.Domain.Enums;
using UniVerse.Domain.Exceptions;
using UniVerse.Infrastructure.Data;
using UniVerse.Infrastructure.Services;
using Xunit;

namespace UniVerse.Api.Tests.Tags;

public class TagServiceTests
{
    [Fact]
    public async Task GetAllAsync_FiltersByTypeAndParentAndOrdersByName()
    {
        await using var db = CreateDbContext();
        db.Tags.AddRange(
            new Tag { Id = 1, Name = "Root", Type = TagType.Topic },
            new Tag { Id = 2, Name = "Zeta", Type = TagType.Subject, ParentId = 1 },
            new Tag { Id = 3, Name = "Alpha", Type = TagType.Subject, ParentId = 1 },
            new Tag { Id = 4, Name = "Other parent", Type = TagType.Subject, ParentId = 99 },
            new Tag { Id = 5, Name = "Other type", Type = TagType.Topic, ParentId = 1 });
        await db.SaveChangesAsync();
        var service = new TagService(db);

        var result = await service.GetAllAsync(TagType.Subject, parentId: 1);

        Assert.Equal(new[] { "Alpha", "Zeta" }, result.Select(tag => tag.Name));
    }

    [Fact]
    public async Task CreateAsync_ThrowsWhenParentMissing()
    {
        await using var db = CreateDbContext();
        var service = new TagService(db);

        await Assert.ThrowsAsync<NotFoundException>(() =>
            service.CreateAsync(new CreateTagRequest("Child", TagType.Subject, ParentId: 404)));
    }

    [Fact]
    public async Task CreateAsync_CreatesChildWhenParentExists()
    {
        await using var db = CreateDbContext();
        db.Tags.Add(new Tag { Id = 1, Name = "Parent", Type = TagType.Topic });
        await db.SaveChangesAsync();
        var service = new TagService(db);

        var created = await service.CreateAsync(new CreateTagRequest("Child", TagType.Subject, ParentId: 1));

        Assert.Equal("Child", created.Name);
        Assert.Equal(1, created.ParentId);
        Assert.True(await db.Tags.AnyAsync(tag => tag.Name == "Child" && tag.ParentId == 1));
    }

    [Fact]
    public async Task GetTreeAsync_ReturnsNestedRootTrees()
    {
        await using var db = CreateDbContext();
        db.Tags.AddRange(
            new Tag { Id = 1, Name = "Root A", Type = TagType.Topic },
            new Tag { Id = 2, Name = "Child A", Type = TagType.Subject, ParentId = 1 },
            new Tag { Id = 3, Name = "Grandchild A", Type = TagType.Other, ParentId = 2 },
            new Tag { Id = 4, Name = "Root B", Type = TagType.Organization });
        await db.SaveChangesAsync();
        var service = new TagService(db);

        var tree = await service.GetTreeAsync();

        Assert.Equal(new[] { "Root A", "Root B" }, tree.Select(tag => tag.Name));
        var rootA = tree.Single(tag => tag.Name == "Root A");
        var child = Assert.Single(rootA.Children);
        Assert.Equal("Child A", child.Name);
        Assert.Equal("Grandchild A", Assert.Single(child.Children).Name);
        Assert.Empty(tree.Single(tag => tag.Name == "Root B").Children);
    }

    private static AppDbContext CreateDbContext()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase($"TagServiceTests_{Guid.NewGuid()}")
            .Options;
        return new AppDbContext(options);
    }
}
