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

namespace Inspectron.Epson.Templates.Tests.Interpreter;

public class InterpreterRowTests
{
    private readonly PrinterProfile _profile = new("test", "Test Printer", 48, 24, false);

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

    [Fact]
    public void Interpret_Row_CombinesColumns()
    {
        var template = Parse("@row\n|20|Name|10|Value\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        // Should have a single command with combined column text
        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Name") && c.Text.Contains("Value"));
        Assert.NotNull(rowCmd);
    }

    [Fact]
    public void Interpret_Row_LeftAlignsPaddsRight()
    {
        var template = Parse("@row\n|10|Hi\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Hi"));
        Assert.NotNull(rowCmd);
        Assert.Equal(10, rowCmd.Text.Length);
        Assert.StartsWith("Hi", rowCmd.Text);
    }

    [Fact]
    public void Interpret_Row_RightAlignsPaddsLeft()
    {
        var template = Parse("@row\n|10,right|Hi\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Hi"));
        Assert.NotNull(rowCmd);
        Assert.Equal(10, rowCmd.Text.Length);
        Assert.EndsWith("Hi", rowCmd.Text);
    }

    [Fact]
    public void Interpret_Row_CenterAlignsCentersText()
    {
        var template = Parse("@row\n|10,center|Hi\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Hi"));
        Assert.NotNull(rowCmd);
        Assert.Equal(10, rowCmd.Text.Length);
        Assert.StartsWith("    Hi", rowCmd.Text);
    }

    [Fact]
    public void Interpret_Row_TruncatesLongText()
    {
        var template = Parse("@row\n|5|HelloWorld\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Hello"));
        Assert.NotNull(rowCmd);
        Assert.Equal(5, rowCmd.Text.Length);
        Assert.Equal("Hello", rowCmd.Text);
    }

    [Fact]
    public void Interpret_Row_WithBindings_ResolvesValues()
    {
        var template = Parse("@row\n|20|{Name}|10,right|{Price:F2}\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, """{"Name": "Item", "Price": 9.99}""");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Item") && c.Text.Contains("9.99"));
        Assert.NotNull(rowCmd);
    }

    [Fact]
    public void Interpret_Row_MultipleColumns_CorrectTotalWidth()
    {
        var template = Parse("@row\n|10|A|10|B|10|C\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("A") && c.Text.Contains("B") && c.Text.Contains("C"));
        Assert.NotNull(rowCmd);
        Assert.Equal(30, rowCmd.Text.Length);
    }

    [Fact]
    public void Interpret_Row_WithBoldStyle_SetsBold()
    {
        var template = Parse("@row bold\n|20|Name|10,right|Price\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Name") && c.Text.Contains("Price"));
        Assert.NotNull(rowCmd);
        Assert.True(rowCmd.IsBold);
    }

    [Fact]
    public void Interpret_Row_WithBigStyle_SetsBig()
    {
        var template = Parse("@row big\n|12|Name|6,right|Price\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Name") && c.Text.Contains("Price"));
        Assert.NotNull(rowCmd);
        Assert.True(rowCmd.IsBig);
    }

    [Fact]
    public void Interpret_Row_WithTallStyle_SetsTall()
    {
        var template = Parse("@row tall\n|20|Name|10,right|Price\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Name") && c.Text.Contains("Price"));
        Assert.NotNull(rowCmd);
        Assert.True(rowCmd.IsTall);
    }

    [Fact]
    public void Interpret_Row_WithMultipleStyles_AppliesAll()
    {
        var template = Parse("@row bold, big\n|12|Name|6,right|Price\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Name") && c.Text.Contains("Price"));
        Assert.NotNull(rowCmd);
        Assert.True(rowCmd.IsBold);
        Assert.True(rowCmd.IsBig);
    }

    [Fact]
    public void Interpret_Row_WithRedStyle_OnSupportedPrinter_SetsRed()
    {
        var redProfile = new PrinterProfile("test", "Test Printer", 48, 24, SupportsRed: true);
        var template = Parse("@row red\n|20|Warning|10,right|!\n@endrow");
        var interpreter = new TemplateInterpreter(redProfile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Warning"));
        Assert.NotNull(rowCmd);
        Assert.True(rowCmd.IsRed);
    }

    [Fact]
    public void Interpret_Row_WithRedStyle_OnUnsupportedPrinter_DoesNotSetRed()
    {
        var template = Parse("@row red\n|20|Warning|10,right|!\n@endrow");
        var interpreter = new TemplateInterpreter(_profile); // _profile has supportsRed: false
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Warning"));
        Assert.NotNull(rowCmd);
        Assert.False(rowCmd.IsRed);
    }

    [Fact]
    public void Interpret_Row_WithoutStyles_NoStylesApplied()
    {
        var template = Parse("@row\n|20|Name|10,right|Price\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Name") && c.Text.Contains("Price"));
        Assert.NotNull(rowCmd);
        Assert.False(rowCmd.IsBold);
        Assert.False(rowCmd.IsBig);
        Assert.False(rowCmd.IsTall);
        Assert.False(rowCmd.IsRed);
    }

    [Fact]
    public void Interpret_Row_WithSpacingStyle_SetsLineSpacing()
    {
        var template = Parse("@row spacing:50\n|20|Name|10,right|Price\n@endrow");
        var interpreter = new TemplateInterpreter(_profile);
        var commands = interpreter.Interpret(template, "{}");

        var rowCmd = commands.FirstOrDefault(c => c.Text.Contains("Name") && c.Text.Contains("Price"));
        Assert.NotNull(rowCmd);
        Assert.Equal(50, rowCmd.SetLineSpacing);
    }
}
