using Microsoft.Extensions.Options;

namespace ZB.MOM.WW.MxGateway.Server.Configuration;

/// <summary>Provides the effective gateway configuration with sensitive values redacted.</summary>
public sealed class GatewayConfigurationProvider(IOptions<GatewayOptions> options) : IGatewayConfigurationProvider
{
    /// <summary>Marker string for redacted sensitive configuration values.</summary>
    public const string RedactedValue = "[redacted]";

    /// <inheritdoc />
    public EffectiveGatewayConfiguration GetEffectiveConfiguration()
    {
        GatewayOptions value = options.Value;

        return new EffectiveGatewayConfiguration(
            Authentication: new EffectiveAuthenticationConfiguration(
                Mode: value.Authentication.Mode.ToString(),
                SqlitePath: value.Authentication.SqlitePath,
                PepperSecretName: RedactedValue,
                RunMigrationsOnStartup: value.Authentication.RunMigrationsOnStartup),
            Ldap: new EffectiveLdapConfiguration(
                Enabled: value.Ldap.Enabled,
                Server: value.Ldap.Server,
                Port: value.Ldap.Port,
                Transport: value.Ldap.Transport.ToString(),
                AllowInsecure: value.Ldap.AllowInsecure,
                SearchBase: value.Ldap.SearchBase,
                ServiceAccountDn: value.Ldap.ServiceAccountDn,
                ServiceAccountPassword: RedactedValue,
                UserNameAttribute: value.Ldap.UserNameAttribute,
                DisplayNameAttribute: value.Ldap.DisplayNameAttribute,
                GroupAttribute: value.Ldap.GroupAttribute,
                FallbackServers: value.Ldap.FallbackServers),
            Worker: new EffectiveWorkerConfiguration(
                ExecutablePath: value.Worker.ExecutablePath,
                WorkingDirectory: value.Worker.WorkingDirectory,
                RequiredArchitecture: value.Worker.RequiredArchitecture.ToString(),
                StartupTimeoutSeconds: value.Worker.StartupTimeoutSeconds,
                ShutdownTimeoutSeconds: value.Worker.ShutdownTimeoutSeconds,
                HeartbeatIntervalSeconds: value.Worker.HeartbeatIntervalSeconds,
                HeartbeatGraceSeconds: value.Worker.HeartbeatGraceSeconds,
                MaxMessageBytes: value.Worker.MaxMessageBytes),
            Sessions: new EffectiveSessionConfiguration(
                DefaultCommandTimeoutSeconds: value.Sessions.DefaultCommandTimeoutSeconds,
                MaxSessions: value.Sessions.MaxSessions,
                MaxPendingCommandsPerSession: value.Sessions.MaxPendingCommandsPerSession,
                DefaultLeaseSeconds: value.Sessions.DefaultLeaseSeconds,
                LeaseSweepIntervalSeconds: value.Sessions.LeaseSweepIntervalSeconds,
                DetachGraceSeconds: value.Sessions.DetachGraceSeconds,
                AllowMultipleEventSubscribers: value.Sessions.AllowMultipleEventSubscribers,
                MaxEventSubscribersPerSession: value.Sessions.MaxEventSubscribersPerSession),
            Events: new EffectiveEventConfiguration(
                QueueCapacity: value.Events.QueueCapacity,
                BackpressurePolicy: value.Events.BackpressurePolicy.ToString(),
                ReplayBufferCapacity: value.Events.ReplayBufferCapacity,
                ReplayRetentionSeconds: value.Events.ReplayRetentionSeconds),
            Dashboard: new EffectiveDashboardConfiguration(
                Enabled: value.Dashboard.Enabled,
                AllowAnonymousLocalhost: value.Dashboard.AllowAnonymousLocalhost,
                SnapshotIntervalMilliseconds: value.Dashboard.SnapshotIntervalMilliseconds,
                RecentFaultLimit: value.Dashboard.RecentFaultLimit,
                RecentSessionLimit: value.Dashboard.RecentSessionLimit,
                ShowTagValues: value.Dashboard.ShowTagValues,
                GroupToRole: value.Dashboard.GroupToRole,
                // Rebuilt rather than passed through because the value type widens from string[]
                // to IReadOnlyList<string>; the source dictionary's own comparer is carried over
                // (case-insensitive by default) so the projected map still matches LDAP group
                // names in whatever case the directory returns them.
                GroupToTag: value.Dashboard.GroupToTag.ToDictionary(
                    pair => pair.Key,
                    pair => (IReadOnlyList<string>)pair.Value,
                    value.Dashboard.GroupToTag.Comparer),
                UntaggedSessionVisibility: value.Dashboard.UntaggedSessionVisibility),
            Protocol: new EffectiveProtocolConfiguration(
                value.Protocol.WorkerProtocolVersion,
                value.Protocol.MaxGrpcMessageBytes));
    }
}
