using Microsoft.AspNetCore.Components;

namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;

/// <summary>
/// Centralised dialog/modal service. Pages inject this service and call
/// <see cref="ConfirmAsync"/>, <see cref="PromptAsync"/>, or
/// <see cref="ShowAsync{TResult}"/> programmatically instead of embedding
/// per-page modal components. A single <c>DialogHost</c> rendered in
/// <c>MainLayout</c> displays the resulting dialog state.
/// </summary>
public interface IDialogService
{
    /// <summary>
    /// Shows a confirmation dialog and resolves to <c>true</c> when the user
    /// confirms, or <c>false</c> when the user cancels (button click, Escape,
    /// or backdrop dismiss).
    /// </summary>
    /// <param name="title">Modal title text.</param>
    /// <param name="message">Body text shown to the user.</param>
    /// <param name="danger">When <c>true</c>, the confirm button renders in
    /// <c>btn-danger</c> styling with the label "Delete"; otherwise a primary
    /// "Confirm" button is shown.</param>
    /// <returns>A task that resolves to <c>true</c> when the user confirms, or <c>false</c> when cancelled.</returns>
    Task<bool> ConfirmAsync(string title, string message, bool danger = false);

    /// <summary>
    /// Shows a single-line text prompt and resolves to the entered value, or
    /// <c>null</c> if the user cancels.
    /// </summary>
    /// <param name="title">Modal title text.</param>
    /// <param name="label">Label rendered above the input field.</param>
    /// <param name="initialValue">Pre-populated value for the input field.</param>
    /// <param name="placeholder">Optional placeholder shown when the input is empty.</param>
    /// <returns>A task that resolves to the entered string, or <c>null</c> if the user cancels.</returns>
    Task<string?> PromptAsync(string title, string label, string initialValue = "", string? placeholder = null);

    /// <summary>
    /// Shows a modal hosting arbitrary custom body content and resolves to the
    /// value the body closes with, or <c>default</c> (<c>null</c> for reference
    /// types) if the user cancels (the body's own cancel path, the header close
    /// button, or Escape). The body fragment receives a
    /// <see cref="DialogContext{TResult}"/> and supplies its own action buttons —
    /// the standard Cancel/Confirm footer is NOT rendered for this kind. Use this
    /// to centralise modal chrome (backdrop, focus trap, focus restoration) while
    /// keeping page-specific form content inside the body.
    /// </summary>
    /// <typeparam name="TResult">The type the body closes the dialog with.</typeparam>
    /// <param name="title">Modal title text.</param>
    /// <param name="body">Renders the modal body; receives the context whose
    /// <c>Close</c>/<c>Cancel</c> methods resolve the returned task.</param>
    /// <param name="size">Optional Bootstrap modal-dialog size modifier applied to
    /// the <c>modal-dialog</c> element (e.g. <c>modal-lg</c>, <c>modal-sm</c>).
    /// <c>null</c> leaves the default width.</param>
    /// <returns>A task that resolves to the value passed to
    /// <see cref="DialogContext{TResult}.Close"/>, or <c>default</c> on cancel.</returns>
    Task<TResult?> ShowAsync<TResult>(string title, RenderFragment<DialogContext<TResult>> body, string? size = null);
}

/// <summary>
/// Handed to the body fragment of a <see cref="IDialogService.ShowAsync{TResult}"/>
/// dialog so the body can close itself with a typed result or cancel. Both
/// methods route back through the owning <see cref="DialogService"/> to complete
/// the awaited <c>ShowAsync</c> task and tear down the modal.
/// </summary>
/// <typeparam name="TResult">The type the dialog resolves with.</typeparam>
public sealed class DialogContext<TResult>
{
    // Routes the close back to the owning service's Resolve(). Held as a typed
    // delegate rather than a service reference so the context stays a small,
    // self-contained closure handle and never widens the body's surface.
    private readonly Action<object?> _resolve;

    /// <summary>
    /// Creates a context bound to the given resolve callback. The callback boxes
    /// the result and forwards it to <see cref="DialogService.Resolve"/>.
    /// </summary>
    /// <param name="resolve">Invoked with the boxed result (<c>null</c> = cancel).</param>
    internal DialogContext(Action<object?> resolve) => _resolve = resolve;

    /// <summary>
    /// Closes the dialog and resolves the awaited <c>ShowAsync</c> task with
    /// <paramref name="result"/>.
    /// </summary>
    /// <param name="result">The value the caller awaits.</param>
    public void Close(TResult result) => _resolve(result);

    /// <summary>
    /// Cancels the dialog and resolves the awaited <c>ShowAsync</c> task with
    /// <c>default</c> (<c>null</c> for reference types).
    /// </summary>
    public void Cancel() => _resolve(null);
}
