namespace Inspectron.Epson.PrintServer.Printers.Utils.KitchenReceipt;

public class KitchenReceiptConverterKlinglers
{
    private readonly int _lineWidth;
    private readonly int _BigLineWidth;

    public KitchenReceiptConverterKlinglers(int lineWidth = 42, int bigLineWidth = 22)
    {
        _BigLineWidth = bigLineWidth;
        _lineWidth = lineWidth;
    }

    public List<PrintCommand> ConvertToPrintCommands(KitchenReceipt kitchenReceipt)
    {
        var commands = new List<PrintCommand>();

        // Header - Restaurant name in red
        commands.Add(new PrintCommand(Center(kitchenReceipt.Title, true)) { IsRed = true, IsBig = true, IsTall = true });
        commands.Add(new PrintCommand(CreateDashedLine()));
        commands.Add(new PrintCommand(""));

        // Transaction info
        string dateTimeLine = $"{kitchenReceipt.TransactionDateTime:dd-MMM-yy HH:mm} Nr.:{kitchenReceipt.ReceiptNumber}";
        commands.Add(new PrintCommand(Center(dateTimeLine, false)));

        string serverLine = $"{kitchenReceipt.WaiterName}";
        commands.Add(new PrintCommand(Center(serverLine, false)));

        //string serverIdLine = $"{kitchenReceipt.WaiterId}";
        //commands.Add(new PrintCommand(Center(serverIdLine, false)));

        string tableLine = $"Tisch: {kitchenReceipt.TableNumber}";
        commands.Add(new PrintCommand(Center(tableLine, true), true, true));

        if (!string.IsNullOrEmpty(kitchenReceipt.SpecialInstruction))
        {
            commands.Add(new PrintCommand(""));
            commands.Add(new PrintCommand(Center(kitchenReceipt.SpecialInstruction, true), true, true));
            commands.Add(new PrintCommand(""));
        }

        commands.Add(new PrintCommand(CreateDashedLine()));




        // Gangs

        foreach (Gang gang in kitchenReceipt.Gangs)
        {

            commands.Add(new PrintCommand(Center($"{gang.Id}. {gang.Name}", true)) { IsRed = true, IsBig = true, IsTall = true });
            PrintGroupedDishes(gang.Dishes, commands);

            if (gang != kitchenReceipt.Gangs.Last())
            {
                commands.Add(new PrintCommand("") { IsCut = true });
                commands.Add(new PrintCommand(""));
                commands.Add(new PrintCommand(""));
            }
        }

        if (kitchenReceipt.Gangs.Count > 0)
        {
            commands.Add(new PrintCommand(CreateDashedLine()));
        }

        PrintGroupedDishes(kitchenReceipt.Dishes, commands);

        if (kitchenReceipt.Dishes != null && kitchenReceipt.Dishes.Any())
        {
            commands.Add(new PrintCommand(""));
            commands.Add(new PrintCommand(CreateDashedLine()));
            commands.Add(new PrintCommand(""));
        }


        //// Additional info section
        //if (kitchenReceipt.AdditionalInfo != null && kitchenReceipt.AdditionalInfo.Count > 0)
        //{
        //    commands.Add(new PrintCommand(CreateAsterisksLine()));
        //    commands.Add(new PrintCommand("Info über zugehörige Artikel"));
        //    commands.Add(new PrintCommand(CreateAsterisksLine()));

        //    foreach (var info in kitchenReceipt.AdditionalInfo)
        //    {
        //        commands.Add(new PrintCommand(info));
        //    }

        //    commands.Add(new PrintCommand(""));
        //}

        return commands;
    }

    private static bool IsGroupable(Dish dish)
    {
        if (!string.IsNullOrEmpty(dish.Size)) return false;
        if (!string.IsNullOrEmpty(dish.Comment)) return false;
        if (dish.Modifications != null && (dish.Modifications.Removed.Any() || dish.Modifications.Added.Any())) return false;
        if (dish.Price != null) return false;
        return true;
    }

    private void PrintGroupedDishes(List<Dish> dishes, List<PrintCommand> commands)
    {
        var groupable = dishes.Where(IsGroupable).ToList();
        var individual = dishes.Where(d => !IsGroupable(d)).ToList();

        // Print grouped dishes
        var groups = groupable
            .GroupBy(d => d.Name)
            .ToList();

        foreach (var group in groups)
        {
            int totalQuantity = group.Sum(d => d.Number);
            var guestIds = group
                .Where(d => d.GuestId.HasValue)
                .SelectMany(d => Enumerable.Repeat(d.GuestId!.Value, d.Number))
                .OrderBy(id => id)
                .ToList();

            PrintDishLine($"{totalQuantity}x ", group.Key, null, null, commands);

            if (guestIds.Any())
            {
                string guestLine = $"({string.Join(",", guestIds)})";
                commands.Add(new PrintCommand(guestLine, isBold: true) { IsTall = true });
            }
        }

        // Print individual dishes (have size, modifications, or comment)
        foreach (var dish in individual)
        {
            PrintDishLine($"{dish.Number}x ", dish.Name, dish.Size, dish.Price, commands);

            if (dish.GuestId.HasValue)
            {
                string guestLine = $"({dish.GuestId.Value})";
                commands.Add(new PrintCommand(guestLine, isBold: true) { IsTall = true });
            }

            if (dish.Modifications != null && (dish.Modifications.Removed.Any() || dish.Modifications.Added.Any()))
            {
                foreach (var removed in dish.Modifications.Removed)
                {
                    commands.Add(new PrintCommand($" - {removed}", isBold: true) { IsTall = true });
                }
                foreach (var added in dish.Modifications.Added)
                {
                    commands.Add(new PrintCommand($" + {added}", isBold: true) { IsTall = true });
                }
            }

            if (!string.IsNullOrEmpty(dish.Comment))
            {
                commands.Add(new PrintCommand($"Comment: {dish.Comment}", isBold: true) { IsTall = true });
            }
        }
    }

    private void PrintDishLine(string quantityPrefix, string dishName, string? size, decimal? price, List<PrintCommand> commands)
    {
        string? priceStr = price != null ? price.Value.ToString("0.00") : null;

        string nameWithSize = dishName;
        if (!string.IsNullOrEmpty(size))
        {
            nameWithSize += $" ({size})";
        }

        int firstLineNameWidth = _lineWidth - quantityPrefix.Length - 6;
        if (priceStr != null)
        {
            firstLineNameWidth -= priceStr.Length + 1;
        }
        int continuationNameWidth = _lineWidth - quantityPrefix.Length - 6;

        // Simple case: name fits on first line
        if (nameWithSize.Length <= firstLineNameWidth || string.IsNullOrEmpty(dishName))
        {
            string line = quantityPrefix + nameWithSize;
            if (priceStr != null)
            {
                int padding = _lineWidth - line.Length - priceStr.Length;
                line += new string(' ', Math.Max(1, padding)) + priceStr;
            }
            commands.Add(new PrintCommand(line) { IsTall = true });
            return;
        }

        if (firstLineNameWidth <= 0)
        {
            string line = quantityPrefix + nameWithSize;
            if (priceStr != null) line += " " + priceStr;
            commands.Add(new PrintCommand(line) { IsTall = true });
            return;
        }

        // Word wrap
        var words = nameWithSize.Split(' ');
        var lines = new List<string>();
        string currentLine = "";
        int currentMaxWidth = firstLineNameWidth;

        foreach (var word in words)
        {
            if (currentLine.Length == 0)
            {
                currentLine = word;
            }
            else if (currentLine.Length + 1 + word.Length <= currentMaxWidth)
            {
                currentLine += " " + word;
            }
            else
            {
                lines.Add(currentLine);
                currentLine = word;
                currentMaxWidth = continuationNameWidth;
            }
        }

        if (currentLine.Length > 0)
        {
            lines.Add(currentLine);
        }

        // First line with prefix and optional right-aligned price
        string firstLine = quantityPrefix + lines[0];
        if (priceStr != null)
        {
            int padding = _lineWidth - firstLine.Length - priceStr.Length;
            firstLine += new string(' ', Math.Max(1, padding)) + priceStr;
        }
        commands.Add(new PrintCommand(firstLine) { IsTall = true });

        // Continuation lines
        string indent = new string(' ', quantityPrefix.Length);
        for (int i = 1; i < lines.Count; i++)
        {
            commands.Add(new PrintCommand(indent + lines[i]) { IsTall = true });
        }
    }

    private string Center(string text, bool isBigFont)
    {
        var actualLineWidth = isBigFont ? _BigLineWidth : _lineWidth;
        if (string.IsNullOrEmpty(text) || text.Length >= actualLineWidth)
            return text;

        int totalPadding = actualLineWidth - text.Length;
        int leftPadding = totalPadding / 2;

        return new string(' ', leftPadding) + text;
    }

    private string CreateDashedLine()
    {
        return new string('-', _lineWidth);
    }

    private string CreateAsterisksLine()
    {
        return new string('*', _lineWidth);
    }
}