using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using Radzen.Blazor.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Radzen.Blazor
{
    /// <summary>
    /// A horizontal menu component with support for nested submenus, icons, and responsive behavior.
    /// RadzenMenu provides a classic menu bar for navigation, typically used in application headers or toolbars.
    /// Displays menu items horizontally with dropdown submenus.
    /// Supports multi-level nested menus via RadzenMenuItem child items, automatic navigation via Path property or custom Click handlers,
    /// icons displayed alongside menu item text, responsive design that automatically collapses to a hamburger menu on small screens (configurable),
    /// click-to-open or hover-to-open interaction modes, keyboard navigation (Arrow keys, Enter, Escape) for accessibility, and visual separators between menu items.
    /// Use for application navigation bars, command menus, or toolbar-style interfaces. Menu items are defined using RadzenMenuItem components as child content.
    /// </summary>
    /// <example>
    /// Basic menu with navigation:
    /// <code>
    /// &lt;RadzenMenu&gt;
    ///     &lt;RadzenMenuItem Text="Home" Path="/" Icon="home" /&gt;
    ///     &lt;RadzenMenuItem Text="Data"&gt;
    ///         &lt;RadzenMenuItem Text="Orders" Path="/orders" /&gt;
    ///         &lt;RadzenMenuItem Text="Customers" Path="/customers" /&gt;
    ///     &lt;/RadzenMenuItem&gt;
    ///     &lt;RadzenMenuItem Text="Reports" Path="/reports" /&gt;
    /// &lt;/RadzenMenu&gt;
    /// </code>
    /// Menu with click handlers:
    /// <code>
    /// &lt;RadzenMenu Click=@OnMenuClick&gt;
    ///     &lt;RadzenMenuItem Text="File"&gt;
    ///         &lt;RadzenMenuItem Text="New" Value="new" Icon="add" /&gt;
    ///         &lt;RadzenMenuItem Text="Open" Value="open" Icon="folder_open" /&gt;
    ///         &lt;RadzenMenuItem Text="Save" Value="save" Icon="save" /&gt;
    ///     &lt;/RadzenMenuItem&gt;
    /// &lt;/RadzenMenu&gt;
    /// </code>
    /// </example>
    public partial class RadzenMenu : RadzenComponentWithChildren
    {
        /// <summary>
        /// Gets or sets whether the menu should automatically collapse to a hamburger menu on small screens.
        /// When enabled, displays a toggle button that expands/collapses the menu on mobile devices.
        /// </summary>
        /// <value><c>true</c> to enable responsive behavior with hamburger menu; <c>false</c> for always-horizontal menu. Default is <c>true</c>.</value>
        [Parameter]
        public bool Responsive { get; set; } = true;

        /// <summary>
        /// Gets or sets the interaction mode for opening submenus.
        /// When true, submenus open on click. When false, submenus open on hover (desktop) and click (touch devices).
        /// </summary>
        /// <value><c>true</c> to open on click; <c>false</c> to open on hover. Default is <c>true</c>.</value>
        [Parameter]
        public bool ClickToOpen { get; set; } = true;

        /// <summary>
        /// Gets or sets whether nested submenus should fly out horizontally to the side instead of expanding vertically inline.
        /// When enabled, 2nd level and deeper submenus appear as cascading flyout menus positioned to the right of their parent item.
        /// </summary>
        /// <value><c>true</c> to enable flyout submenus; <c>false</c> for default accordion-style nesting. Default is <c>false</c>.</value>
        [Parameter]
        public bool Flyout { get; set; }

        private bool IsOpen { get; set; }

        /// <inheritdoc />
        protected override string GetComponentCssClass() => ClassList.Create("rz-menu")
                                                                     .Add("rz-menu-open", Responsive && IsOpen)
                                                                     .Add("rz-menu-closed", Responsive && !IsOpen)
                                                                     .Add("rz-menu-flyout", Flyout)
                                                                     .ToString();

        IJSObjectReference? _jsRef;
        bool _clickToOpenChanged;

        /// <inheritdoc />
        public override async Task SetParametersAsync(ParameterView parameters)
        {
            if (parameters.DidParameterChange(nameof(ClickToOpen), ClickToOpen))
            {
                _clickToOpenChanged = true;
            }

            await base.SetParametersAsync(parameters);
        }

        /// <inheritdoc />
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            await base.OnAfterRenderAsync(firstRender);

            if ((firstRender || _clickToOpenChanged) && Visible && JSRuntime != null)
            {
                _clickToOpenChanged = false;

                if (_jsRef != null)
                {
                    await _jsRef.InvokeVoidAsync("dispose");
                    await _jsRef.DisposeAsync();
                }

                _jsRef = await JSRuntime.InvokeAsync<IJSObjectReference>(
                    "Radzen.createMenu", Element, ClickToOpen);
            }
        }

        void OnToggle()
        {
            IsOpen = !IsOpen;
        }

        /// <summary>
        /// Gets or sets the click callback.
        /// </summary>
        /// <value>The click callback.</value>
        [Parameter]
        public EventCallback<MenuItemEventArgs> Click { get; set; }

        private string? ariaLabel;

        /// <summary>
        /// Gets or sets the menu aria label text.
        /// </summary>
        /// <value>The menu aria label text.</value>
        [Parameter]
        public string AriaLabel { get => ariaLabel ?? Localize(nameof(RadzenStrings.Menu_AriaLabel)); set => ariaLabel = value; }

        [Inject]
        NavigationManager? NavigationManager { get; set; }

        bool subMenuOpen;
        internal int focusedIndex = -1;
        bool preventKeyPress = true;
        bool stopKeydownPropagation;
        async Task OnKeyPress(KeyboardEventArgs args)
        {
            var key = args.Code != null ? args.Code : args.Key;

            if (currentItems.Count == 0)
            {
                currentItems = items.Where(i => i.Visible && !i.Disabled).ToList();
            }

            if (key == "ArrowUp" || key == "ArrowDown")
            {
                preventKeyPress = true;
                stopKeydownPropagation = true;

                if (subMenuOpen)
                {
                    focusedIndex = Math.Clamp(focusedIndex + (key == "ArrowUp" ? -1 : 1), 0, currentItems.Count - 1);
                }
                else
                {
                    if (key == "ArrowDown" && currentItems.Count > 0)
                    {
                        focusedIndex = Math.Clamp(focusedIndex, 0, currentItems.Count - 1);

                        var item = currentItems[focusedIndex];

                        if (item.items.Count > 0)
                        {
                            currentItems = item.items.Where(i => i.Visible && !i.Disabled).ToList();
                            focusedIndex = -1;
                            subMenuOpen = true;
                            await item.Open();
                        }
                    }
                }
            }
            else if (key == "ArrowLeft" || key == "ArrowRight")
            {
                preventKeyPress = true;
                stopKeydownPropagation = true;

                // Flyout mode: ArrowRight opens nested submenu, ArrowLeft closes it
                if (Flyout && subMenuOpen)
                {
                    if (key == "ArrowRight" && focusedIndex >= 0 && focusedIndex < currentItems.Count)
                    {
                        var item = currentItems[focusedIndex];
                        if (item.items.Count > 0)
                        {
                            currentItems = item.items.Where(i => i.Visible && !i.Disabled).ToList();
                            focusedIndex = 0;
                            subMenuOpen = true;
                            await item.Open();
                            return;
                        }
                    }
                    else if (key == "ArrowLeft")
                    {
                        var firstItem = currentItems.FirstOrDefault();
                        var parentItem = firstItem?.ParentItem;
                        if (parentItem?.ParentItem != null)
                        {
                            currentItems = parentItem.ParentItem.items.Where(i => i.Visible && !i.Disabled).ToList();
                            focusedIndex = currentItems.IndexOf(parentItem);
                            subMenuOpen = true;
                            await parentItem.Close();
                            return;
                        }
                    }
                }

                bool shouldOpenNextMenu = false;
                if (subMenuOpen)
                {
                    var firstItem = currentItems.FirstOrDefault();
                    var parentItem = firstItem?.ParentItem;
                    if (parentItem != null && parentItem.Parent != null)
                    {
                        currentItems = parentItem.Parent.items.Where(i => i.Visible && !i.Disabled).ToList();
                        focusedIndex = currentItems.IndexOf(parentItem);
                        subMenuOpen = false;
                        await parentItem.Close();
                        shouldOpenNextMenu = true;
                    }
                }

                focusedIndex = Math.Clamp(focusedIndex + (key == "ArrowLeft" ? -1 : 1), 0, currentItems.Count - 1);

                if (shouldOpenNextMenu)
                {
                    shouldOpenNextMenu = false;

                    var item = currentItems[focusedIndex];

                    if (item.items.Count > 0)
                    {
                        currentItems = item.items.Where(i => i.Visible && !i.Disabled).ToList();
                        focusedIndex = -1;
                        subMenuOpen = true;
                        await item.Toggle();
                    }
                }
            }
            else if (key == "Space" || key == "Enter")
            {
                preventKeyPress = true;
                stopKeydownPropagation = true;

                if (focusedIndex >= 0 && focusedIndex < currentItems.Count)
                {
                    var item = currentItems[focusedIndex];

                    if (item.items.Count > 0)
                    {
                        currentItems = item.items.Where(i => i.Visible && !i.Disabled).ToList();
                        focusedIndex = -1;
                        subMenuOpen = true;
                        await item.Toggle();
                    }
                    else
                    {
                        if (item.Path != null)
                        {
                            NavigationManager?.NavigateTo(item.Path);
                        }
                        else
                        {
                            await item.OnClick(new MouseEventArgs());
                        }
                    }
                }
            }
            else if (key == "Escape")
            {
                preventKeyPress = true;
                stopKeydownPropagation = true;

                if (currentItems.Any(i => i.ParentItem != null))
                {
                    var firstItem = currentItems.FirstOrDefault();
                    var parentItem = firstItem?.ParentItem;
                    if (parentItem != null)
                    {
                        currentItems = (parentItem.ParentItem != null ? parentItem.ParentItem.items : parentItem.Parent?.items ?? new List<RadzenMenuItem>()).Where(i => i.Visible && !i.Disabled).ToList();
                        focusedIndex = currentItems.IndexOf(parentItem);
                        subMenuOpen = false;
                        await parentItem.Close();
                    }
                }
            }
            else
            {
                preventKeyPress = false;
                stopKeydownPropagation = false;
            }
        }

        internal bool IsFocused(RadzenMenuItem item)
        {
            return focusedIndex != -1 && currentItems.IndexOf(item) == focusedIndex;
        }

        List<RadzenMenuItem> currentItems = new();

        internal List<RadzenMenuItem> items = new List<RadzenMenuItem>();

        /// <summary>
        /// Adds the item.
        /// </summary>
        /// <param name="item">The item.</param>
        public void AddItem(RadzenMenuItem item)
        {
            if (items.IndexOf(item) == -1)
            {
                items.Add(item);
                StateHasChanged();
            }
        }

        private string? toggleAriaLabel;

        /// <summary>
        /// Gets or sets the add button aria-label attribute.
        /// </summary>
        [Parameter]
        public string ToggleAriaLabel { get => toggleAriaLabel ?? Localize(nameof(RadzenStrings.Menu_ToggleAriaLabel)); set => toggleAriaLabel = value; }

        /// <inheritdoc />
        protected override void OnInitialized()
        {
            if (NavigationManager != null)
            {
                NavigationManager.LocationChanged += OnLocationChanged;
            }
        }

        private void OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
        {
            IsOpen = false;
            StateHasChanged();
        }

        /// <inheritdoc />
        public override void Dispose()
        {
            base.Dispose();
            if (NavigationManager != null)
            {
                NavigationManager.LocationChanged -= OnLocationChanged;
            }
            _jsRef?.InvokeVoidAsync("dispose");
            _jsRef?.DisposeAsync();
            GC.SuppressFinalize(this);
        }

        void OnFocus()
        {
            focusedIndex = focusedIndex == -1 ? 0 : focusedIndex;
        }
    }
}