using Inspectron.Epson.Templates.Configuration;
using Inspectron.Epson.Templates.Interpreter;
using Inspectron.Epson.Templates.Language;
using Inspectron.Epson.Templates.Language.Nodes;
using Inspectron.Epson.Templates.Tests.Helpers;

namespace Inspectron.Epson.Templates.Tests.Integration;

public class KitchenReceiptTests
{
    private readonly PrinterProfile _profile = new("tm-t30iii", "TM-T30III", 48, 24, false);

    private const string KitchenReceiptJson = """
    {
        "Title": "Restaurant Test",
        "TransactionDateTime": "2024-03-15T14:30:00",
        "ReceiptNumber": "12345",
        "WaiterName": "John Doe",
        "WaiterId": "W001",
        "TableNumber": "5",
        "SpecialInstruction": "Rush Order",
        "Gangs": [
            {
                "Id": 1,
                "Name": "Starters",
                "Dishes": [
                    {
                        "Number": 2,
                        "Name": "Caesar Salad",
                        "Modifications": {
                            "Removed": ["Croutons"],
                            "Added": ["Extra Dressing"]
                        },
                        "Comment": "No anchovies"
                    }
                ]
            }
        ],
        "Dishes": [
            {
                "Number": 1,
                "Name": "Grilled Salmon",
                "Modifications": {
                    "Removed": [],
                    "Added": []
                },
                "Comment": null
            }
        ]
    }
    """;

    private const string KitchenTemplate = """
    #red,big,tall,center# {Title} #
    ---

    #center# {TransactionDateTime:dd-MMM-yy HH:mm} Nr.:{ReceiptNumber} #
    #center# {WaiterName} #
    #center# {WaiterId} #
    #big,bold,center# Tisch: {TableNumber} #

    @if SpecialInstruction

    #big,bold,center# {SpecialInstruction} #

    @end
    ---
    @foreach gang in Gangs
    #red,big,tall,center# {gang.Id}. {gang.Name} #
    @foreach dish in gang.Dishes
    #tall# {dish.Number}x {dish.Name} #
    @if dish.Modifications.Removed.count > 0
    @foreach removed in dish.Modifications.Removed
    #bold,tall#   - {removed} #
    @end
    @end
    @if dish.Modifications.Added.count > 0
    @foreach added in dish.Modifications.Added
    #bold,tall#   + {added} #
    @end
    @end
    @if dish.Comment
    #bold,tall#   Comment: {dish.Comment} #
    @end
    @end
    @end
    @if Gangs.count > 0
    ---
    @end
    @foreach dish in Dishes
    #tall# {dish.Number}x {dish.Name} #
    @end
    """;

    private TemplateNode Parse(string source)
    {
        var lexer = new Lexer(source);
        var parser = new Parser(lexer.Tokenize().Tokens);
        return parser.Parse().Template;
    }

    [Fact]
    public void KitchenReceipt_RendersFully()
    {
        var template = Parse(KitchenTemplate);
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, KitchenReceiptJson);

        // Verify key elements are present
        Assert.Contains(commands, c => c.Text.Contains("Restaurant Test"));
        Assert.Contains(commands, c => c.Text.Contains("John Doe"));
        Assert.Contains(commands, c => c.Text.Contains("Tisch: 5"));
        Assert.Contains(commands, c => c.Text.Contains("Rush Order"));
        Assert.Contains(commands, c => c.Text.Contains("1. Starters"));
        Assert.Contains(commands, c => c.Text.Contains("2x Caesar Salad"));
        Assert.Contains(commands, c => c.Text.Contains("- Croutons"));
        Assert.Contains(commands, c => c.Text.Contains("+ Extra Dressing"));
        Assert.Contains(commands, c => c.Text.Contains("Comment: No anchovies"));
        Assert.Contains(commands, c => c.Text.Contains("1x Grilled Salmon"));
    }

    [Fact]
    public void KitchenReceipt_AppliesStyles()
    {
        var template = Parse(KitchenTemplate);
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, KitchenReceiptJson);

        // Title should be big and tall
        var titleCmd = commands.FirstOrDefault(c => c.Text.Contains("Restaurant Test"));
        Assert.NotNull(titleCmd);
        Assert.True(titleCmd.IsBig);
        Assert.True(titleCmd.IsTall);

        // Table should be bold and big
        var tableCmd = commands.FirstOrDefault(c => c.Text.Contains("Tisch: 5"));
        Assert.NotNull(tableCmd);
        Assert.True(tableCmd.IsBold);
        Assert.True(tableCmd.IsBig);

        // Modifications should be bold and tall
        var modCmd = commands.FirstOrDefault(c => c.Text.Contains("- Croutons"));
        Assert.NotNull(modCmd);
        Assert.True(modCmd.IsBold);
        Assert.True(modCmd.IsTall);
    }

    [Fact]
    public void KitchenReceipt_GeneratesSeparators()
    {
        var template = Parse(KitchenTemplate);
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, KitchenReceiptJson);

        var separatorCount = commands.Count(c => c.Text.All(ch => ch == '-') && c.Text.Length == 48);
        Assert.True(separatorCount >= 2, $"Expected at least 2 separators, found {separatorCount}");
    }

    [Fact]
    public void KitchenReceipt_FormatsDateTime()
    {
        var template = Parse(KitchenTemplate);
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, KitchenReceiptJson);

        Assert.Contains(commands, c => c.Text.Contains("15-Mar-24") || c.Text.Contains("15-Mär-24"));
    }

    [Fact]
    public void KitchenReceipt_WithoutSpecialInstruction_OmitsSection()
    {
        var jsonWithoutInstruction = """
        {
            "Title": "Test",
            "TransactionDateTime": "2024-03-15T14:30:00",
            "ReceiptNumber": "123",
            "WaiterName": "John",
            "WaiterId": "W001",
            "TableNumber": "1",
            "SpecialInstruction": null,
            "Gangs": [],
            "Dishes": []
        }
        """;

        var template = Parse(KitchenTemplate);
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, jsonWithoutInstruction);

        // Should not have any content that looks like special instruction
        Assert.DoesNotContain(commands, c => c.Text.Contains("Rush"));
    }
}
