using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Commons.Browsing;

/// <summary>
///     Serves a captured discovery snapshot through the standard picker session interface.
///     No I/O after open; the tree is point-in-time (picker offers Refresh = close + re-open).
///     The tree may be shared by coalesced sessions — dispose drops only this session's
///     reference. See design §4.3.
/// </summary>
public sealed class CapturedTreeBrowseSession(CapturedTree tree) : IBrowseSession
{
    private CapturedTree? _tree = tree;

    /// <inheritdoc />
    public Guid Token { get; } = Guid.NewGuid();

    /// <inheritdoc />
    public DateTime LastUsedUtc { get; private set; } = DateTime.UtcNow;

    /// <inheritdoc />
    public Task<IReadOnlyList<BrowseNode>> RootAsync(CancellationToken cancellationToken)
    {
        var t = Tree();
        var nodes = t.Root.Children.Select(Map).ToList();
        if (t.Truncated)
            nodes.Add(new BrowseNode("__truncated__",
                "⚠ Results truncated — narrow the driver config or use manual entry",
                BrowseNodeKind.Folder, HasChildrenHint: false));
        LastUsedUtc = DateTime.UtcNow;
        return Task.FromResult<IReadOnlyList<BrowseNode>>(nodes);
    }

    /// <inheritdoc />
    public Task<IReadOnlyList<BrowseNode>> ExpandAsync(string nodeId, CancellationToken cancellationToken)
    {
        var t = Tree();
        LastUsedUtc = DateTime.UtcNow;
        return Task.FromResult<IReadOnlyList<BrowseNode>>(
            t.ById.TryGetValue(nodeId, out var n) ? n.Children.Select(Map).ToList() : []);
    }

    /// <inheritdoc />
    public Task<IReadOnlyList<AttributeInfo>> AttributesAsync(string nodeId, CancellationToken cancellationToken)
    {
        var t = Tree();
        LastUsedUtc = DateTime.UtcNow;
        if (!t.ById.TryGetValue(nodeId, out var n))
            return Task.FromResult<IReadOnlyList<AttributeInfo>>([]);
        var attrs = new List<AttributeInfo>();
        if (n.Attribute is { } a)
            attrs.Add(new AttributeInfo(n.Name, a.DriverDataType.ToString(), a.IsArray,
                a.SecurityClass.ToString(), a.IsAlarm || n.IsAlarmMarked));
        attrs.AddRange(n.Properties.Select(p =>
            new AttributeInfo(p.Name, p.Type.ToString(), false, "", false)));
        return Task.FromResult<IReadOnlyList<AttributeInfo>>(attrs);
    }

    /// <inheritdoc />
    public ValueTask DisposeAsync()
    {
        _tree = null; // drop this session's reference only — tree may be shared (coalescing)
        return ValueTask.CompletedTask;
    }

    private CapturedTree Tree() => _tree ?? throw new ObjectDisposedException(nameof(CapturedTreeBrowseSession));

    private static BrowseNode Map(CapturedNode n) => n.IsLeaf
        ? new BrowseNode(n.Id, n.DisplayName, BrowseNodeKind.Leaf, false)
        : new BrowseNode(n.Id, n.DisplayName, BrowseNodeKind.Folder, n.Children.Count > 0);
}
