using Microsoft.Extensions.DependencyInjection;
using DigitalData.Core.Abstractions;

namespace DigitalData.Core.Tests.Abstractions
{
    [TestFixture]
    public class FactoryTests
    {
        private Factory _factory;

        [SetUp]
        public void Setup()
        {
            _factory = new Factory();
        }

        [Test]
        public void Add_ServiceDescriptor_ShouldIncreaseCount()
        {
            // Arrange
            var descriptor = ServiceDescriptor.Singleton(typeof(string), "test");

            // Act
            _factory.Add(descriptor);

            // Assert
            Assert.That(_factory.Count, Is.EqualTo(1));
            Assert.That(_factory.Contains(descriptor), Is.True);
        }

        [Test]
        public void Clear_ShouldRemoveAllServices()
        {
            // Arrange
            _factory.Add(ServiceDescriptor.Singleton(typeof(string), "test"));
            _factory.Add(ServiceDescriptor.Singleton(typeof(int), 42));

            // Act
            _factory.Clear();

            // Assert
            Assert.That(_factory.Count, Is.EqualTo(0));
        }

        [Test]
        public void GetService_ShouldReturnRegisteredInstance()
        {
            // Arrange
            _factory.Add(ServiceDescriptor.Singleton(typeof(string), "Hello World"));

            // Act
            var result = _factory.GetService(typeof(string));

            // Assert
            Assert.That(result, Is.EqualTo("Hello World"));
        }

        [Test]
        public void GetService_UnregisteredType_ShouldReturnNull()
        {
            // Act
            var result = _factory.GetService(typeof(int));

            // Assert
            Assert.That(result, Is.Null);
        }

        [Test]
        public void ModifyAfterBuild_ShouldThrowInvalidOperationException()
        {
            // Arrange
            _factory.Add(ServiceDescriptor.Singleton(typeof(string), "x"));
            var _ = _factory.GetService(typeof(string)); // trigger build

            // Act & Assert
            Assert.Throws<InvalidOperationException>(() => _factory.Add(ServiceDescriptor.Singleton(typeof(int), 1)));
        }

        [Test]
        public void Remove_ShouldWorkBeforeBuild()
        {
            // Arrange
            var descriptor = ServiceDescriptor.Singleton(typeof(string), "Test");
            _factory.Add(descriptor);

            // Act
            var removed = _factory.Remove(descriptor);

            // Assert
            Assert.That(removed, Is.True);
            Assert.That(_factory.Count, Is.EqualTo(0));
        }

        [Test]
        public void Insert_ShouldInsertAtCorrectIndex()
        {
            // Arrange
            var d1 = ServiceDescriptor.Singleton(typeof(string), "A");
            var d2 = ServiceDescriptor.Singleton(typeof(int), 1);

            _factory.Add(d1);
            _factory.Insert(0, d2);

            // Act
            var first = _factory[0];

            // Assert
            Assert.That(first.ServiceType, Is.EqualTo(typeof(int)));
        }

        [Test]
        public void Enumerator_ShouldIterateOverServices()
        {
            // Arrange
            _factory.Add(ServiceDescriptor.Singleton(typeof(string), "a"));
            _factory.Add(ServiceDescriptor.Singleton(typeof(int), 5));

            // Act
            var list = _factory.ToList();

            // Assert
            Assert.That(list.Count, Is.EqualTo(2));
            Assert.That(list.Any(sd => sd.ServiceType == typeof(string)), Is.True);
            Assert.That(list.Any(sd => sd.ServiceType == typeof(int)), Is.True);
        }

        [Test]
        public void CopyTo_ShouldCopyElements()
        {
            // Arrange
            _factory.Add(ServiceDescriptor.Singleton(typeof(string), "x"));
            _factory.Add(ServiceDescriptor.Singleton(typeof(int), 10));
            var array = new ServiceDescriptor[2];

            // Act
            _factory.CopyTo(array, 0);

            // Assert
            Assert.That(array[0].ServiceType, Is.EqualTo(typeof(string)));
            Assert.That(array[1].ServiceType, Is.EqualTo(typeof(int)));
        }

        [Test]
        public void RemoveAt_ShouldRemoveItemAtIndex()
        {
            // Arrange
            _factory.Add(ServiceDescriptor.Singleton(typeof(string), "x"));
            _factory.Add(ServiceDescriptor.Singleton(typeof(int), 5));

            // Act
            _factory.RemoveAt(0);

            // Assert
            Assert.That(_factory.Count, Is.EqualTo(1));
            Assert.That(_factory[0].ServiceType, Is.EqualTo(typeof(int)));
        }

        [Test]
        public void Indexer_Set_ShouldReplaceItem()
        {
            // Arrange
            var original = ServiceDescriptor.Singleton(typeof(string), "A");
            var replacement = ServiceDescriptor.Singleton(typeof(string), "B");
            _factory.Add(original);

            // Act
            _factory[0] = replacement;

            // Assert
            Assert.That(_factory[0], Is.EqualTo(replacement));
        }
    }
}
