using NUnit.Framework;
using UnityEngine;
using YachtDice.Inventory;
using YachtDice.Modifiers.Definition;
using YachtDice.Modifiers.Runtime;

namespace YachtDice.Tests
{
    public class InventoryModelTests
    {
        private ModifierRegistry registry;
        private InventoryModel inventory;

        [SetUp]
        public void SetUp()
        {
            registry = new ModifierRegistry(3);
            inventory = new InventoryModel(registry);
        }

        private ModifierDefinition CreateTestDef(string id = "test",
            bool hasLimitedUses = false, int maxUses = 0)
        {
            return ModifierDefinition.CreateForTest(id, null,
                hasLimitedUses: hasLimitedUses, maxUses: maxUses);
        }

        [Test]
        public void AddModifier_IncreasesCount()
        {
            inventory.AddModifier(CreateTestDef());

            Assert.AreEqual(1, inventory.OwnedModifiers.Count);
        }

        [Test]
        public void TryActivate_SucceedsWithinSlotLimit()
        {
            inventory.AddModifier(CreateTestDef("a"));
            var mod = inventory.OwnedModifiers[0];

            bool result = inventory.TryActivate(mod);

            Assert.IsTrue(result);
            Assert.IsTrue(mod.IsActive);
            Assert.AreEqual(1, inventory.ActiveCount);
        }

        [Test]
        public void TryActivate_FailsWhenSlotsFull()
        {
            for (int i = 0; i < 3; i++)
            {
                inventory.AddModifier(CreateTestDef($"m{i}"));
                inventory.TryActivate(inventory.OwnedModifiers[i]);
            }

            inventory.AddModifier(CreateTestDef("extra"));
            var extra = inventory.OwnedModifiers[3];
            bool result = inventory.TryActivate(extra);

            Assert.IsFalse(result);
            Assert.IsFalse(extra.IsActive);
            Assert.AreEqual(3, inventory.ActiveCount);
        }

        [Test]
        public void Deactivate_FreesSlot()
        {
            inventory.AddModifier(CreateTestDef());
            var mod = inventory.OwnedModifiers[0];
            inventory.TryActivate(mod);

            inventory.Deactivate(mod);

            Assert.IsFalse(mod.IsActive);
            Assert.AreEqual(0, inventory.ActiveCount);
        }

        [Test]
        public void RemoveModifier_DeactivatesAndRemoves()
        {
            inventory.AddModifier(CreateTestDef());
            var mod = inventory.OwnedModifiers[0];
            inventory.TryActivate(mod);

            inventory.RemoveModifier(mod);

            Assert.AreEqual(0, inventory.OwnedModifiers.Count);
            Assert.AreEqual(0, inventory.ActiveCount);
        }

        [Test]
        public void ConsumeUseOnActive_DecrementsUses()
        {
            inventory.AddModifier(CreateTestDef("ltd", hasLimitedUses: true, maxUses: 3));
            var mod = inventory.OwnedModifiers[0];
            inventory.TryActivate(mod);

            inventory.ConsumeUseOnActive();

            Assert.AreEqual(2, mod.RemainingUses);
        }

        [Test]
        public void ConsumeUseOnActive_RemovesExpired()
        {
            inventory.AddModifier(CreateTestDef("ltd", hasLimitedUses: true, maxUses: 1));
            var mod = inventory.OwnedModifiers[0];
            inventory.TryActivate(mod);

            inventory.ConsumeUseOnActive();

            Assert.AreEqual(0, inventory.OwnedModifiers.Count);
        }

        [Test]
        public void ConsumeUseOnActive_IgnoresPermanent()
        {
            inventory.AddModifier(CreateTestDef("perm"));
            var mod = inventory.OwnedModifiers[0];
            inventory.TryActivate(mod);

            inventory.ConsumeUseOnActive();

            Assert.AreEqual(1, inventory.OwnedModifiers.Count);
            Assert.IsTrue(mod.IsActive);
        }

        [Test]
        public void GetActiveModifierDefinitions_ReturnsOnlyActive()
        {
            inventory.AddModifier(CreateTestDef("a"));
            inventory.AddModifier(CreateTestDef("b"));
            inventory.TryActivate(inventory.OwnedModifiers[0]);

            var active = inventory.GetActiveModifierDefinitions();

            Assert.AreEqual(1, active.Count);
        }

        [Test]
        public void SetMaxActiveSlots_AllowsExpansion()
        {
            inventory.SetMaxActiveSlots(10);

            Assert.AreEqual(10, inventory.MaxActiveSlots);
        }

        [Test]
        public void OnActiveModifiersChanged_FiredOnActivate()
        {
            bool fired = false;
            inventory.OnActiveModifiersChanged += _ => fired = true;
            inventory.AddModifier(CreateTestDef());

            inventory.TryActivate(inventory.OwnedModifiers[0]);

            Assert.IsTrue(fired);
        }
    }
}
