using Mbproxy.Admin;
using Mbproxy.Configuration;
using Mbproxy.Diagnostics;
using Mbproxy.Options;
using Serilog;

namespace Mbproxy;

internal static class HostingExtensions
{
    /// <summary>
    /// Registers the <c>"Mbproxy"</c> configuration section, binds it to
    /// <see cref="MbproxyOptions"/> via <c>IOptionsMonitor</c>, registers the schema-
    /// level <see cref="MbproxyOptionsValidator"/>, and registers the singleton
    /// <see cref="ServiceCounters"/> and <see cref="ConfigReconciler"/> so they can be
    /// injected into <see cref="Proxy.ProxyWorker"/>.
    /// </summary>
    public static IHostApplicationBuilder AddMbproxyOptions(this IHostApplicationBuilder builder)
    {
        builder.Services
            .AddOptions<MbproxyOptions>()
            .BindConfiguration("Mbproxy")
            .ValidateOnStart();

        builder.Services.AddSingleton<
            Microsoft.Extensions.Options.IValidateOptions<MbproxyOptions>,
            MbproxyOptionsValidator>();

        // Service-wide counters (read by the status page).
        builder.Services.AddSingleton<ServiceCounters>();

        // Hot-reload reconciler (singleton; subscribes to IOptionsMonitor.OnChange).
        builder.Services.AddSingleton<ConfigReconciler>();

        return builder;
    }

    /// <summary>
    /// Registers the admin endpoint services:
    /// <list type="bullet">
    ///   <item><see cref="AssemblyVersionAccessor"/> (singleton — reads version attribute once).</item>
    ///   <item><see cref="StatusSnapshotBuilder"/> (singleton — pure orchestration).</item>
    ///   <item><see cref="AdminEndpointHost"/> (singleton — owns the Kestrel admin server).</item>
    /// </list>
    /// Must be called after <see cref="AddMbproxyOptions"/> and after
    /// <c>AddHostedService&lt;ProxyWorker&gt;</c> (so ProxyWorker is available via DI).
    ///
    /// <para><see cref="AdminEndpointHost"/> is intentionally NOT registered via
    /// <c>AddHostedService</c>. <see cref="Proxy.ProxyWorker"/> drives its lifecycle
    /// directly so admin start/stop ordering matches the design contract (admin starts
    /// after listeners are up; admin stops AFTER the in-flight drain).</para>
    /// </summary>
    public static IHostApplicationBuilder AddMbproxyAdmin(this IHostApplicationBuilder builder)
    {
        builder.Services.AddSingleton<AssemblyVersionAccessor>();
        builder.Services.AddSingleton<StatusSnapshotBuilder>();
        builder.Services.AddSingleton<AdminEndpointHost>();
        return builder;
    }

    /// <summary>
    /// Configures Serilog from the <c>"Serilog"</c> configuration section, with console
    /// and rolling-file sinks as defaults.
    ///
    /// <para>When <paramref name="addEventLogBridge"/> is <c>true</c>, the
    /// <see cref="Diagnostics.EventLogBridge"/> is added as a sub-sink for events at
    /// <see cref="Serilog.Events.LogEventLevel.Error"/> and above. This flag should only be
    /// set when the service is running as a Windows Service — the bridge silently ignores
    /// events when the Event Log source is not registered.</para>
    /// </summary>
    public static IHostApplicationBuilder AddMbproxySerilog(
        this IHostApplicationBuilder builder,
        bool addEventLogBridge = false)
    {
        var cfg = new LoggerConfiguration()
            .ReadFrom.Configuration(builder.Configuration);

        if (addEventLogBridge && OperatingSystem.IsWindows())
        {
            cfg = cfg.WriteTo.Sink(
                new EventLogBridge(enabled: true),
                Serilog.Events.LogEventLevel.Error);
        }

        Log.Logger = cfg.CreateLogger();

        builder.Services.AddSerilog(dispose: true);

        return builder;
    }
}
