using Mbproxy.Admin;
using Mbproxy.Configuration;
using Mbproxy.Diagnostics;
using Mbproxy.Options;
using Mbproxy.Proxy;
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>, and registers
    /// the schema-level <see cref="MbproxyOptionsValidator"/>.
    ///
    /// Phase 06: also registers <see cref="ServiceCounters"/> (singleton) and
    /// <see cref="ConfigReconciler"/> (singleton) 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>();

        // Phase 06: service-wide counters (read by Phase 07 status page).
        builder.Services.AddSingleton<ServiceCounters>();

        // Phase 06: hot-reload reconciler (singleton; subscribes to IOptionsMonitor.OnChange).
        builder.Services.AddSingleton<ConfigReconciler>();

        return builder;
    }

    /// <summary>
    /// Registers Phase 07 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"/> (hosted service — 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).
    /// </summary>
    public static IHostApplicationBuilder AddMbproxyAdmin(this IHostApplicationBuilder builder)
    {
        builder.Services.AddSingleton<AssemblyVersionAccessor>();
        builder.Services.AddSingleton<StatusSnapshotBuilder>();
        // Register AdminEndpointHost as a singleton so ShutdownCoordinator can inject it
        // directly without going through the IHostedService collection.
        builder.Services.AddSingleton<AdminEndpointHost>();
        builder.Services.AddHostedService(sp => sp.GetRequiredService<AdminEndpointHost>());
        return builder;
    }

    /// <summary>
    /// Configures Serilog from the <c>"Serilog"</c> configuration section,
    /// with console and rolling-file sinks as defaults.
    ///
    /// <para>Phase 08: 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;
    }
}
