# Base Akka.NET cluster configuration for OtOpcUa fused-host nodes.
#
# Roles, seed nodes, public hostname/port, and the actor system name are overlaid
# at runtime by ServiceCollectionExtensions.WithOtOpcUaClusterBootstrap (via Akka.Hosting).
# Everything else here is the cluster-wide tuning that should match across nodes.
#
# Any divergence from these defaults must be deliberate and recorded in docs/v2/Architecture.md.

akka {
    # Akka logger wiring (route ILoggingAdapter → Serilog) is configured via Akka.Hosting's
    # ConfigureLoggers in ServiceCollectionExtensions.WithOtOpcUaClusterBootstrap — HOCON
    # `akka.loggers` alone is not honored by Akka.Hosting. logger-startup-timeout is kept here
    # since the Serilog logger can be slow to initialize at startup.
    logger-startup-timeout = 30s

    extensions = [
        "Akka.Cluster.Tools.PublishSubscribe.DistributedPubSubExtensionProvider, Akka.Cluster.Tools"
    ]

    actor {
        provider = cluster
    }

    remote {
        dot-netty.tcp {
            hostname = "0.0.0.0"
            port = 4053
        }
        transport-failure-detector {
            heartbeat-interval = 2s
            acceptable-heartbeat-pause = 10s
        }
    }

    cluster {
        seed-nodes = []
        roles = []
        min-nr-of-members = 1

        # Split-brain resolver (arch-review 03/S1). This block IS active: Akka.Cluster.Hosting's
        # WithClustering enables an SBR downing provider by default (it applies
        # SplitBrainResolverOption.Default when ClusterOptions.SplitBrainResolver is null), and that
        # provider reads this block. ServiceCollectionExtensions.BuildClusterOptions additionally sets
        # the typed KeepOldestOption { DownIfAlone = true } to make the strategy EXPLICIT in code —
        # its active-strategy + keep-oldest.down-if-alone MUST match this block. (The cluster is NOT
        # NoDowning; only an explicit downing-provider-class = "" would disable failover.)
        # stable-after must stay >= failure-detector.acceptable-heartbeat-pause (10s) + margin.
        split-brain-resolver {
            active-strategy = "keep-oldest"
            stable-after = 15s
            keep-oldest {
                down-if-alone = on
            }
        }

        failure-detector {
            heartbeat-interval = 2s
            threshold = 10.0
            acceptable-heartbeat-pause = 10s
        }

        down-removal-margin = 15s
        run-coordinated-shutdown-when-down = on

        singleton {
            singleton-name = "singleton"
        }
        singleton-proxy {
            singleton-identification-interval = 1s
        }
    }

    coordinated-shutdown {
        run-by-clr-shutdown-hook = on
        default-phase-timeout = 30s
    }
}

# Pinned dispatcher used by OpcUaPublishActor (Task 44) so the OPC UA SDK sees
# only one thread per actor instance — its session/subscription locks expect
# strict single-threaded access.
opcua-synchronized-dispatcher {
    type = "PinnedDispatcher"
    executor = "thread-pool-executor"
    throughput = 1
}
