using System;
using System.Threading;
using System.Threading.Tasks;
using DragonISO.Engine.Controller;
using DragonISO.Engine.Domain;

namespace DragonISO.App.ViewModels;

/// <summary>
/// View-model for the per-participant <see cref="FrameProcessingSettings"/> override
/// editor. Holds the operator's in-progress choice of framerate / resolution /
/// aspect / audio for a single participant, and exposes Apply / Clear / Cancel
/// commands that drive the engine through <see cref="IIsoController"/>.
///
/// "Following global settings" state is reflected via <see cref="HasOverride"/>,
/// which is true exactly when the participant currently has a non-null override
/// on the engine side (i.e. their pipeline beats the global defaults).
/// </summary>
public sealed class IsoOverrideDialogViewModel : ObservableObject
{
    private readonly IIsoController _controller;
    private readonly ToastViewModel? _toast;
    private TargetFramerate _framerate;
    private TargetResolution _resolution;
    private AspectMode _aspect;
    private AudioMode _audio;
    private bool _hasOverride;

    /// <summary>
    /// Participant identity. The engine API is Guid-keyed; we cache the display
    /// name once at construction so the dialog title doesn't need to re-resolve
    /// the participant from the live list (and so it survives the participant
    /// going offline mid-dialog).
    /// </summary>
    public Guid ParticipantId { get; }
    public string DisplayName { get; }

    /// <summary>
    /// Reference to the existing <see cref="GlobalSettingsViewModel"/> so the
    /// dialog's ComboBoxes can bind directly to its Available* lists â€” there's
    /// no point duplicating the Enum.GetValues calls here.
    /// </summary>
    public GlobalSettingsViewModel Settings { get; }

    /// <summary>
    /// Action the dialog code-behind wires up so the VM can close the window
    /// after Apply / Clear / Cancel without taking a Window dependency.
    /// </summary>
    public Action? RequestClose { get; set; }

    public IsoOverrideDialogViewModel(
        IIsoController controller,
        GlobalSettingsViewModel settings,
        Guid participantId,
        string displayName,
        FrameProcessingSettings? currentOverride,
        ToastViewModel? toast = null)
    {
        _controller = controller;
        Settings = settings;
        _toast = toast;
        ParticipantId = participantId;
        DisplayName = displayName;

        // Initialize the four enum values from the existing override (if any)
        // or fall back to the global settings â€” the dialog should always open
        // with values that already reflect what this pipeline is using.
        var source = currentOverride ?? new FrameProcessingSettings(
            settings.Framerate,
            settings.Resolution,
            settings.Aspect,
            settings.Audio);

        _framerate = source.Framerate;
        _resolution = source.Resolution;
        _aspect = source.Aspect;
        _audio = source.Audio;
        _hasOverride = currentOverride is not null;

        ApplyCommand = new AsyncRelayCommand(ApplyAsync);
        ClearCommand = new AsyncRelayCommand(ClearAsync, () => _hasOverride);
        CancelCommand = new RelayCommand(() => RequestClose?.Invoke());
    }

    public TargetFramerate Framerate { get => _framerate; set => SetField(ref _framerate, value); }
    public TargetResolution Resolution { get => _resolution; set => SetField(ref _resolution, value); }
    public AspectMode Aspect { get => _aspect; set => SetField(ref _aspect, value); }
    public AudioMode Audio { get => _audio; set => SetField(ref _audio, value); }

    /// <summary>
    /// True when the participant currently has a non-null override (i.e. this
    /// dialog opened on an already-overridden pipeline). Toggles the visibility
    /// of the "Following global settings" indicator and gates the
    /// <see cref="ClearCommand"/>.
    /// </summary>
    public bool HasOverride
    {
        get => _hasOverride;
        private set
        {
            if (SetField(ref _hasOverride, value))
            {
                OnPropertyChanged(nameof(FollowingGlobalsVisible));
                ClearCommand.RaiseCanExecuteChanged();
            }
        }
    }

    /// <summary>
    /// Convenience for XAML visibility binding â€” true when we should show the
    /// "Following global settings Â· Reset to global" affordance.
    /// </summary>
    public bool FollowingGlobalsVisible => _hasOverride;

    public AsyncRelayCommand ApplyCommand { get; }
    public AsyncRelayCommand ClearCommand { get; }
    public RelayCommand CancelCommand { get; }

    private async Task ApplyAsync()
    {
        var settings = new FrameProcessingSettings(_framerate, _resolution, _aspect, _audio);
        await _controller.SetIsoOverrideAsync(ParticipantId, settings, CancellationToken.None);
        HasOverride = true;
        _toast?.Show($"Override saved for {DisplayName}");
        RequestClose?.Invoke();
    }

    private async Task ClearAsync()
    {
        await _controller.SetIsoOverrideAsync(ParticipantId, null, CancellationToken.None);
        HasOverride = false;
        _toast?.Show($"{DisplayName} now follows global settings");
        RequestClose?.Invoke();
    }
}
