using Microsoft.AspNetCore.Components;
using System.Linq;
using System.Threading.Tasks;

namespace Radzen.Blazor
{
    /// <summary>
    /// A data list component for displaying collections of items using custom templates, with support for paging and virtualization.
    /// RadzenDataList provides a flexible way to render data in cards, tiles, or custom layouts instead of traditional table rows.
    /// Ideal when you need more control over item presentation than a traditional table provides. Perfect for product catalogs, image galleries, card-based dashboards, or any non-tabular data display.
    /// Supports complete control over item rendering via Template parameter, built-in paging with configurable page size, item wrapping to multiple columns/rows based on container width,
    /// efficient rendering for large datasets via virtualization, customizable message or template for empty state when no data exists, and on-demand data loading for server-side paging via LoadData.
    /// Use Template to define how each item should be rendered. The template receives the item as @context. Combine with RadzenRow/RadzenColumn for grid-based layouts or RadzenCard for card designs.
    /// </summary>
    /// <typeparam name="TItem">The type of data items in the list. Each item is rendered using the Template.</typeparam>
    /// <example>
    /// Basic data list with card template:
    /// <code>
    /// &lt;RadzenDataList Data=@products TItem="Product" AllowPaging="true" PageSize="12"&gt;
    ///     &lt;Template Context="product"&gt;
    ///         &lt;RadzenCard Style="width: 250px; margin: 1rem;"&gt;
    ///             &lt;RadzenImage Path=@product.ImageUrl Style="width: 100%;" /&gt;
    ///             &lt;RadzenText TextStyle="TextStyle.H6"&gt;@product.Name&lt;/RadzenText&gt;
    ///             &lt;RadzenText&gt;@product.Price.ToString("C")&lt;/RadzenText&gt;
    ///         &lt;/RadzenCard&gt;
    ///     &lt;/Template&gt;
    /// &lt;/RadzenDataList&gt;
    /// </code>
    /// Data list with empty state:
    /// <code>
    /// &lt;RadzenDataList Data=@items TItem="Item" WrapItems="true" ShowEmptyMessage="true" EmptyText="No items found"&gt;
    ///     &lt;Template Context="item"&gt;
    ///         &lt;div class="item-card"&gt;@item.Name&lt;/div&gt;
    ///     &lt;/Template&gt;
    /// &lt;/RadzenDataList&gt;
    /// </code>
    /// </example>
    [CascadingTypeParameter(nameof(TItem))]
    public partial class RadzenDataList<
        [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields)] TItem> : PagedDataBoundComponent<TItem>
    {
        /// <inheritdoc />
        protected override string GetComponentCssClass()
        {
            return "rz-datalist-content";
        }

        /// <summary>
        /// Gets or sets whether to display an empty message when the data source has no items.
        /// Enable this to show EmptyText or EmptyTemplate when the list is empty, providing user feedback.
        /// </summary>
        /// <value><c>true</c> to show empty message; <c>false</c> to show nothing. Default is <c>false</c>.</value>
        [Parameter]
        public bool ShowEmptyMessage { get; set; }

        private string? emptyText;
        /// <summary>
        /// Gets or sets the text message displayed when the data source is empty.
        /// Only shown if <see cref="ShowEmptyMessage"/> is true and no <see cref="EmptyTemplate"/> is specified.
        /// </summary>
        /// <value>The empty state message text. Default is "No records to display."</value>
        [Parameter]
        public string EmptyText
        {
            get { return emptyText ?? Localize(nameof(RadzenStrings.DataList_EmptyText)); }
            set
            {
                if (value != emptyText)
                {
                    emptyText = value;
                }
            }
        }

        /// <summary>
        /// Gets or sets a custom template for rendering the empty state when the data source has no items.
        /// Takes precedence over <see cref="EmptyText"/> when both are set.
        /// Use this for rich empty states with images, icons, or action buttons.
        /// </summary>
        /// <value>The empty state template render fragment.</value>
        [Parameter]
        public RenderFragment? EmptyTemplate { get; set; }

        /// <summary>
        /// Gets or sets whether items should wrap to multiple rows based on their width and the container size.
        /// When true, items flow horizontally and wrap like words in a paragraph. When false, items stack vertically.
        /// </summary>
        /// <value><c>true</c> to enable wrapping (horizontal flow); <c>false</c> for vertical stacking. Default is <c>false</c>.</value>
        [Parameter]
        public bool WrapItems { get; set; }

        /// <summary>
        /// Gets or sets whether the DataList uses virtualization to improve performance with large datasets.
        /// When enabled, only visible items are rendered in the DOM, significantly improving performance for long lists.
        /// </summary>
        /// <value><c>true</c> to enable virtualization; <c>false</c> for standard rendering. Default is <c>false</c>.</value>
        [Parameter]
        public bool AllowVirtualization { get; set; }

        internal Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>? virtualize;

        /// <summary>
        /// Gets Virtualize component reference.
        /// </summary>
        public Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>? Virtualize
        {
            get
            {
                return virtualize;
            }
        }

        private async ValueTask<Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<TItem>> LoadItems(Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderRequest request)
        {
            var view = AllowPaging ? PagedView : View;
            var top = request.Count;

            if(top <= 0)
            {
                top = PageSize;
            }

            await LoadData.InvokeAsync(new Radzen.LoadDataArgs()
            {
                Skip = request.StartIndex,
                Top = top
            });
            
            var totalItemsCount = LoadData.HasDelegate ? Count : view.Count();

            var virtualDataItems = (LoadData.HasDelegate ? Data : view.Skip(request.StartIndex).Take(top))?.ToList();

            return new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult<TItem>(virtualDataItems ?? Enumerable.Empty<TItem>(), totalItemsCount);
        }
        RenderFragment DrawDataListRows()
        {
            return new RenderFragment(builder =>
            {
                if (AllowVirtualization)
                {
                    builder.OpenComponent(0, typeof(Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>));
                    builder.AddAttribute(1, "ItemsProvider", new Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderDelegate<TItem>(LoadItems));
                    
                    builder.AddAttribute(2, "ChildContent", (RenderFragment<TItem>)((context) =>
                    {
                        return (RenderFragment)((b) =>
                        {
                            DrawRow(b, context);
                        });
                    }));

                    builder.AddComponentReferenceCapture(4, c => { virtualize = (Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>)c; });

                    builder.CloseComponent();
                }
                else
                {
                    DrawRows(builder);
                }
            });
        }

        /// <summary>
        /// Reloads this instance.
        /// </summary>
        public async override Task Reload()
        {
            await base.Reload();

            if (virtualize != null)
            {
                await virtualize.RefreshDataAsync();
            }
        }

        internal void DrawRows(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder)
        {
            var items = LoadData.HasDelegate ? Data : PagedView;
            if (items != null)
            {
                foreach (var item in items)
                {
                    DrawRow(builder, item);
                }
            }
        }

        internal void DrawRow(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder, TItem item)
        {
            builder.OpenComponent<RadzenDataListRow<TItem>>(0);
            builder.AddAttribute(1, "DataList", this);
            builder.AddAttribute(2, "Item", item);
            builder.SetKey(item);
            builder.CloseComponent();
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance loading indicator is shown.
        /// </summary>
        /// <value><c>true</c> if this instance loading indicator is shown; otherwise, <c>false</c>.</value>
        [Parameter]
        public bool IsLoading { get; set; }
    }
}
