namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;

/// <summary>
/// Pure placement policy for monitored items across OPC UA subscription shards: first
/// shard with free capacity, otherwise a new shard. Factored out of
/// <see cref="RealOpcUaClient"/> so the budget arithmetic is testable without a live
/// server (the SDK's Session/Subscription types cannot be faked).
/// </summary>
internal static class MonitoredItemShardPlanner
{
    /// <summary>
    /// Plans where <paramref name="itemCount"/> new items go given the current per-shard
    /// item counts. Returns one shard index per item, in order; an index equal to or above
    /// <paramref name="currentCounts"/>.Count means "a shard that must be created first".
    /// </summary>
    /// <param name="currentCounts">Item count of each existing shard, in shard order.</param>
    /// <param name="budget">Maximum items per shard; values &lt;= 0 fall back to 1.</param>
    /// <param name="itemCount">Number of items to place.</param>
    /// <returns>Shard index per item, in request order.</returns>
    public static IReadOnlyList<int> Plan(IReadOnlyList<int> currentCounts, int budget, int itemCount)
    {
        var effectiveBudget = budget > 0 ? budget : 1;
        var counts = currentCounts.ToList();
        var placement = new List<int>(itemCount);

        for (var i = 0; i < itemCount; i++)
        {
            var target = -1;
            for (var shard = 0; shard < counts.Count; shard++)
            {
                if (counts[shard] < effectiveBudget)
                {
                    target = shard;
                    break;
                }
            }

            if (target < 0)
            {
                counts.Add(0);
                target = counts.Count - 1;
            }

            counts[target]++;
            placement.Add(target);
        }

        return placement;
    }

    /// <summary>
    /// Number of shards needed to hold <paramref name="itemCount"/> items at
    /// <paramref name="budget"/> items per shard, starting from nothing.
    /// </summary>
    /// <param name="itemCount">Total monitored items.</param>
    /// <param name="budget">Maximum items per shard; values &lt;= 0 fall back to 1.</param>
    /// <returns>The shard count.</returns>
    public static int ShardCountFor(int itemCount, int budget)
    {
        var effectiveBudget = budget > 0 ? budget : 1;
        return (itemCount + effectiveBudget - 1) / effectiveBudget;
    }
}
