using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Transport;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Templates;

namespace ZB.MOM.WW.ScadaBridge.DeploymentManager;

public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Configuration section that <see cref="DeploymentManagerOptions"/> is bound to.
    /// The Host binds this section to <c>appsettings.json</c> (see
    /// <c>Program.cs</c>); component libraries do not depend on
    /// <c>IConfiguration</c> directly, consistent with the Options-pattern
    /// convention enforced by <c>OptionsTests</c>.
    /// </summary>
    public const string OptionsSection = "ScadaBridge:DeploymentManager";

    /// <summary>
    /// Registers the Deployment Manager services. <see cref="DeploymentManagerOptions"/>
    /// is registered via <see cref="OptionsServiceCollectionExtensions.AddOptions"/> so
    /// <c>IOptions&lt;DeploymentManagerOptions&gt;</c> is always resolvable; the Host
    /// binds <see cref="OptionsSection"/> to configuration so the operation-lock and
    /// artifact-deployment timeouts are tunable via <c>appsettings.json</c>.
    /// </summary>
    /// <param name="services">The service collection to register into.</param>
    /// <returns>The <paramref name="services"/> instance for chaining.</returns>
    public static IServiceCollection AddDeploymentManager(this IServiceCollection services)
    {
        // Ensure the options class is always resolvable.
        // The Host binds OptionsSection to appsettings.json; absent that binding
        // the declared option-class defaults apply. Eager startup validation
        // (arch-review 08 §1.5): ValidateOnStart makes a zero/negative timeout —
        // which would fire a linked-CTS deadline immediately (or throw in the
        // CancellationTokenSource ctor) — fail fast at boot with a clear,
        // key-naming message. Applies even though defaults are code-configured.
        services.AddOptions<DeploymentManagerOptions>().ValidateOnStart();
        services.TryAddEnumerable(
            ServiceDescriptor.Singleton<IValidateOptions<DeploymentManagerOptions>, DeploymentManagerOptionsValidator>());
        services.AddSingleton<OperationLockManager>();

        // Template/instance version watermark backing the flatten-session cache key
        // and the staleness fast path. TryAdd because AddConfigurationDatabase
        // registers the same singleton — whichever composition root runs first wins
        // and both observe one instance.
        services.TryAddSingleton<ITemplateGraphWatermark, TemplateGraphWatermark>();

        // Push-based deployment-status notification. Registered
        // as a singleton so the scoped DeploymentService and the Central UI's
        // scoped Blazor page component share one instance — both run in the
        // same central Host process. The deployment-status page subscribes to
        // it instead of polling the database every 10 seconds.
        services.AddSingleton<IDeploymentStatusNotifier, DeploymentStatusNotifier>();

        services.AddScoped<IFlatteningPipeline, FlatteningPipeline>();
        services.AddScoped<DeploymentService>();
        services.AddScoped<ArtifactDeploymentService>();

        // Expose the flattening pipeline's revision-hash computation
        // to the Transport bundle importer through the Commons IStaleInstanceProbe
        // seam, so a template overwrite can enumerate the deployed instances whose
        // flattened config has drifted from their deployed snapshot. Scoped so it
        // shares the per-request DbContext with the in-flight import.
        services.AddScoped<IStaleInstanceProbe, StaleInstanceProbe>();
        return services;
    }

    /// <summary>
    /// Registers Deployment Manager Akka actor bindings. Actor creation is handled by the Host during actor system startup.
    /// </summary>
    /// <param name="services">The service collection to register into.</param>
    /// <returns>The <paramref name="services"/> instance for chaining.</returns>
    public static IServiceCollection AddDeploymentManagerActors(this IServiceCollection services)
    {
        // Akka actor registration is handled by Host component during actor system startup
        return services;
    }
}
