using Microsoft.Extensions.DependencyInjection;
using System.Collections;
#if NETFRAMEWORK
using System;
using System.Collections.Generic;
#endif

namespace DigitalData.Core.Abstractions
{
    public class Factory : IServiceProvider, IServiceCollection
    {
        public static readonly Factory Shared = new Factory();

        private readonly IServiceCollection _serviceCollection = new ServiceCollection();
        private readonly Lazy<IServiceProvider> _lazyServiceProvider;

        private bool IsBuilt => _lazyServiceProvider.IsValueCreated;

        private PostBuildBehavior _pbBehavior = PostBuildBehavior.ThrowException;

        public Factory BehaveOnPostBuild(PostBuildBehavior postBuildBehavior)
        {
            _pbBehavior = postBuildBehavior;
            return this;
        }

        public Factory()
        {
            _lazyServiceProvider = new Lazy<IServiceProvider>(() => _serviceCollection.BuildServiceProvider());
        }

        #region Service Provider
        public object
#if NET
            ?
#endif
            GetService(Type serviceType)
        {
            return _lazyServiceProvider.Value.GetService(serviceType);
        }

        public IServiceScopeFactory ScopeFactory => this.GetRequiredService<IServiceScopeFactory>();
        #endregion

        #region Service Collection
        public int Count => _serviceCollection.Count;

        public bool IsReadOnly => _serviceCollection.IsReadOnly;

        public ServiceDescriptor this[int index]
        {
            get => _serviceCollection[index];
            set
            {
                if (EnsureBuilt())
                    return;
                _serviceCollection[index] = value;
            }
        }

        public int IndexOf(ServiceDescriptor item)
        {
            return _serviceCollection.IndexOf(item);
        }

        public void Insert(int index, ServiceDescriptor item)
        {
            if (EnsureBuilt())
                return;
            _serviceCollection.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            if (EnsureBuilt())
                return;
            _serviceCollection.RemoveAt(index);
        }

        public void Add(ServiceDescriptor item)
        {
            if (EnsureBuilt())
                return;
            _serviceCollection.Add(item);
        }

        public void Clear()
        {
            if (EnsureBuilt())
                return;
            _serviceCollection.Clear();
        }

        public bool Contains(ServiceDescriptor item)
        {
            return _serviceCollection.Contains(item);
        }

        public void CopyTo(ServiceDescriptor[] array, int arrayIndex)
        {
            _serviceCollection.CopyTo(array, arrayIndex);
        }

        public bool Remove(ServiceDescriptor item)
        {
            if (EnsureBuilt())
                return false;
            return _serviceCollection.Remove(item);
        }

        public IEnumerator<ServiceDescriptor> GetEnumerator()
        {
            return _serviceCollection.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (_serviceCollection as IEnumerable).GetEnumerator();
        }
        #endregion

        #region Helpers
        private bool EnsureBuilt()
        {
            if (IsBuilt)
            {
                return _pbBehavior == PostBuildBehavior.ThrowException
                    ? throw new InvalidOperationException("Service provider has already been built. No further service modifications are allowed.")
                    : true;
            }
            else
                return false;
        }
        #endregion
    }

    public enum PostBuildBehavior
    {
        ThrowException,
        Ignore
    }
}