using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SVSim.Database;
using SVSim.Database.Enums;
using SVSim.Database.Models;
using SVSim.EmulatedEntrypoint.Services;
using SVSim.UnitTests.Infrastructure;

namespace SVSim.UnitTests.Services;

public class DbCardPoolProviderTests
{
    [Test]
    public async Task GetPool_for_standard_pack_returns_cards_of_matching_set()
    {
        using var factory = new SVSimTestFactory();
        long anyCardId;
        int setId;
        using (var scope = factory.Services.CreateScope())
        {
            var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
            var setWithCards = await db.CardSets.Include(s => s.Cards)
                .FirstAsync(s => s.Cards.Count > 0);
            setId = setWithCards.Id;
            anyCardId = setWithCards.Cards.First().Id;
        }

        using var scope2 = factory.Services.CreateScope();
        var provider = scope2.ServiceProvider.GetRequiredService<ICardPoolProvider>();
        var pool = provider.GetPool(new PackConfigEntry
        {
            Id = setId, BasePackId = setId, PackCategory = PackCategory.None
        });

        Assert.That(pool.Any(c => c.Id == anyCardId), Is.True);
    }

    [Test]
    public void GetPool_for_legendary_special_returns_cards_from_rotation_sets()
    {
        using var factory = new SVSimTestFactory();
        using var scope = factory.Services.CreateScope();
        var provider = scope.ServiceProvider.GetRequiredService<ICardPoolProvider>();

        var pool = provider.GetPool(new PackConfigEntry
        {
            Id = 92001, BasePackId = 90001, PackCategory = PackCategory.SpecialCardPack
        });

        Assert.That(pool.Count, Is.GreaterThan(0));
    }

    [Test]
    public void GetPool_for_skin_pack_returns_empty()
    {
        using var factory = new SVSimTestFactory();
        using var scope = factory.Services.CreateScope();
        var provider = scope.ServiceProvider.GetRequiredService<ICardPoolProvider>();

        var pool = provider.GetPool(new PackConfigEntry
        {
            Id = 70001, BasePackId = 70001, PackCategory = PackCategory.LeaderSkinPack
        });

        Assert.That(pool, Is.Empty);
    }

    [Test]
    public async Task GetPool_excludes_foil_cards()
    {
        using var factory = new SVSimTestFactory();
        long nonFoilId, foilId;
        using (var scope = factory.Services.CreateScope())
        {
            var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
            // Pick the highest-Id card so that id+1 is guaranteed unoccupied.
            nonFoilId = await db.Cards.OrderByDescending(c => c.Id).Select(c => c.Id).FirstAsync();
            foilId = nonFoilId + 1;
            var foilCard = new ShadowverseCardEntry
            {
                Id = foilId, Name = $"Card {foilId}", Rarity = Rarity.Bronze, IsFoil = true,
            };
            // Add directly to the Cards DbSet and set the FK via shadow property,
            // avoiding nav-collection tracker conflicts.
            db.Cards.Add(foilCard);
            db.Entry(foilCard).Property("ShadowverseCardSetEntryId").CurrentValue = 10001;
            await db.SaveChangesAsync();
        }

        using var scope2 = factory.Services.CreateScope();
        var provider = scope2.ServiceProvider.GetRequiredService<ICardPoolProvider>();
        var pool = provider.GetPool(new PackConfigEntry
        {
            Id = 10001, BasePackId = 10001,
            PackCategory = PackCategory.None,
        });

        Assert.That(pool.Any(c => c.Id == nonFoilId), Is.True,  "non-foil must be in the pool");
        Assert.That(pool.Any(c => c.Id == foilId),    Is.False, "foil must be excluded from the pool");
    }

    [Test]
    public async Task TryGetFoilTwin_returns_the_id_plus_one_foil_when_present()
    {
        using var factory = new SVSimTestFactory();
        long nonFoilId, foilId;
        using (var scope = factory.Services.CreateScope())
        {
            var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
            // Pick the highest-Id card so that id+1 is guaranteed unoccupied.
            nonFoilId = await db.Cards.OrderByDescending(c => c.Id).Select(c => c.Id).FirstAsync();
            foilId = nonFoilId + 1;
            var foilCard = new ShadowverseCardEntry
            {
                Id = foilId, Name = $"Card {foilId}", Rarity = Rarity.Bronze, IsFoil = true,
            };
            db.Cards.Add(foilCard);
            db.Entry(foilCard).Property("ShadowverseCardSetEntryId").CurrentValue = 10001;
            await db.SaveChangesAsync();
        }

        using var scope2 = factory.Services.CreateScope();
        var provider = scope2.ServiceProvider.GetRequiredService<ICardPoolProvider>();

        var twin = provider.TryGetFoilTwin(nonFoilId);
        Assert.That(twin, Is.Not.Null);
        Assert.That(twin!.Id, Is.EqualTo(foilId));
        Assert.That(twin.IsFoil, Is.True);
    }

    [Test]
    public async Task TryGetFoilTwin_returns_null_when_no_foil_at_id_plus_one()
    {
        using var factory = new SVSimTestFactory();
        long anyCardId;
        using (var scope = factory.Services.CreateScope())
        {
            var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
            anyCardId = await db.Cards.OrderBy(c => c.Id).Select(c => c.Id).FirstAsync();
        }

        using var scope2 = factory.Services.CreateScope();
        var provider = scope2.ServiceProvider.GetRequiredService<ICardPoolProvider>();

        Assert.That(provider.TryGetFoilTwin(anyCardId), Is.Null,
            "no foil seeded at anyCardId+1, so TryGetFoilTwin must return null");
    }
}
