using Akka.Actor;
using Akka.Cluster.Hosting;
using Akka.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.ControlPlane.AdminOperations;
using ZB.MOM.WW.OtOpcUa.ControlPlane.Audit;
using ZB.MOM.WW.OtOpcUa.ControlPlane.Coordinators;
using ZB.MOM.WW.OtOpcUa.ControlPlane.Fleet;
using ZB.MOM.WW.OtOpcUa.ControlPlane.Redundancy;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.ControlPlane;

public static class ServiceCollectionExtensions
{
    public const string AdminRole = "admin";

    public const string ConfigPublishSingletonName = "config-publish";
    public const string AdminOperationsSingletonName = "admin-operations";
    public const string AuditWriterSingletonName = "audit-writer";
    public const string FleetStatusSingletonName = "fleet-status";
    public const string RedundancyStateSingletonName = "redundancy-state";

    /// <summary>
    /// Registers all five admin-role cluster singletons + their proxies on the AkkaConfigurationBuilder.
    /// Must be called against the same builder used by <c>AkkaHostedService</c> so the singletons
    /// share the host's ActorSystem.
    /// </summary>
    /// <param name="builder">The Akka configuration builder.</param>
    /// <returns>The builder for fluent chaining.</returns>
    /// <remarks>
    /// Wire from the fused Host's Program.cs:
    /// <code>
    /// builder.Services.AddAkka("otopcua", (ab, sp) =>
    /// {
    ///     ab.WithRemoting(/* ... */).WithClustering(/* ... */);
    ///     ab.WithOtOpcUaControlPlaneSingletons();
    /// });
    /// </code>
    /// </remarks>
    public static AkkaConfigurationBuilder WithOtOpcUaControlPlaneSingletons(this AkkaConfigurationBuilder builder)
    {
        var singletonOptions = new ClusterSingletonOptions { Role = AdminRole };
        var proxyOptions = new ClusterSingletonOptions { Role = AdminRole };

        builder.WithSingleton<ConfigPublishCoordinatorKey>(
            ConfigPublishSingletonName,
            (system, registry, resolver) =>
            {
                var dbFactory = resolver.GetService<IDbContextFactory<OtOpcUaConfigDbContext>>();
                return ConfigPublishCoordinator.Props(dbFactory);
            },
            singletonOptions);

        builder.WithSingleton<AdminOperationsActorKey>(
            AdminOperationsSingletonName,
            (system, registry, resolver) =>
            {
                var dbFactory = resolver.GetService<IDbContextFactory<OtOpcUaConfigDbContext>>();
                var coordinator = registry.Get<ConfigPublishCoordinatorKey>();
                var probes = resolver.GetService<IEnumerable<IDriverProbe>>() ?? Enumerable.Empty<IDriverProbe>();
                // R2-11 (05/CONV-2): deploy-gate TagConfig strictness mode from appsettings
                // (Deployment:TagConfigValidationMode = Warn default | Error opt-in).
                var mode = Enum.TryParse<TagConfigValidationMode>(
                    resolver.GetService<IConfiguration>()?["Deployment:TagConfigValidationMode"],
                    ignoreCase: true, out var m) ? m : TagConfigValidationMode.Warn;
                return AdminOperationsActor.Props(dbFactory, coordinator, probes, mode);
            },
            singletonOptions);

        builder.WithSingleton<AuditWriterActorKey>(
            AuditWriterSingletonName,
            (system, registry, resolver) =>
            {
                var dbFactory = resolver.GetService<IDbContextFactory<OtOpcUaConfigDbContext>>();
                return AuditWriterActor.Props(dbFactory);
            },
            singletonOptions);

        builder.WithSingleton<FleetStatusBroadcasterKey>(
            FleetStatusSingletonName,
            (system, registry, resolver) => FleetStatusBroadcaster.Props(),
            singletonOptions);

        builder.WithSingleton<RedundancyStateActorKey>(
            RedundancyStateSingletonName,
            (system, registry, resolver) => RedundancyStateActor.Props(),
            singletonOptions);

        return builder;
    }
}

/// <summary>Marker key types used by <c>Akka.Hosting</c> to resolve singletons from the registry.</summary>
public sealed class ConfigPublishCoordinatorKey { }
public sealed class AdminOperationsActorKey { }
public sealed class AuditWriterActorKey { }
public sealed class FleetStatusBroadcasterKey { }
public sealed class RedundancyStateActorKey { }
