using Inspectron.Epson.Templates.Language;

namespace Inspectron.Epson.Templates.Tests.Language;

public class LexerTests
{
    [Fact]
    public void Tokenize_PlainText_ReturnsTextToken()
    {
        var lexer = new Lexer("Hello World");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "Hello World");
    }

    [Fact]
    public void Tokenize_Binding_ReturnsBindingToken()
    {
        var lexer = new Lexer("{PropertyName}");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.Binding && t.Value == "PropertyName");
    }

    [Fact]
    public void Tokenize_BindingWithFormat_ReturnsBindingTokenWithFormat()
    {
        var lexer = new Lexer("{TransactionDateTime:dd-MMM-yy}");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.Binding && t.Value == "TransactionDateTime:dd-MMM-yy");
    }

    [Fact]
    public void Tokenize_StyleBlock_ReturnsStyleTokens()
    {
        var lexer = new Lexer("#bold,center# text #");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.StyleStart && t.Value == "bold,center");
        Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == " text ");
        Assert.Contains(result.Tokens, t => t.Type == TokenType.StyleEnd);
    }

    [Fact]
    public void Tokenize_DashSeparator_ReturnsSeparatorToken()
    {
        var lexer = new Lexer("---");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.DashSeparator);
    }

    [Fact]
    public void Tokenize_EqualsSeparator_ReturnsSeparatorToken()
    {
        var lexer = new Lexer("===");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.EqualsSeparator);
    }

    [Fact]
    public void Tokenize_StarSeparator_ReturnsSeparatorToken()
    {
        var lexer = new Lexer("***");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.StarSeparator);
    }

    [Fact]
    public void Tokenize_IfDirective_ReturnsIfToken()
    {
        var lexer = new Lexer("@if condition");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.If && t.Value == "condition");
    }

    [Fact]
    public void Tokenize_ForeachDirective_ReturnsForeachToken()
    {
        var lexer = new Lexer("@foreach item in items");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.Foreach && t.Value == "item in items");
    }

    [Fact]
    public void Tokenize_EndDirective_ReturnsEndToken()
    {
        var lexer = new Lexer("@end");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.End);
    }

    [Fact]
    public void Tokenize_RowDirectives_ReturnsRowTokens()
    {
        var lexer = new Lexer("@row\n@endrow");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.Row);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.EndRow);
    }

    [Fact]
    public void Tokenize_Column_ReturnsColumnToken()
    {
        var lexer = new Lexer("|20|");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.Column && t.Value.StartsWith("20"));
    }

    [Fact]
    public void Tokenize_ColumnWithAlignment_ReturnsColumnTokenWithAlignment()
    {
        var lexer = new Lexer("|20,right|");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.Column && t.Value == "20,right");
    }

    [Fact]
    public void Tokenize_MultipleLines_PreservesLineStructure()
    {
        var lexer = new Lexer("Line 1\nLine 2\nLine 3");
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        var newlines = result.Tokens.Count(t => t.Type == TokenType.NewLine);
        Assert.Equal(3, newlines);
    }

    [Fact]
    public void Tokenize_ComplexTemplate_ParsesCorrectly()
    {
        var template = @"#bold,center# {Title} #
---
@if HasItems
@foreach item in Items
{item.Name}
@end
@end";
        var lexer = new Lexer(template);
        var result = lexer.Tokenize();

        Assert.False(result.HasErrors);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.StyleStart);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.DashSeparator);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.If);
        Assert.Contains(result.Tokens, t => t.Type == TokenType.Foreach);
        Assert.Equal(2, result.Tokens.Count(t => t.Type == TokenType.End));
    }
}
