using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Elements;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;

namespace QuestPDF.Examples
{
    public class OrderItem
    {
        public string ItemName { get; set; } = Placeholders.Label();
        public int Price { get; set; } = Placeholders.Random.Next(1, 11) * 10;
        public int Count { get; set; } = Placeholders.Random.Next(1, 11);
    }

    public struct OrdersTableState
    {
        public int ShownItemsCount { get; set; }
    }
    
    public class OptimizedOrdersTable : IDynamicComponent<OrdersTableState>
    {
        private ICollection<OrderItem> Items { get; }
        public OrdersTableState State { get; set; }

        public OptimizedOrdersTable(ICollection<OrderItem> items)
        {
            Items = items;

            State = new OrdersTableState
            {
                ShownItemsCount = 0
            };
        }
        
        public DynamicComponentComposeResult Compose(DynamicContext context)
        {
            var header = ComposeHeader(context);
            var sampleFooter = ComposeFooter(context, Enumerable.Empty<OrderItem>());
            var decorationHeight = header.Size.Height + sampleFooter.Size.Height;
            
            var rows = GetItemsForPage(context, decorationHeight).ToList();
            var footer = ComposeFooter(context, rows.Select(x => x.Item));

            var content = context.CreateElement(container =>
            {
                container.MinimalBox().Decoration(decoration =>
                {
                    decoration.Before().Element(header);

                    decoration.Content().Column(column =>
                    {
                        foreach (var row in rows)
                            column.Item().Element(row.Element);
                    });

                    decoration.After().Element(footer);
                });
            });

            State = new OrdersTableState
            {
                ShownItemsCount = State.ShownItemsCount + rows.Count
            };

            return new DynamicComponentComposeResult
            {
                Content = content,
                HasMoreContent = State.ShownItemsCount < Items.Count
            };
        }

        private IDynamicElement ComposeHeader(DynamicContext context)
        {
            return context.CreateElement(element =>
            {
                element
                    .Width(context.AvailableSize.Width)
                    .BorderBottom(1)
                    .BorderColor(Colors.Grey.Darken2)
                    .Padding(5)
                    .DefaultTextStyle(TextStyle.Default.SemiBold())
                    .Row(row =>
                    {
                        row.ConstantItem(30).Text("#");
                        row.RelativeItem().Text("Item name");
                        row.ConstantItem(50).AlignRight().Text("Count");
                        row.ConstantItem(50).AlignRight().Text("Price");
                        row.ConstantItem(50).AlignRight().Text("Total");
                    });
            });
        }
        
        private IDynamicElement ComposeFooter(DynamicContext context, IEnumerable<OrderItem> items)
        {
            var total = items.Sum(x => x.Count * x.Price);

            return context.CreateElement(element =>
            {
                element
                    .Width(context.AvailableSize.Width)
                    .Padding(5)
                    .AlignRight()
                    .DefaultTextStyle(TextStyle.Default.FontSize(14).SemiBold())
                    .Text($"Subtotal: {total}$");
            });
        }
        
        private IEnumerable<(OrderItem Item, IDynamicElement Element)> GetItemsForPage(DynamicContext context, float decorationHeight)
        {
            var totalHeight = decorationHeight;

            foreach (var index in Enumerable.Range(State.ShownItemsCount, Items.Count - State.ShownItemsCount))
            {
                var item = Items.ElementAt(index);
                
                var element = context.CreateElement(content =>
                {
                    content
                        .Width(context.AvailableSize.Width)
                        .BorderBottom(1)
                        .BorderColor(Colors.Grey.Lighten2)
                        .Padding(5)
                        .Row(row =>
                        {
                            row.ConstantItem(30).Text((index + 1).ToString(CultureInfo.InvariantCulture));
                            row.RelativeItem().Text(item.ItemName);
                            row.ConstantItem(50).AlignRight().Text(item.Count.ToString(CultureInfo.InvariantCulture));
                            row.ConstantItem(50).AlignRight().Text($"{item.Price}$");
                            row.ConstantItem(50).AlignRight().Text($"{item.Count*item.Price}$");
                        });
                });

                var elementHeight = element.Size.Height;
                    
                if (totalHeight + elementHeight > context.AvailableSize.Height)
                    break;
                    
                totalHeight += elementHeight;
                yield return (item, element);
            }
        }
    }
    
    public static class DynamicOptimizedExamples
    {
        [Test]
        public static void Dynamic()
        {
            RenderingTest
                .Create()
                .PageSize(PageSizes.A5)
                .ShowResults()
                .Render(container =>
                {
                    var items = Enumerable.Range(0, 25).Select(x => new OrderItem()).ToList();
                    
                    container
                        .Background(Colors.White)
                        .Padding(25)
                        .Decoration(decoration =>
                        {
                            decoration
                                .Before()
                                .PaddingBottom(5)
                                .Text(text =>
                                {
                                    text.DefaultTextStyle(TextStyle.Default.SemiBold().FontColor(Colors.Blue.Darken2).FontSize(16));
                                    text.Span("Page ");
                                    text.CurrentPageNumber();
                                    text.Span(" of ");
                                    text.TotalPages();
                                });
                            
                            decoration
                                .Content()
                                .Dynamic(new OptimizedOrdersTable(items));
                        });
                });
        }
    }
}