namespace ScadaLink.CentralUI.Components.Shared;

/// <summary>
/// Default <see cref="IDialogService"/> implementation. Holds the currently
/// open dialog state in <see cref="Current"/> and notifies subscribers (the
/// <c>DialogHost</c> component) via <see cref="OnChange"/>. Only a single
/// dialog can be open at a time; attempting to open another while one is
/// already active throws <see cref="InvalidOperationException"/> — there is
/// no nested-dialog use case today and surfacing the bug is preferable to
/// silently queuing.
/// </summary>
public class DialogService : IDialogService
{
    /// <summary>
    /// Raised whenever <see cref="Current"/> changes (dialog opened or closed).
    /// The host component subscribes and calls <c>StateHasChanged</c>.
    /// </summary>
    public event Action? OnChange;

    /// <summary>
    /// The dialog currently being displayed, or <c>null</c> when no dialog is
    /// open. The host reads this to decide what (if anything) to render.
    /// </summary>
    public DialogState? Current { get; private set; }

    private TaskCompletionSource<object?>? _tcs;

    public Task<bool> ConfirmAsync(string title, string message, bool danger = false)
    {
        EnsureNoActiveDialog();
        var tcs = new TaskCompletionSource<object?>(TaskCreationOptions.RunContinuationsAsynchronously);
        _tcs = tcs;
        Current = new DialogState(title, DialogKind.Confirm, message, danger, PromptInitial: string.Empty, Placeholder: null);
        OnChange?.Invoke();
        return tcs.Task.ContinueWith(t => t.Result is bool b && b, TaskScheduler.Default);
    }

    public Task<string?> PromptAsync(string title, string label, string initialValue = "", string? placeholder = null)
    {
        EnsureNoActiveDialog();
        var tcs = new TaskCompletionSource<object?>(TaskCreationOptions.RunContinuationsAsynchronously);
        _tcs = tcs;
        Current = new DialogState(title, DialogKind.Prompt, label, Danger: false, PromptInitial: initialValue, Placeholder: placeholder);
        OnChange?.Invoke();
        return tcs.Task.ContinueWith(t => t.Result as string, TaskScheduler.Default);
    }

    /// <summary>
    /// Called by the host component when the user dismisses or confirms the
    /// dialog. <paramref name="result"/> must be a <c>bool</c> for confirms
    /// and a <c>string?</c> for prompts (null = cancel).
    /// </summary>
    internal void Resolve(object? result)
    {
        var tcs = _tcs;
        _tcs = null;
        Current = null;
        OnChange?.Invoke();
        tcs?.TrySetResult(result);
    }

    private void EnsureNoActiveDialog()
    {
        if (Current is not null)
        {
            throw new InvalidOperationException(
                "A dialog is already open. IDialogService does not support nested dialogs.");
        }
    }
}

/// <summary>
/// Snapshot of a dialog's display state, exposed read-only on
/// <see cref="DialogService.Current"/> for the host component to render.
/// </summary>
/// <param name="Title">Modal title text.</param>
/// <param name="Kind">Discriminates between confirm and prompt rendering.</param>
/// <param name="Body">For confirm: the message; for prompt: the input label.</param>
/// <param name="Danger">When true, the confirm button uses danger styling.</param>
/// <param name="PromptInitial">Initial value for prompt-kind dialogs.</param>
/// <param name="Placeholder">Placeholder shown when the prompt input is empty.</param>
public record DialogState(
    string Title,
    DialogKind Kind,
    string Body,
    bool Danger,
    string PromptInitial,
    string? Placeholder);

public enum DialogKind
{
    Confirm,
    Prompt
}
