using ZB.MOM.WW.OtOpcUa.Server.Security;

namespace ZB.MOM.WW.OtOpcUa.Server.OpcUa;

/// <summary>
///     OPC UA transport security profile selector. Controls which <c>ServerSecurityPolicy</c>
///     entries the endpoint advertises + which token types the <c>UserTokenPolicies</c> permits.
/// </summary>
public enum OpcUaSecurityProfile
{
    /// <summary>Anonymous only on <c>SecurityPolicies.None</c> — dev-only, no signing or encryption.</summary>
    None,

    /// <summary>
    ///     <c>Basic256Sha256 SignAndEncrypt</c> with <c>UserName</c> and <c>Anonymous</c> token
    ///     policies. Clients must present a valid application certificate + user credentials.
    /// </summary>
    Basic256Sha256SignAndEncrypt,
}

/// <summary>
///     OPC UA server endpoint + application-identity configuration. Bound from the
///     <c>OpcUaServer</c> section of <c>appsettings.json</c>. PR 17 minimum-viable scope: no LDAP,
///     no security profiles beyond None — those wire in alongside a future deployment-policy PR
///     that reads from the central config DB instead of appsettings.
/// </summary>
public sealed class OpcUaServerOptions
{
    public const string SectionName = "OpcUaServer";

    /// <summary>
    ///     Fully-qualified endpoint URI clients connect to. Use <c>0.0.0.0</c> to bind all
    ///     interfaces; the stack rewrites to the machine's hostname for the returned endpoint
    ///     description at GetEndpoints time.
    /// </summary>
    public string EndpointUrl { get; init; } = "opc.tcp://0.0.0.0:4840/OtOpcUa";

    /// <summary>Human-readable application name surfaced in the endpoint description.</summary>
    public string ApplicationName { get; init; } = "OtOpcUa Server";

    /// <summary>Stable application URI — must match the subjectAltName of the app cert.</summary>
    public string ApplicationUri { get; init; } = "urn:OtOpcUa:Server";

    /// <summary>
    ///     Directory where the OPC UA stack stores the application certificate + trusted /
    ///     rejected cert folders. Defaults to <c>%ProgramData%\OtOpcUa\pki</c>; the stack
    ///     creates the directory tree on first run and generates a self-signed cert.
    /// </summary>
    public string PkiStoreRoot { get; init; } =
        System.IO.Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
            "OtOpcUa", "pki");

    /// <summary>
    ///     When true, the stack auto-trusts client certs on first connect. Dev-default = true,
    ///     production deployments should flip this to false and manually trust clients via the
    ///     Admin UI.
    /// </summary>
    public bool AutoAcceptUntrustedClientCertificates { get; init; } = true;

    /// <summary>
    ///     Whether to start the Phase 6.1 Stream C <c>/healthz</c> + <c>/readyz</c> HTTP listener.
    ///     Defaults to <c>true</c>; set false in embedded deployments that don't need HTTP
    ///     (e.g. tests that only exercise the OPC UA surface).
    /// </summary>
    public bool HealthEndpointsEnabled { get; init; } = true;

    /// <summary>
    ///     URL prefix the health endpoints bind to. Default <c>http://localhost:4841/</c> — loopback
    ///     avoids Windows URL-ACL elevation. Production deployments that need remote probing should
    ///     either reverse-proxy or use <c>http://+:4841/</c> with netsh urlacl granted.
    /// </summary>
    public string HealthEndpointsPrefix { get; init; } = "http://localhost:4841/";

    /// <summary>
    ///     Security profile advertised on the endpoint. Default <see cref="OpcUaSecurityProfile.None"/>
    ///     preserves the PR 17 endpoint shape; set to <see cref="OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt"/>
    ///     for production deployments with LDAP-backed UserName auth.
    /// </summary>
    public OpcUaSecurityProfile SecurityProfile { get; init; } = OpcUaSecurityProfile.None;

    /// <summary>
    ///     LDAP binding for UserName token validation. Only consulted when the active
    ///     <see cref="SecurityProfile"/> advertises a UserName token policy. When
    ///     <c>LdapOptions.Enabled = false</c>, UserName token attempts are rejected.
    /// </summary>
    public LdapOptions Ldap { get; init; } = new();

    /// <summary>
    ///     Roles granted to anonymous OPC UA sessions. Default empty — anonymous clients can
    ///     read <c>FreeAccess</c> attributes but cannot write <c>Operate</c>/<c>Tune</c>/
    ///     <c>Configure</c> tags (<see cref="WriteAuthzPolicy"/> rejects the empty role set).
    ///     Dev + smoke-test deployments that need anonymous writes populate this with the
    ///     role names they want, e.g. <c>["WriteOperate"]</c> to match v1's anonymous-can-
    ///     operate default. Production deployments leave it empty + route operators through
    ///     UserName auth.
    /// </summary>
    public IReadOnlyList<string> AnonymousRoles { get; init; } = [];
}
