using DevExpress.Blazor;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components;

namespace TIAMSharedUI.Shared.Components
{
    /// <summary>
    /// A MessageBox (async and synchronized) component to provide a way to show informational messages.
    /// </summary>
    public partial class PopupMessageBox
    {

        public static readonly string ButtonOk = "Ok";
        public static readonly string ButtonCancel = "Cancel";
        public static readonly string ButtonYes = "Yes";
        public static readonly string ButtonNo = "No";

        [Inject]
        private NavigationManager Navigation { get; set; } = default!;

        /// <summary>
        /// The MessageBox popup. This is a DxPopup that is called asynchronously so it is displayed and control returns
        /// to the caller.
        /// </summary>
        private DxPopup MessageBoxPopup { get; set; } = default!;

        /// <summary>
        /// The header text in the MessageBox popup. This is a MarkupString so do not put any user entered text in this!
        /// </summary>
        private string HeaderText { get; set; } = string.Empty;

        /// <summary>
        /// The message text in the MessageBox popup. This is a MarkupString so do not put any user entered text in this!
        /// </summary>
        private string MessageHtml { get; set; } = string.Empty;

        private Func<string, object?, Task>? OnClick { get; set; }

        private Func<object?, Task>? OnClose { get; set; }

        /// <summary>
        /// This is passed in in the call to Show() and returned in the calls to OnClick() and OnClose().
        /// </summary>
        private object? Tag { get; set; }

        /// <summary>
        /// Used to make the popup synchronous.
        /// </summary>
        private TaskCompletionSource<string> _taskCompletionSource = new();

        /// <inheritdoc />
        public PopupMessageBox()
        {
            ButtonVisible = new bool[4];
            ButtonText = new string[4];
        }

        /// <summary>
        /// Displays the MessageBox. This is a static object so you can't create multiple instances of it. But you
        /// can create another instance in the onClick or onCLose calls as the existing instance will be closed by
        /// then.
        /// </summary>
        /// <param name="header">The header text in the message box.</param>
        /// <param name="message">The main message in the message box. This is treated as html
        ///     so use <code>HttpUtility.HtmlEncode()</code> on any user generated text in the message.</param>
        /// <param name="tag">Caller defined data returned in the OnClick/OnClose calls.</param>
        /// <param name="onClick">Will call this method when a button is clicked. Passes the button text as the parameter.</param>
        /// <param name="onClose">Will call this when the popup is closed. Including after receiving the onClick call.</param>
        /// <param name="buttonText">The text for the button(s). Can be 1 .. 4 buttons.</param>
        /// <returns>The task for the underlying DxPopup.ShowAsync() call.</returns>
        public Task<bool> ShowAsync(string header, string message, object? tag, Func<string, object?, Task>? onClick,
            Func<object?, Task>? onClose, params string[] buttonText)
        {

            HeaderText = header;
            MessageHtml = message;
            Tag = tag;
            OnClick = onClick;
            OnClose = onClose;

            if (buttonText.Length == 0 || buttonText.Length > 4)
                throw new ArgumentOutOfRangeException(nameof(buttonText), "Must have between 1 and 4 buttons");
            Array.Clear(ButtonVisible);
            Array.Clear(ButtonText);
            for (var index = 0; index < buttonText.Length; index++)
            {
                ButtonVisible[index] = true;
                ButtonText[index] = buttonText[index];
            }

            StateHasChanged();

            return MessageBoxPopup.ShowAsync();
        }

        /// <summary>
        /// Displays the MessageBox. Then when click OK, goes to the url. This is a static object so you can't create
        /// multiple instances of it. 
        /// </summary>
        /// <param name="header">The header text in the message box.</param>
        /// <param name="message">The main message in the message box. This is treated as html
        /// so use <code>HttpUtility.HtmlEncode()</code> on any user generated text in the message.</param>
        /// <param name="url">The url to navigate to when click OK.</param>
        /// <returns>The task for the underlying DxPopup.ShowAsync() call.</returns>
        public Task<bool> ShowThenRedirectAsync(string header, string message, string url)
        {
            return ShowAsync(header, message, null,
                (_, _) =>
                {
                    Navigation.NavigateTo(url, false);
                    return Task.CompletedTask;
                },
                null, PopupMessageBox.ButtonOk);
        }

        /// <summary>
        /// Displays the MessageBox synchronously. Returns the text of the button clicked. This is a static object
        /// so you can't create multiple instances of it. But you can create another instance when it returns as the
        /// existing instance will be closed by then.
        /// </summary>
        /// <param name="header">The header text in the message box.</param>
        /// <param name="message">The main message in the message box. This is treated as html
        /// so use <code>HttpUtility.HtmlEncode()</code> on any user generated text in the message.</param>
        /// <param name="buttonText">The text for the button(s). Can be 1 .. 4 buttons.</param>
        /// <returns>The text of the button clicked.</returns>
        public Task<string> Show(string header, string message, params string[] buttonText)
        {
            _taskCompletionSource = new TaskCompletionSource<string>();

            ShowAsync(header, message, null, ShowOnClick, null, buttonText);

            // we return the Task from the TaskCompletionSource that is not completed
            return _taskCompletionSource.Task;
        }

        private Task ShowOnClick(string buttonText, object? tag)
        {
            // as this is called from the OnClick handler, the popup has been closed.

            // sets the TaskCompletionSource to completed.  Any await-ers will now complete
            _taskCompletionSource.SetResult(buttonText);
            return Task.FromResult(_taskCompletionSource);
        }

        private string[] ButtonText { get; }

        private bool[] ButtonVisible { get; }

        private async Task Button0Click(MouseEventArgs arg)
        {
            // need to close before calling OnClick because OnClick may call ShowAsync again
            await MessageBoxPopup.CloseAsync();
            if (OnClick != null)
                await OnClick.Invoke(ButtonText[0], Tag);
        }

        private async Task Button1Click(MouseEventArgs arg)
        {
            // need to close before calling OnClick because OnClick may call ShowAsync again
            await MessageBoxPopup.CloseAsync();
            if (OnClick != null)
                await OnClick.Invoke(ButtonText[1], Tag);
        }
        private async Task Button2Click(MouseEventArgs arg)
        {
            // need to close before calling OnClick because OnClick may call ShowAsync again
            await MessageBoxPopup.CloseAsync();
            if (OnClick != null)
                await OnClick.Invoke(ButtonText[2], Tag);
        }
        private async Task Button3Click(MouseEventArgs arg)
        {
            // need to close before calling OnClick because OnClick may call ShowAsync again
            await MessageBoxPopup.CloseAsync();
            if (OnClick != null)
                await OnClick.Invoke(ButtonText[3], Tag);
        }

        private async Task PopupClosed(PopupClosedEventArgs arg)
        {
            // need to close before calling OnClose because OnClose may call ShowAsync again
            await MessageBoxPopup.CloseAsync();
            if (OnClose != null)
                await OnClose.Invoke(Tag);
        }
    }
}