using System;
using System.Threading.Tasks;
using System.Windows.Input;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Layout;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using ReactiveUI;

namespace Artemis.UI.Shared.Services.Builders;

/// <summary>
///     Represents a builder that can be used to create notifications.
/// </summary>
public class NotificationBuilder
{
    private readonly InfoBar _infoBar;
    private readonly Window _parent;
    private TimeSpan _timeout = TimeSpan.FromSeconds(5);

    /// <summary>
    ///     Creates a new instance of the <see cref="NotificationBuilder" /> class.
    /// </summary>
    /// <param name="parent">The parent window that will host the notification.</param>
    public NotificationBuilder(Window parent)
    {
        _parent = parent;
        _infoBar = new InfoBar();
        _infoBar.Classes.Add("notification-info-bar");
    }

    /// <summary>
    ///     Changes the title of the notification.
    /// </summary>
    /// <param name="title">The new title.</param>
    /// <returns>The notification builder that can be used to further build the notification.</returns>
    public NotificationBuilder WithTitle(string? title)
    {
        _infoBar.Title = title;
        return this;
    }

    /// <summary>
    ///     Changes the message of the notification.
    /// </summary>
    /// <param name="content">The new message.</param>
    /// <returns>The notification builder that can be used to further build the notification.</returns>
    public NotificationBuilder WithMessage(string? content)
    {
        _infoBar.Message = content;
        return this;
    }

    /// <summary>
    ///     Changes the timeout of the notification after which it disappears automatically.
    /// </summary>
    /// <param name="timeout">The timeout of the notification after which it disappears automatically.</param>
    /// <returns>The notification builder that can be used to further build the notification.</returns>
    public NotificationBuilder WithTimeout(TimeSpan timeout)
    {
        _timeout = timeout;
        return this;
    }

    /// <summary>
    ///     Changes the vertical position of the notification inside the parent window.
    /// </summary>
    /// <param name="position">The vertical position of the notification inside the parent window.</param>
    /// <returns>The notification builder that can be used to further build the notification.</returns>
    public NotificationBuilder WithVerticalPosition(VerticalAlignment position)
    {
        _infoBar.VerticalAlignment = position;
        return this;
    }

    /// <summary>
    ///     Changes the horizontal position of the notification inside the parent window.
    /// </summary>
    /// <param name="position">The horizontal position of the notification inside the parent window.</param>
    /// <returns>The notification builder that can be used to further build the notification.</returns>
    public NotificationBuilder WithHorizontalPosition(HorizontalAlignment position)
    {
        _infoBar.HorizontalAlignment = position;
        return this;
    }

    /// <summary>
    ///     Changes the severity (color) of the notification.
    /// </summary>
    /// <param name="severity">The severity (color) of the notification.</param>
    /// <returns>The notification builder that can be used to further build the notification.</returns>
    public NotificationBuilder WithSeverity(NotificationSeverity severity)
    {
        _infoBar.Severity = (InfoBarSeverity) severity;
        return this;
    }

    /// <summary>
    ///     Changes the action button of the notification.
    /// </summary>
    /// <param name="configure">An action to configure the button.</param>
    /// <returns>The notification builder that can be used to further build the notification.</returns>
    public NotificationBuilder HavingButton(Action<NotificationButtonBuilder> configure)
    {
        NotificationButtonBuilder builder = new();
        configure(builder);
        _infoBar.ActionButton = builder.Build();

        return this;
    }

    /// <summary>
    ///     Shows the notification.
    /// </summary>
    /// <returns>An action that can be called to hide the notification.</returns>
    /// <exception cref="ArtemisSharedUIException" />
    public Action Show()
    {
        Dispatcher.UIThread.Post(() =>
        {
            OverlayLayer? overlayLayer = OverlayLayer.GetOverlayLayer(_parent);
            if (overlayLayer == null)
                throw new ArtemisSharedUIException("Can't display a notification on a window an overlay layer.");

            NotificationHost container = new() {Content = _infoBar};
            overlayLayer.Children.Add(container);
            _infoBar.Closed += InfoBarOnClosed;
            _infoBar.IsOpen = true;

            Dispatcher.UIThread.InvokeAsync(async () =>
            {
                await Task.Delay(_timeout);
                _infoBar.IsOpen = false;
            });

            return;

            void InfoBarOnClosed(InfoBar sender, InfoBarClosedEventArgs args)
            {
                overlayLayer.Children.Remove(container);
                _infoBar.Closed -= InfoBarOnClosed;
            }
        });

        return () => Dispatcher.UIThread.Post(() => _infoBar.IsOpen = false);
    }
}

/// <summary>
///     Represents a builder that can be used to create buttons inside notifications.
/// </summary>
public class NotificationButtonBuilder
{
    private Action? _action;
    private Func<Task>? _asyncAction;
    private ICommand? _command;
    private object? _commandParameter;
    private string _text = "Text";

    /// <summary>
    ///     Changes text message of the button.
    /// </summary>
    /// <param name="text">The new text.</param>
    /// <returns>The notification builder that can be used to further build the button.</returns>
    public NotificationButtonBuilder WithText(string text)
    {
        _text = text;
        return this;
    }

    /// <summary>
    ///     Changes action that is called when the button is clicked.
    /// </summary>
    /// <param name="action">The action to call when the button is clicked.</param>
    /// <returns>The builder that can be used to further build the button.</returns>
    public NotificationButtonBuilder WithAction(Action action)
    {
        _command = null;
        _action = action;
        return this;
    }

    /// <summary>
    ///     Changes action that is called when the button is clicked.
    /// </summary>
    /// <param name="action">The action to call when the button is clicked.</param>
    /// <returns>The builder that can be used to further build the button.</returns>
    public NotificationButtonBuilder WithAction(Func<Task> action)
    {
        _command = null;
        _asyncAction = action;
        return this;
    }

    /// <summary>
    ///     Changes command that is called when the button is clicked.
    /// </summary>
    /// <param name="command">The command to call when the button is clicked.</param>
    /// <returns>The builder that can be used to further build the button.</returns>
    public NotificationButtonBuilder WithCommand(ICommand command)
    {
        _action = null;
        _command = command;
        return this;
    }

    /// <summary>
    ///     Changes parameter of the command that is called when the button is clicked.
    /// </summary>
    /// <param name="commandParameter">The parameter of the command to call when the button is clicked.</param>
    /// <returns>The builder that can be used to further build the button.</returns>
    public NotificationButtonBuilder WithCommandParameter(object? commandParameter)
    {
        _commandParameter = commandParameter;
        return this;
    }

    internal Control Build()
    {
        Button button = new() {Content = _text};
        button.Classes.Add("AppBarButton");

        if (_action != null)
        {
            button.Command = ReactiveCommand.Create(() => _action());
        }
        else if (_asyncAction != null)
        {
            button.Command = ReactiveCommand.CreateFromTask(() => _asyncAction());
        }
        else if (_command != null)
        {
            button.Command = _command;
            button.CommandParameter = _commandParameter;
        }

        return button;
    }
}

/// <summary>
///     Represents a severity of a notification.
/// </summary>
public enum NotificationSeverity
{
    /// <summary>
    ///     A severity for informational messages.
    /// </summary>
    Informational,

    /// <summary>
    ///     A severity for success messages.
    /// </summary>
    Success,

    /// <summary>
    ///     A severity for warning messages.
    /// </summary>
    Warning,

    /// <summary>
    ///     A severity for error messages.
    /// </summary>
    Error
}