using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Sonex.Data.Database;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace Sonex.Client.Dialogs
{
    public static class MessageDialog
    {
        private const int DialogAlreadyOpenHResult = unchecked((int)0x80000019);
        private static readonly TimeSpan DialogRetryDelay = TimeSpan.FromMilliseconds(200);
        private static readonly SemaphoreSlim DialogLock = new(1, 1);

        public static async Task ShowMessageAsync(this FrameworkElement element, DB.Result? result)
        {
            if (result == null || !result.Success)
                await ShowMessageAsync(element, "Error", result?.ErrorMessage ?? "");
        }

        public static async Task ShowMessageAsync(this FrameworkElement element, string title, string content)
        {
            await ShowDialogAsync(
                element,
                title,
                content,
                closeButtonText: "OK");
        }

        public static Task<ContentDialogResult> ShowQuestionAsync(
            this FrameworkElement element,
            string title,
            string content,
            string primaryButtonText,
            string closeButtonText,
            string? secondaryButtonText = null)
        {
            ArgumentException.ThrowIfNullOrWhiteSpace(primaryButtonText);
            ArgumentException.ThrowIfNullOrWhiteSpace(closeButtonText);

            return ShowDialogAsync(
                element,
                title,
                content,
                primaryButtonText: primaryButtonText,
                secondaryButtonText: secondaryButtonText,
                closeButtonText: closeButtonText);
        }

        public static Task ShowMessageAsync(this Window window, string title, string content)
        {
            ArgumentNullException.ThrowIfNull(window);

            if (window.Content is not FrameworkElement root)
                throw new InvalidOperationException("Window.Content is not a FrameworkElement.");

            return root.ShowMessageAsync(title, content);
        }

        public static Task<ContentDialogResult> ShowQuestionAsync(
            this Window window,
            string title,
            string content,
            string primaryButtonText,
            string closeButtonText,
            string? secondaryButtonText = null)
        {
            ArgumentNullException.ThrowIfNull(window);

            if (window.Content is not FrameworkElement root)
                throw new InvalidOperationException("Window.Content is not a FrameworkElement.");

            return root.ShowQuestionAsync(
                title,
                content,
                primaryButtonText,
                closeButtonText,
                secondaryButtonText);
        }

        private static async Task<ContentDialogResult> ShowDialogAsync(
            FrameworkElement element,
            string title,
            string content,
            string? primaryButtonText = null,
            string? secondaryButtonText = null,
            string? closeButtonText = null)
        {
            ArgumentNullException.ThrowIfNull(element);

            await DialogLock.WaitAsync();
            try
            {
                while (true)
                {
                    var xamlRoot = element.XamlRoot;
                    if (xamlRoot == null)
                        return ContentDialogResult.None;

                    var dialog = new ContentDialog
                    {
                        XamlRoot = xamlRoot,
                        Title = title,
                        Content = content,
                        DefaultButton = GetDefaultButton(primaryButtonText, secondaryButtonText, closeButtonText)
                    };

                    if (!string.IsNullOrWhiteSpace(primaryButtonText))
                        dialog.PrimaryButtonText = primaryButtonText;

                    if (!string.IsNullOrWhiteSpace(secondaryButtonText))
                        dialog.SecondaryButtonText = secondaryButtonText;

                    if (!string.IsNullOrWhiteSpace(closeButtonText))
                        dialog.CloseButtonText = closeButtonText;

                    try
                    {
                        return await dialog.ShowAsync();
                    }
                    catch (COMException ex) when (ex.HResult == DialogAlreadyOpenHResult)
                    {
                        await Task.Delay(DialogRetryDelay);
                    }
                }
            }
            finally
            {
                DialogLock.Release();
            }
        }

        private static ContentDialogButton GetDefaultButton(
            string? primaryButtonText,
            string? secondaryButtonText,
            string? closeButtonText)
        {
            if (!string.IsNullOrWhiteSpace(closeButtonText))
                return ContentDialogButton.Close;

            if (!string.IsNullOrWhiteSpace(primaryButtonText))
                return ContentDialogButton.Primary;

            if (!string.IsNullOrWhiteSpace(secondaryButtonText))
                return ContentDialogButton.Secondary;

            return ContentDialogButton.Primary;
        }
    }
}
