using Akka.Cluster.Hosting;
using Akka.Hosting;
using Akka.Remote.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.OtOpcUa.Commons.Interfaces;

namespace ZB.MOM.WW.OtOpcUa.Cluster;

public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Binds <see cref="AkkaClusterOptions"/> and registers <see cref="IClusterRoleInfo"/>. The
    /// actual ActorSystem + cluster bootstrap is layered on inside the host's <c>AddAkka(...)</c>
    /// configurator via <see cref="WithOtOpcUaClusterBootstrap"/> — keeping the entire Akka graph
    /// under Akka.Hosting's management so cluster singletons land on the same ActorSystem.
    /// </summary>
    /// <param name="services">The service collection to configure.</param>
    /// <param name="configuration">The application configuration containing cluster options.</param>
    public static IServiceCollection AddOtOpcUaCluster(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddOptions<AkkaClusterOptions>()
            .Bind(configuration.GetSection(AkkaClusterOptions.SectionName));

        services.AddSingleton<IClusterRoleInfo, ClusterRoleInfo>();

        return services;
    }

    /// <summary>
    /// Configures the Akka.Hosting builder with the embedded OtOpcUa HOCON (split-brain resolver,
    /// pinned dispatcher, failure detector tuning) + remote endpoint + cluster bootstrap derived
    /// from <see cref="AkkaClusterOptions"/>.
    ///
    /// Wire from Program.cs:
    /// <code>
    /// services.AddAkka("otopcua", (ab, sp) =>
    /// {
    ///     ab.WithOtOpcUaClusterBootstrap(sp);
    ///     if (hasAdmin) ab.WithOtOpcUaControlPlaneSingletons();
    ///     if (hasDriver) ab.WithOtOpcUaRuntimeActors();
    /// });
    /// </code>
    /// </summary>
    /// <param name="builder">The Akka configuration builder to configure.</param>
    /// <param name="serviceProvider">The service provider for resolving cluster options.</param>
    public static AkkaConfigurationBuilder WithOtOpcUaClusterBootstrap(
        this AkkaConfigurationBuilder builder,
        IServiceProvider serviceProvider)
    {
        var options = serviceProvider.GetRequiredService<IOptions<AkkaClusterOptions>>().Value;

        builder.AddHocon(HoconLoader.LoadBaseConfig(), HoconAddMode.Append);

        builder.WithRemoting(new RemoteOptions
        {
            HostName = options.Hostname,
            Port = options.Port,
            PublicHostName = options.PublicHostname,
        });

        builder.WithClustering(new ClusterOptions
        {
            SeedNodes = options.SeedNodes,
            Roles = options.Roles,
        });

        return builder;
    }
}
