//
//  StringConverterRepository.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using NosSmooth.Packets.Converters.Special;
using NosSmooth.Packets.Errors;
using NosSmooth.Packets.Extensions;
using NosSmooth.PacketSerializer.Abstractions;
using Remora.Results;
namespace NosSmooth.Packets.Converters;
/// 
/// Repository for .
/// 
public class StringConverterRepository : IStringConverterRepository
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ConcurrentDictionary _typeConverters;
    private IReadOnlyList? _converterFactories;
    /// 
    /// Initializes a new instance of the  class.
    /// 
    /// The dependency injection service provider.
    public StringConverterRepository(IServiceProvider serviceProvider)
    {
        _typeConverters = new ConcurrentDictionary();
        _converterFactories = null;
        _serviceProvider = serviceProvider;
    }
    private IReadOnlyList ConverterFactories
    {
        get
        {
            if (_converterFactories is null)
            {
                _converterFactories = _serviceProvider
                    .GetServices()
                    .ToArray();
            }
            return _converterFactories;
        }
    }
    /// 
    /// Gets the type converter for the given type.
    /// 
    /// The type to find converter for.
    /// The type converter or an error.
    public Result GetTypeConverter(Type type)
    {
        var typeConverter = _typeConverters.GetOrAddResult
        (
            type,
            (getType) =>
            {
                foreach (var converterFactory in ConverterFactories)
                {
                    if (converterFactory.ShouldHandle(getType))
                    {
                        var result = converterFactory.CreateTypeSerializer(getType);
                        if (!result.IsSuccess)
                        {
                            return Result.FromError(result);
                        }
                        return Result.FromSuccess(result.Entity);
                    }
                }
                var converterType = typeof(IStringConverter<>).MakeGenericType(type);
                return Result.FromSuccess
                    ((IStringConverter?)_serviceProvider.GetService(converterType));
            }
        );
        if (!typeConverter.IsSuccess)
        {
            return Result.FromError(typeConverter);
        }
        if (typeConverter.Entity is null)
        {
            return new TypeConverterNotFoundError(type);
        }
        return Result.FromSuccess(typeConverter.Entity);
    }
    /// 
    /// Gets the type converter for the given type.
    /// 
    /// The type to find converter for.
    /// The type converter or an error.
    public Result> GetTypeConverter()
        => GetTypeConverter(typeof(TParseType)).Cast, IStringConverter>();
}