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

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

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

    [ObservableProperty] private DateTimeOffset? _endTime = DateTimeOffset.UtcNow;

    [ObservableProperty] private double _intervalMs = 3600000;

    [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(ReadHistoryCommand))]
    private bool _isConnected;

    [ObservableProperty] private bool _isLoading;

    [ObservableProperty] private int _maxValues = 1000;

    [ObservableProperty] private AggregateType? _selectedAggregateType;

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

    [ObservableProperty] private DateTimeOffset? _startTime = DateTimeOffset.UtcNow.AddHours(-1);

    /// <summary>Initializes a new instance of the HistoryViewModel.</summary>
    /// <param name="service">The OPC UA client service.</param>
    /// <param name="dispatcher">The UI dispatcher for thread marshalling.</param>
    public HistoryViewModel(IOpcUaClientService service, IUiDispatcher dispatcher)
    {
        _service = service;
        _dispatcher = dispatcher;
    }

    /// <summary>Available aggregate types (null means "Raw").</summary>
    public IReadOnlyList<AggregateType?> AggregateTypes { get; } =
    [
        null,
        AggregateType.Average,
        AggregateType.Minimum,
        AggregateType.Maximum,
        AggregateType.Count,
        AggregateType.Start,
        AggregateType.End,
        AggregateType.StandardDeviation
    ];

    /// <summary>Gets a value indicating whether an aggregate read is selected.</summary>
    public bool IsAggregateRead => SelectedAggregateType != null;

    /// <summary>History read results.</summary>
    public ObservableCollection<HistoryValueViewModel> Results { get; } = [];

    partial void OnSelectedAggregateTypeChanged(AggregateType? value)
    {
        OnPropertyChanged(nameof(IsAggregateRead));
    }

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

    [RelayCommand(CanExecute = nameof(CanReadHistory))]
    private async Task ReadHistoryAsync()
    {
        if (string.IsNullOrEmpty(SelectedNodeId)) return;

        _dispatcher.Post(() => IsLoading = true);
        _dispatcher.Post(() => Results.Clear());

        try
        {
            var nodeId = NodeId.Parse(SelectedNodeId);
            IReadOnlyList<DataValue> values;

            var start = (StartTime ?? DateTimeOffset.UtcNow.AddHours(-1)).UtcDateTime;
            var end = (EndTime ?? DateTimeOffset.UtcNow).UtcDateTime;

            if (SelectedAggregateType != null)
                values = await _service.HistoryReadAggregateAsync(
                    nodeId,
                    start,
                    end,
                    SelectedAggregateType.Value,
                    IntervalMs);
            else
                values = await _service.HistoryReadRawAsync(
                    nodeId,
                    start,
                    end,
                    MaxValues);

            _dispatcher.Post(() =>
            {
                foreach (var dv in values)
                    Results.Add(new HistoryValueViewModel(
                        Helpers.ValueFormatter.Format(dv.Value),
                        Helpers.StatusCodeFormatter.Format(dv.StatusCode),
                        dv.SourceTimestamp.ToString("O"),
                        dv.ServerTimestamp.ToString("O")));
            });
        }
        catch (Exception ex)
        {
            _dispatcher.Post(() =>
            {
                Results.Add(new HistoryValueViewModel(
                    $"Error: {ex.Message}", string.Empty, string.Empty, string.Empty));
            });
        }
        finally
        {
            _dispatcher.Post(() => IsLoading = false);
        }
    }

    /// <summary>
    ///     Clears results and resets state.
    /// </summary>
    public void Clear()
    {
        Results.Clear();
        SelectedNodeId = null;
    }
}