using ZB.MOM.WW.Auth.Abstractions.Ldap;
using LibLdapOptions = ZB.MOM.WW.Auth.Abstractions.Ldap.LdapOptions;

namespace ZB.MOM.WW.OtOpcUa.Security.Ldap;

/// <summary>
///     LDAP + role-mapping configuration for the Admin UI. Bound from <c>appsettings.json</c>
///     <c>Security:Ldap</c> section. Defaults point at the local GLAuth dev instance (see
///     <c>C:\publish\glauth\auth.md</c>).
/// </summary>
/// <remarks>
///     Carries both the wire fields the shared <c>ZB.MOM.WW.Auth.Ldap</c> directory client needs
///     (<see cref="Server"/>/<see cref="Port"/>/<see cref="Transport"/>/…) AND the app-only concerns
///     the shared library has no notion of (<see cref="Enabled"/> master switch,
///     <see cref="DevStubMode"/> dev bypass, <see cref="GroupToRole"/> appsettings role baseline).
///     The app wrapper (<c>OtOpcUaLdapAuthService</c>) projects this onto the library's
///     <see cref="LibLdapOptions"/> at construction; see <see cref="ToLibraryOptions"/>.
/// </remarks>
public sealed class LdapOptions
{
    public const string SectionName = "Security:Ldap";

    /// <summary>Gets or sets a value indicating whether LDAP authentication is enabled.</summary>
    public bool Enabled { get; set; } = true;

    /// <summary>Gets or sets the LDAP server hostname.</summary>
    public string Server { get; set; } = "localhost";

    /// <summary>Gets or sets the LDAP server port.</summary>
    public int Port { get; set; } = 3893;

    /// <summary>
    ///     Transport security for the LDAP connection — <see cref="LdapTransport.Ldaps"/> (implicit
    ///     TLS), <see cref="LdapTransport.StartTls"/> (upgrade), or <see cref="LdapTransport.None"/>
    ///     (plaintext, dev/test only — requires <see cref="AllowInsecure"/>). Replaces the former
    ///     <c>UseTls</c> bool (Task 1.4): <c>true</c>→<see cref="LdapTransport.Ldaps"/>,
    ///     <c>false</c>→<see cref="LdapTransport.None"/>.
    /// </summary>
    public LdapTransport Transport { get; set; } = LdapTransport.None;

    /// <summary>Dev-only escape hatch — must be <c>false</c> in production. Maps to the shared
    ///     library's <see cref="LibLdapOptions.AllowInsecure"/> (renamed from <c>AllowInsecureLdap</c>).</summary>
    public bool AllowInsecure { get; set; }

    /// <summary>
    ///     Dev-only stub: when <c>true</c>, <see cref="OtOpcUaLdapAuthService"/> bypasses the real LDAP
    ///     bind and accepts any non-empty username/password, returning a single Administrator role
    ///     so the operator can navigate the full Admin UI. MUST be <c>false</c> in production.
    /// </summary>
    public bool DevStubMode { get; set; }

    /// <summary>Gets or sets the LDAP search base DN.</summary>
    public string SearchBase { get; set; } = "dc=zb,dc=local";

    /// <summary>
    ///     Service-account DN used for search-then-bind. When empty, a direct-bind with
    ///     <c>cn={user},{SearchBase}</c> is attempted.
    /// </summary>
    public string ServiceAccountDn { get; set; } = string.Empty;

    /// <summary>Gets or sets the service account password for LDAP authentication.</summary>
    public string ServiceAccountPassword { get; set; } = string.Empty;

    /// <summary>Gets or sets the LDAP attribute name for user display name.</summary>
    public string DisplayNameAttribute { get; set; } = "cn";

    /// <summary>Gets or sets the LDAP attribute name for group membership.</summary>
    public string GroupAttribute { get; set; } = "memberOf";

    /// <summary>
    ///     Attribute the service-account search matches the login name against to resolve the
    ///     user's DN. <c>cn</c> for GLAuth (the dev default); set <c>sAMAccountName</c> for
    ///     Active Directory.
    /// </summary>
    public string UserNameAttribute { get; set; } = "cn";

    /// <summary>
    ///     Maps LDAP group name → Admin role. Group match is case-insensitive. A user gets every
    ///     role whose source group is in their membership list. Values are the canonical control-plane
    ///     roles (Task 1.7). Example dev mapping:
    ///     <code>"ReadOnly":"Viewer","ReadWrite":"Designer","AlarmAck":"Administrator"</code>
    /// </summary>
    public Dictionary<string, string> GroupToRole { get; set; } = new(StringComparer.OrdinalIgnoreCase);

    /// <summary>
    ///     Projects the wire fields onto the shared <c>ZB.MOM.WW.Auth.Ldap</c>
    ///     <see cref="LibLdapOptions"/> the directory client consumes. App-only concerns
    ///     (<see cref="DevStubMode"/>, <see cref="GroupToRole"/>) have no library counterpart and are
    ///     handled by the app wrapper around the library service; <see cref="Enabled"/> is carried
    ///     through so the library's own feature gate stays consistent with the app master switch.
    /// </summary>
    public LibLdapOptions ToLibraryOptions() => new()
    {
        Enabled = Enabled,
        Server = Server,
        Port = Port,
        Transport = Transport,
        AllowInsecure = AllowInsecure,
        SearchBase = SearchBase,
        ServiceAccountDn = ServiceAccountDn,
        ServiceAccountPassword = ServiceAccountPassword,
        UserNameAttribute = UserNameAttribute,
        DisplayNameAttribute = DisplayNameAttribute,
        GroupAttribute = GroupAttribute,
    };
}
