using Newtonsoft.Json.Linq;
namespace PlanTempus.Core.Configurations
{
    public interface IConfigurationBuilder
    {
        ConfigurationBuilder AddProvider(IConfigurationProvider provider);
        IConfigurationRoot Build();
        List<IConfigurationProvider> ConfigurationProviders { get; }
    }

    public class ConfigurationBuilder : IConfigurationBuilder
    {
        public List<IConfigurationProvider> ConfigurationProviders { get; private set; } = [];

        public ConfigurationBuilder AddProvider(IConfigurationProvider provider)
        {
            ((IConfigurationBuilder)this).ConfigurationProviders.Add(provider);
            return this;
        }
        public IConfigurationRoot Build()
        {
            foreach (var provider in ConfigurationProviders)
            {
                provider.Build();
            }
            //TODO: we need to come up with merge strategy, right now the latest key-path dominates

            return new ConfigurationRoot(ConfigurationProviders);
        }
    }

    public class Configuration : IConfiguration
    {
        List<IConfigurationProvider> _providers = [];

        /// <summary>
        /// Implements a string-based indexer for backwards compatibility with Microsoft.Extensions.Configuration.
        /// This implementation is marked as obsolete and should be replaced with type-safe alternatives.
        /// </summary>
        /// <param name="key">The configuration key to retrieve.</param>
        /// <returns>The configuration value for the specified key.</returns>
        /// <exception cref="NotSupportedException">Thrown when attempting to set a value, as this operation is not supported.</exception>
        [Obsolete("Use type-safe configuration methods instead")]
        public string this[string key]
        {
            get => GetConfiguration(_providers, key);
            set => throw new NotSupportedException();
        }

        List<IConfigurationProvider> IConfiguration.ConfigurationProviders
        {
            get { return _providers; }
            set { _providers = value; }
        }

        internal static string GetConfiguration(IList<IConfigurationProvider> providers, string key)
        {
            string value = null;
            foreach (var provider in providers)
            {
                var test = provider.Configuration().SelectToken(ConfigurationBinder.NormalizePath(key));

                if (test != null)
                    value = test.ToString();
            }
            
            return value;
        }
    }

    public class ConfigurationRoot : Configuration, IConfigurationRoot
    {
        public ConfigurationRoot(List<IConfigurationProvider> configurationProviders)
        {
            ((IConfiguration)this).ConfigurationProviders = configurationProviders;
        }

    }
    public static class ConfigurationBinder
    {
        public static string NormalizePath(string path)
        {
            return path?.Replace(":", ".", StringComparison.Ordinal) ?? string.Empty;
        }
        public static string GetConnectionString(this IConfigurationRoot configuration, string name)
        {
            return configuration.GetSection("ConnectionStrings").Get<string>(name);
        }
        public static IConfigurationSection GetSection(this IConfigurationRoot configuration, string path)
        {
            JToken value = null;
            foreach (var provider in configuration.ConfigurationProviders)
            {
                var test = provider.Configuration().SelectToken(NormalizePath(path));

                if (test != null)
                    value = test;
            }

            return new ConfigurationSection { Path = path, Key = path.Split(':').Last(), Value = value };
        }
        public static T Get<T>(this IConfigurationRoot configuration, string path, bool optional = true)
        {
            JToken value = null;
            foreach (var provider in configuration.ConfigurationProviders)
            {
                var test = provider.Configuration().SelectToken(NormalizePath(path));

                if (test != null)
                    value = test;
            }
            if(value is null && !optional)
                throw new Exceptions.ConfigurationException($"Path not found in configuration, path: {path}");
            if (value is null)
                return default;

            return value.ToObject<T>();
        }
        public static T Get<T>(this IConfigurationSection configuration, string path)
        {
            var value = configuration.Value.SelectToken(NormalizePath(path)).ToObject<T>();
            return value;
        }
        public static T ToObject<T>(this IConfigurationSection configuration)
        {
            var value = configuration.Value.ToObject<T>();
            return value;
        }

        [Obsolete("Use ToObject")]
        public static T Get<T>(this IConfigurationSection configuration)
        {
            return configuration.Value.ToObject<T>();
        }

    }
    public interface IConfigurationProvider
    {
        void Build();
        JObject Configuration();
    }

    public class ConfigurationSection : IConfigurationSection
    {
        public required string Path { get; set; }
        public required string Key { get; set; }

        public required JToken Value { get; set; }

    }
    public interface IConfigurationSection
    {
        string Path { get; }
        string Key { get; }
        JToken Value { get; set; }
    }
}
