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 alarms panel.
/// </summary>
public partial class AlarmsViewModel : ObservableObject
{
    private readonly IUiDispatcher _dispatcher;
    private readonly IOpcUaClientService _service;

    [ObservableProperty] private int _interval = 1000;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SubscribeCommand))]
    [NotifyCanExecuteChangedFor(nameof(UnsubscribeCommand))]
    [NotifyCanExecuteChangedFor(nameof(RefreshCommand))]
    private bool _isConnected;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SubscribeCommand))]
    [NotifyCanExecuteChangedFor(nameof(UnsubscribeCommand))]
    [NotifyCanExecuteChangedFor(nameof(RefreshCommand))]
    private bool _isSubscribed;

    [ObservableProperty] private string? _monitoredNodeIdText;

    [ObservableProperty] private int _activeAlarmCount;

    public AlarmsViewModel(IOpcUaClientService service, IUiDispatcher dispatcher)
    {
        _service = service;
        _dispatcher = dispatcher;
        _service.AlarmEvent += OnAlarmEvent;
    }

    /// <summary>Received alarm events.</summary>
    public ObservableCollection<AlarmEventViewModel> AlarmEvents { get; } = [];

    private void OnAlarmEvent(object? sender, AlarmEventArgs e)
    {
        // Only display alarm/condition events (those with a ConditionName), not generic events
        if (string.IsNullOrEmpty(e.ConditionName)) return;

        _dispatcher.Post(() =>
        {
            // Find existing row by source + condition and update it, or add new
            var existing = AlarmEvents.FirstOrDefault(a =>
                a.SourceName == e.SourceName && a.ConditionName == e.ConditionName);

            if (existing != null)
            {
                var index = AlarmEvents.IndexOf(existing);
                AlarmEvents[index] = new AlarmEventViewModel(
                    e.SourceName, e.ConditionName, e.Severity, e.Message,
                    e.Retain, e.ActiveState, e.AckedState, e.Time,
                    e.EventId, e.ConditionNodeId);

                // Remove alarms that are no longer retained
                if (!e.Retain)
                    AlarmEvents.RemoveAt(index);
            }
            else if (e.Retain)
            {
                AlarmEvents.Add(new AlarmEventViewModel(
                    e.SourceName, e.ConditionName, e.Severity, e.Message,
                    e.Retain, e.ActiveState, e.AckedState, e.Time,
                    e.EventId, e.ConditionNodeId));
            }

            ActiveAlarmCount = AlarmEvents.Count(a => a.ActiveState && !a.AckedState);
        });
    }

    private bool CanSubscribe()
    {
        return IsConnected && !IsSubscribed;
    }

    [RelayCommand(CanExecute = nameof(CanSubscribe))]
    private async Task SubscribeAsync()
    {
        try
        {
            var sourceNodeId = string.IsNullOrWhiteSpace(MonitoredNodeIdText)
                ? null
                : NodeId.Parse(MonitoredNodeIdText);

            await _service.SubscribeAlarmsAsync(sourceNodeId, Interval);
            IsSubscribed = true;

            try
            {
                await _service.RequestConditionRefreshAsync();
            }
            catch
            {
                // Refresh not supported
            }
        }
        catch
        {
            // Subscribe failed
        }
    }

    private bool CanUnsubscribe()
    {
        return IsConnected && IsSubscribed;
    }

    [RelayCommand(CanExecute = nameof(CanUnsubscribe))]
    private async Task UnsubscribeAsync()
    {
        try
        {
            await _service.UnsubscribeAlarmsAsync();
            IsSubscribed = false;
        }
        catch
        {
            // Unsubscribe failed
        }
    }

    [RelayCommand(CanExecute = nameof(CanUnsubscribe))]
    private async Task RefreshAsync()
    {
        try
        {
            await _service.RequestConditionRefreshAsync();
        }
        catch
        {
            // Refresh failed
        }
    }

    /// <summary>
    ///     Acknowledges an alarm and returns (success, message).
    /// </summary>
    public async Task<(bool Success, string Message)> AcknowledgeAlarmAsync(AlarmEventViewModel alarm, string comment)
    {
        if (!IsConnected || alarm.EventId == null || alarm.ConditionNodeId == null)
            return (false, "Alarm cannot be acknowledged (missing EventId or ConditionId).");

        try
        {
            var result = await _service.AcknowledgeAlarmAsync(alarm.ConditionNodeId, alarm.EventId, comment);
            if (Opc.Ua.StatusCode.IsGood(result))
                return (true, "Alarm acknowledged successfully.");
            return (false, $"Acknowledge failed: {Helpers.StatusCodeFormatter.Format(result)}");
        }
        catch (Exception ex)
        {
            return (false, $"Error: {ex.Message}");
        }
    }

    /// <summary>
    ///     Returns the monitored node ID for persistence, or null if not subscribed.
    /// </summary>
    public string? GetAlarmSourceNodeId()
    {
        return IsSubscribed ? MonitoredNodeIdText : null;
    }

    /// <summary>
    ///     Restores an alarm subscription and requests a condition refresh.
    /// </summary>
    public async Task RestoreAlarmSubscriptionAsync(string? sourceNodeId)
    {
        if (!IsConnected || string.IsNullOrWhiteSpace(sourceNodeId)) return;

        MonitoredNodeIdText = sourceNodeId;

        try
        {
            var nodeId = string.IsNullOrWhiteSpace(sourceNodeId)
                ? null
                : NodeId.Parse(sourceNodeId);

            await _service.SubscribeAlarmsAsync(nodeId, Interval);
            IsSubscribed = true;

            try
            {
                await _service.RequestConditionRefreshAsync();
            }
            catch
            {
                // Refresh not supported
            }
        }
        catch
        {
            // Subscribe failed
        }
    }

    /// <summary>
    ///     Clears alarm events and resets state.
    /// </summary>
    public void Clear()
    {
        AlarmEvents.Clear();
        IsSubscribed = false;
        ActiveAlarmCount = 0;
    }

    /// <summary>
    ///     Unhooks event handlers from the service.
    /// </summary>
    public void Teardown()
    {
        _service.AlarmEvent -= OnAlarmEvent;
    }
}