using GitObjectDb.Api.OData.Model;
using GitObjectDb.Model;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;

namespace GitObjectDb.Api.OData;

/// <summary>A set of methods for instances of <see cref="IServiceCollection"/>.</summary>
public static class ServiceConfiguration
{
    /// <summary>Adds access to GitObjectDb repositories.</summary>
    /// <param name="source">The source.</param>
    /// <returns>The source <see cref="IServiceCollection"/>.</returns>
    public static IServiceCollection AddGitObjectDbOData(this IServiceCollection source)
    {
        // Avoid double-registrations
        if (source.IsGitObjectDbODataRegistered())
        {
            throw new NotSupportedException("GitObjectDbOData has already been registered.");
        }

        if (!source.Any(sd => sd.ServiceType == typeof(IMemoryCache)))
        {
            throw new NotSupportedException($"No {nameof(IMemoryCache)} service registered.");
        }

        var model = source.FirstOrDefault(s => s.ServiceType == typeof(IDataModel) &&
            s.Lifetime == ServiceLifetime.Singleton &&
            s.ImplementationInstance is not null)?.ImplementationInstance as IDataModel ??
            throw new NotSupportedException($"{nameof(IDataModel)} has not bee registered.");
        source
            .AddSingleton<DataProvider>()
            .AddSingleton(new DtoTypeEmitter(model))
            .AddAutoMapper((s, c) => c.AddProfile(
                new AutoMapperProfile(
                    s.GetRequiredService<DtoTypeEmitter>().TypeDescriptions)),
                Type.EmptyTypes);

        return source;
    }

    /// <summary>Gets whether GitObjectDbOData has already been registered.</summary>
    /// <param name="source">The source.</param>
    /// <returns><c>true</c> if the service has already been registered, <c>false</c> otherwise.</returns>
    private static bool IsGitObjectDbODataRegistered(this IServiceCollection source) =>
        source.Any(sd => sd.ServiceType == typeof(DataProvider)) ||
        source.Any(sd => sd.ServiceType == typeof(DtoTypeEmitter));
}
