using System;
using Microsoft.Extensions.Configuration;
using NetEscapades.Configuration.Yaml;

namespace StellaOps.Configuration;

public static class StellaOpsConfigurationBootstrapper
{
    public static StellaOpsConfigurationContext<TOptions> Build<TOptions>(
        Action<StellaOpsBootstrapOptions<TOptions>>? configure = null)
        where TOptions : class, new()
    {
        var bootstrapOptions = new StellaOpsBootstrapOptions<TOptions>();
        configure?.Invoke(bootstrapOptions);

        var configurationOptions = bootstrapOptions.ConfigurationOptions;
        var builder = new ConfigurationBuilder();

        if (!string.IsNullOrWhiteSpace(configurationOptions.BasePath))
        {
            builder.SetBasePath(configurationOptions.BasePath!);
        }

        if (configurationOptions.IncludeJsonFiles)
        {
            foreach (var file in configurationOptions.JsonFiles)
            {
                builder.AddJsonFile(file.Path, optional: file.Optional, reloadOnChange: file.ReloadOnChange);
            }
        }

        if (configurationOptions.IncludeYamlFiles)
        {
            foreach (var file in configurationOptions.YamlFiles)
            {
                builder.AddYamlFile(file.Path, optional: file.Optional);
            }
        }

        configurationOptions.ConfigureBuilder?.Invoke(builder);

        if (configurationOptions.IncludeEnvironmentVariables)
        {
            builder.AddEnvironmentVariables(configurationOptions.EnvironmentPrefix);
        }

        var configuration = builder.Build();

        IConfiguration bindingSource;
        if (string.IsNullOrWhiteSpace(configurationOptions.BindingSection))
        {
            bindingSource = configuration;
        }
        else
        {
            bindingSource = configuration.GetSection(configurationOptions.BindingSection!);
        }

        var options = new TOptions();
        bindingSource.Bind(options);

        bootstrapOptions.PostBind?.Invoke(options, configuration);

        return new StellaOpsConfigurationContext<TOptions>(configuration, options);
    }

    public static IConfigurationBuilder AddStellaOpsDefaults(
        this IConfigurationBuilder builder,
        Action<StellaOpsConfigurationOptions>? configure = null)
    {
        ArgumentNullException.ThrowIfNull(builder);

        var options = new StellaOpsConfigurationOptions();
        configure?.Invoke(options);

        if (!string.IsNullOrWhiteSpace(options.BasePath))
        {
            builder.SetBasePath(options.BasePath!);
        }

        if (options.IncludeJsonFiles)
        {
            foreach (var file in options.JsonFiles)
            {
                builder.AddJsonFile(file.Path, optional: file.Optional, reloadOnChange: file.ReloadOnChange);
            }
        }

        if (options.IncludeYamlFiles)
        {
            foreach (var file in options.YamlFiles)
            {
                builder.AddYamlFile(file.Path, optional: file.Optional);
            }
        }

        options.ConfigureBuilder?.Invoke(builder);

        if (options.IncludeEnvironmentVariables)
        {
            builder.AddEnvironmentVariables(options.EnvironmentPrefix);
        }

        return builder;
    }
}
