namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip;

/// <summary>
///     Swappable scanner that walks a controller's symbol table (via libplctag's
///     <c>@tags</c> pseudo-tag or the CIP Symbol Object class 0x6B) and yields the tags it
///     finds. Defaults to <see cref="EmptyAbCipTagEnumeratorFactory"/> which returns no
///     controller-side tags — the full <c>@tags</c> decoder lands as a follow-up PR once
///     libplctag 1.5.2 either gains <c>TagInfoPlcMapper</c> upstream or we ship our own
///     <c>IPlcMapper</c> for the Symbol Object byte layout (tracked via follow-up task; PR 5
///     ships the abstraction + pre-declared-tag emission).
/// </summary>
public interface IAbCipTagEnumerator : IDisposable
{
    /// <summary>
    ///     Enumerate the controller's tags for one device. Callers iterate asynchronously so
    ///     large symbol tables don't require buffering the entire list.
    /// </summary>
    IAsyncEnumerable<AbCipDiscoveredTag> EnumerateAsync(
        AbCipTagCreateParams deviceParams,
        CancellationToken cancellationToken);
}

/// <summary>Factory for per-driver enumerators.</summary>
public interface IAbCipTagEnumeratorFactory
{
    IAbCipTagEnumerator Create();
}

/// <summary>One tag yielded by <see cref="IAbCipTagEnumerator.EnumerateAsync"/>.</summary>
/// <param name="Name">Logix symbolic name as returned by the Symbol Object.</param>
/// <param name="ProgramScope">Program name if the tag is program-scoped; <c>null</c> for controller scope.</param>
/// <param name="DataType">Detected data type; <see cref="AbCipDataType.Structure"/> when the tag
///     is UDT-typed — the UDT shape lookup + per-member expansion ship with PR 6.</param>
/// <param name="ReadOnly"><c>true</c> when the Symbol Object's External Access attribute forbids writes.</param>
/// <param name="IsSystemTag">Hint from the enumerator that this is a system / infrastructure tag;
///     the driver applies <see cref="AbCipSystemTagFilter"/> on top so the enumerator is not the
///     single source of truth.</param>
public sealed record AbCipDiscoveredTag(
    string Name,
    string? ProgramScope,
    AbCipDataType DataType,
    bool ReadOnly,
    bool IsSystemTag = false);

/// <summary>
///     Default production enumerator — currently returns an empty sequence. The real <c>@tags</c>
///     walk lands as a follow-up PR. Documented in <c>driver-specs.md §3</c> as the gap the
///     Symbol Object walker closes.
/// </summary>
internal sealed class EmptyAbCipTagEnumerator : IAbCipTagEnumerator
{
    public async IAsyncEnumerable<AbCipDiscoveredTag> EnumerateAsync(
        AbCipTagCreateParams deviceParams,
        [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken)
    {
        await Task.CompletedTask;
        yield break;
    }

    public void Dispose() { }
}

/// <summary>Factory for <see cref="EmptyAbCipTagEnumerator"/>.</summary>
internal sealed class EmptyAbCipTagEnumeratorFactory : IAbCipTagEnumeratorFactory
{
    public IAbCipTagEnumerator Create() => new EmptyAbCipTagEnumerator();
}
