using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ZB.MOM.WW.LmxProxy.Host.Domain
{
    /// <summary>
    ///     Interface for SCADA system clients.
    /// </summary>
    public interface IScadaClient : IAsyncDisposable
    {
        /// <summary>
        ///     Gets the connection status.
        /// </summary>
        bool IsConnected { get; }

        /// <summary>
        ///     Gets the current connection state.
        /// </summary>
        ConnectionState ConnectionState { get; }

        /// <summary>
        ///     Occurs when the connection state changes.
        /// </summary>
        event EventHandler<ConnectionStateChangedEventArgs> ConnectionStateChanged;

        /// <summary>
        ///     Connects to the SCADA system.
        /// </summary>
        /// <param name="ct">Cancellation token.</param>
        Task ConnectAsync(CancellationToken ct = default);

        /// <summary>
        ///     Disconnects from the SCADA system.
        /// </summary>
        /// <param name="ct">Cancellation token.</param>
        Task DisconnectAsync(CancellationToken ct = default);

        /// <summary>
        ///     Reads a single tag value from the SCADA system.
        /// </summary>
        /// <param name="address">The tag address.</param>
        /// <param name="ct">Cancellation token.</param>
        /// <returns>The value, timestamp, and quality.</returns>
        Task<Vtq> ReadAsync(string address, CancellationToken ct = default);

        /// <summary>
        ///     Reads multiple tag values from the SCADA system.
        /// </summary>
        /// <param name="addresses">The tag addresses.</param>
        /// <param name="ct">Cancellation token.</param>
        /// <returns>Dictionary of address to VTQ values.</returns>
        Task<IReadOnlyDictionary<string, Vtq>>
            ReadBatchAsync(IEnumerable<string> addresses, CancellationToken ct = default);

        /// <summary>
        ///     Writes a single tag value to the SCADA system.
        /// </summary>
        /// <param name="address">The tag address.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="ct">Cancellation token.</param>
        Task WriteAsync(string address, object value, CancellationToken ct = default);

        /// <summary>
        ///     Writes multiple tag values to the SCADA system.
        /// </summary>
        /// <param name="values">Dictionary of address to value.</param>
        /// <param name="ct">Cancellation token.</param>
        Task WriteBatchAsync(IReadOnlyDictionary<string, object> values, CancellationToken ct = default);

        /// <summary>
        ///     Writes a batch of tag values and a flag tag, then waits for a response tag to
        ///     equal the expected value.
        /// </summary>
        /// <param name="values">The regular tag values to write.</param>
        /// <param name="flagAddress">The address of the flag tag to write.</param>
        /// <param name="flagValue">The value to write to the flag tag.</param>
        /// <param name="responseAddress">The address of the response tag to monitor.</param>
        /// <param name="responseValue">The expected value of the response tag.</param>
        /// <param name="ct">Cancellation token controlling the wait.</param>
        /// <returns>
        ///     <c>true</c> if the response value was observed before cancellation;
        ///     otherwise <c>false</c>.
        /// </returns>
        Task<bool> WriteBatchAndWaitAsync(
            IReadOnlyDictionary<string, object> values,
            string flagAddress,
            object flagValue,
            string responseAddress,
            object responseValue,
            CancellationToken ct = default);

        /// <summary>
        ///     Subscribes to value changes for specified addresses.
        /// </summary>
        /// <param name="addresses">The tag addresses to monitor.</param>
        /// <param name="callback">Callback for value changes.</param>
        /// <param name="ct">Cancellation token.</param>
        /// <returns>Subscription handle for unsubscribing.</returns>
        Task<IAsyncDisposable> SubscribeAsync(IEnumerable<string> addresses, Action<string, Vtq> callback,
            CancellationToken ct = default);
    }
}
