using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Opc.Ua;
using ZB.MOM.WW.OtOpcUa.Client.Shared;
using ZB.MOM.WW.OtOpcUa.Client.UI.Services;

namespace ZB.MOM.WW.OtOpcUa.Client.UI.ViewModels;

/// <summary>
///     ViewModel for the read/write panel.
/// </summary>
public partial class ReadWriteViewModel : ObservableObject
{
    private readonly IUiDispatcher _dispatcher;
    private readonly IOpcUaClientService _service;

    [ObservableProperty] private string? _currentStatus;

    [ObservableProperty] private string? _currentValue;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(ReadCommand))]
    [NotifyCanExecuteChangedFor(nameof(WriteCommand))]
    private bool _isConnected;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(ReadCommand))]
    [NotifyCanExecuteChangedFor(nameof(WriteCommand))]
    private string? _selectedNodeId;

    [ObservableProperty] private string? _serverTimestamp;

    [ObservableProperty] private string? _sourceTimestamp;

    [ObservableProperty] private string? _writeStatus;

    [ObservableProperty] private string? _writeValue;

    /// <summary>Initializes a new instance of the ReadWriteViewModel class.</summary>
    /// <param name="service">The OPC UA client service for read/write operations.</param>
    /// <param name="dispatcher">The UI dispatcher for posting updates to the UI thread.</param>
    public ReadWriteViewModel(IOpcUaClientService service, IUiDispatcher dispatcher)
    {
        _service = service;
        _dispatcher = dispatcher;
    }

    /// <summary>Gets a value indicating whether a node is currently selected.</summary>
    public bool IsNodeSelected => !string.IsNullOrEmpty(SelectedNodeId);

    partial void OnSelectedNodeIdChanged(string? value)
    {
        OnPropertyChanged(nameof(IsNodeSelected));
        if (!string.IsNullOrEmpty(value) && IsConnected) _ = ExecuteReadAsync();
    }

    private bool CanReadOrWrite()
    {
        return IsConnected && !string.IsNullOrEmpty(SelectedNodeId);
    }

    [RelayCommand(CanExecute = nameof(CanReadOrWrite))]
    private async Task ReadAsync()
    {
        await ExecuteReadAsync();
    }

    private async Task ExecuteReadAsync()
    {
        if (string.IsNullOrEmpty(SelectedNodeId)) return;

        try
        {
            var nodeId = NodeId.Parse(SelectedNodeId);
            var dataValue = await _service.ReadValueAsync(nodeId);

            _dispatcher.Post(() =>
            {
                CurrentValue = Helpers.ValueFormatter.Format(dataValue.Value);
                CurrentStatus = Helpers.StatusCodeFormatter.Format(dataValue.StatusCode);
                SourceTimestamp = dataValue.SourceTimestamp.ToString("O");
                ServerTimestamp = dataValue.ServerTimestamp.ToString("O");
            });
        }
        catch (Exception ex)
        {
            _dispatcher.Post(() =>
            {
                CurrentValue = null;
                CurrentStatus = $"Error: {ex.Message}";
                SourceTimestamp = null;
                ServerTimestamp = null;
            });
        }
    }

    [RelayCommand(CanExecute = nameof(CanReadOrWrite))]
    private async Task WriteAsync()
    {
        if (string.IsNullOrEmpty(SelectedNodeId) || WriteValue == null) return;

        try
        {
            var nodeId = NodeId.Parse(SelectedNodeId);
            var statusCode = await _service.WriteValueAsync(nodeId, WriteValue);

            _dispatcher.Post(() => { WriteStatus = statusCode.ToString(); });
        }
        catch (Exception ex)
        {
            _dispatcher.Post(() => { WriteStatus = $"Error: {ex.Message}"; });
        }
    }

    /// <summary>
    ///     Clears all displayed values.
    /// </summary>
    public void Clear()
    {
        SelectedNodeId = null;
        CurrentValue = null;
        CurrentStatus = null;
        SourceTimestamp = null;
        ServerTimestamp = null;
        WriteValue = null;
        WriteStatus = null;
    }
}