package core

import (
	"testing"
)

func TestLexer(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected []Token
	}{
		{
			name:  "keywords",
			input: "MATCH CREATE WHERE SET DELETE RETURN IN AS COUNT SUM AND NOT",
			expected: []Token{
				{Type: MATCH, Literal: "MATCH"},
				{Type: CREATE, Literal: "CREATE"},
				{Type: WHERE, Literal: "WHERE"},
				{Type: SET, Literal: "SET"},
				{Type: DELETE, Literal: "DELETE"},
				{Type: RETURN, Literal: "RETURN"},
				{Type: IN, Literal: "IN"},
				{Type: AS, Literal: "AS"},
				{Type: COUNT, Literal: "COUNT"},
				{Type: SUM, Literal: "SUM"},
				{Type: AND, Literal: "AND"},
				{Type: NOT, Literal: "NOT"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "identifiers and literals",
			input: `pod nginx "hello world" 42 true false null`,
			expected: []Token{
				{Type: IDENT, Literal: "pod"},
				{Type: IDENT, Literal: "nginx"},
				{Type: STRING, Literal: `"hello world"`},
				{Type: NUMBER, Literal: "42"},
				{Type: BOOLEAN, Literal: "true"},
				{Type: BOOLEAN, Literal: "false"},
				{Type: NULL, Literal: "null"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "delimiters",
			input: "( ) { } [ ] : , .",
			expected: []Token{
				{Type: LPAREN, Literal: "("},
				{Type: RPAREN, Literal: ")"},
				{Type: LBRACE, Literal: "{"},
				{Type: RBRACE, Literal: "}"},
				{Type: LBRACKET, Literal: "["},
				{Type: RBRACKET, Literal: "]"},
				{Type: COLON, Literal: ":"},
				{Type: COMMA, Literal: ","},
				{Type: DOT, Literal: "."},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "operators",
			input: "= != > < >= <= =~ CONTAINS",
			expected: []Token{
				{Type: EQUALS, Literal: "="},
				{Type: NOT_EQUALS, Literal: "!="},
				{Type: GREATER_THAN, Literal: ">"},
				{Type: LESS_THAN, Literal: "<"},
				{Type: GREATER_THAN_EQUALS, Literal: ">="},
				{Type: LESS_THAN_EQUALS, Literal: "<="},
				{Type: REGEX_COMPARE, Literal: "=~"},
				{Type: CONTAINS, Literal: "CONTAINS"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "relationship tokens",
			input: "-> <- -- -[r:uses]-> <-[r:uses]- -[r:uses]-",
			expected: []Token{
				{Type: REL_NOPROPS_RIGHT, Literal: "->"},
				{Type: REL_NOPROPS_LEFT, Literal: "<-"},
				{Type: REL_NOPROPS_NONE, Literal: "--"},
				{Type: REL_BEGINPROPS_NONE, Literal: "-["},
				{Type: IDENT, Literal: "r"},
				{Type: COLON, Literal: ":"},
				{Type: IDENT, Literal: "uses"},
				{Type: REL_ENDPROPS_RIGHT, Literal: "]->"},
				{Type: REL_BEGINPROPS_LEFT, Literal: "<-["},
				{Type: IDENT, Literal: "r"},
				{Type: COLON, Literal: ":"},
				{Type: IDENT, Literal: "uses"},
				{Type: REL_ENDPROPS_NONE, Literal: "]-"},
				{Type: REL_BEGINPROPS_NONE, Literal: "-["},
				{Type: IDENT, Literal: "r"},
				{Type: COLON, Literal: ":"},
				{Type: IDENT, Literal: "uses"},
				{Type: REL_ENDPROPS_NONE, Literal: "]-"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "complex json",
			input: `{"metadata": {"name": "test", "labels": {"app": "nginx"}}}`,
			expected: []Token{
				{Type: LBRACE, Literal: "{"},
				{Type: STRING, Literal: `"metadata"`},
				{Type: COLON, Literal: ":"},
				{Type: LBRACE, Literal: "{"},
				{Type: STRING, Literal: `"name"`},
				{Type: COLON, Literal: ":"},
				{Type: STRING, Literal: `"test"`},
				{Type: COMMA, Literal: ","},
				{Type: STRING, Literal: `"labels"`},
				{Type: COLON, Literal: ":"},
				{Type: LBRACE, Literal: "{"},
				{Type: STRING, Literal: `"app"`},
				{Type: COLON, Literal: ":"},
				{Type: STRING, Literal: `"nginx"`},
				{Type: RBRACE, Literal: "}"},
				{Type: RBRACE, Literal: "}"},
				{Type: RBRACE, Literal: "}"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "invalid tokens",
			input: "@ # $ %",
			expected: []Token{
				{Type: ILLEGAL, Literal: "@"},
				{Type: ILLEGAL, Literal: "#"},
				{Type: ILLEGAL, Literal: "$"},
				{Type: ILLEGAL, Literal: "%"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "invalid relationship",
			input: "<<",
			expected: []Token{
				{Type: ILLEGAL, Literal: "<<"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "fully qualified resource kinds",
			input: "(d:deployments.apps), (p:pods.v1.core), (c:configmaps.v1)",
			expected: []Token{
				{Type: LPAREN, Literal: "("},
				{Type: IDENT, Literal: "d"},
				{Type: COLON, Literal: ":"},
				{Type: IDENT, Literal: "deployments.apps"},
				{Type: RPAREN, Literal: ")"},
				{Type: COMMA, Literal: ","},
				{Type: LPAREN, Literal: "("},
				{Type: IDENT, Literal: "p"},
				{Type: COLON, Literal: ":"},
				{Type: IDENT, Literal: "pods.v1.core"},
				{Type: RPAREN, Literal: ")"},
				{Type: COMMA, Literal: ","},
				{Type: LPAREN, Literal: "("},
				{Type: IDENT, Literal: "c"},
				{Type: COLON, Literal: ":"},
				{Type: IDENT, Literal: "configmaps.v1"},
				{Type: RPAREN, Literal: ")"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "array wildcards",
			input: "containers[*].image, volumes[0].name, mounts[*].path[*]",
			expected: []Token{
				{Type: IDENT, Literal: "containers"},
				{Type: LBRACKET, Literal: "["},
				{Type: ILLEGAL, Literal: "*"},
				{Type: RBRACKET, Literal: "]"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "image"},
				{Type: COMMA, Literal: ","},
				{Type: IDENT, Literal: "volumes"},
				{Type: LBRACKET, Literal: "["},
				{Type: NUMBER, Literal: "0"},
				{Type: RBRACKET, Literal: "]"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "name"},
				{Type: COMMA, Literal: ","},
				{Type: IDENT, Literal: "mounts"},
				{Type: LBRACKET, Literal: "["},
				{Type: ILLEGAL, Literal: "*"},
				{Type: RBRACKET, Literal: "]"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "path"},
				{Type: LBRACKET, Literal: "["},
				{Type: ILLEGAL, Literal: "*"},
				{Type: RBRACKET, Literal: "]"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "partial nodes",
			input: "() (p) (:pod)",
			expected: []Token{
				{Type: LPAREN, Literal: "("},
				{Type: RPAREN, Literal: ")"},
				{Type: LPAREN, Literal: "("},
				{Type: IDENT, Literal: "p"},
				{Type: RPAREN, Literal: ")"},
				{Type: LPAREN, Literal: "("},
				{Type: COLON, Literal: ":"},
				{Type: IDENT, Literal: "pod"},
				{Type: RPAREN, Literal: ")"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "partial nodes in relationships",
			input: "(p:pod)->()->(:service)",
			expected: []Token{
				{Type: LPAREN, Literal: "("},
				{Type: IDENT, Literal: "p"},
				{Type: COLON, Literal: ":"},
				{Type: IDENT, Literal: "pod"},
				{Type: RPAREN, Literal: ")"},
				{Type: REL_NOPROPS_RIGHT, Literal: "->"},
				{Type: LPAREN, Literal: "("},
				{Type: RPAREN, Literal: ")"},
				{Type: REL_NOPROPS_RIGHT, Literal: "->"},
				{Type: LPAREN, Literal: "("},
				{Type: COLON, Literal: ":"},
				{Type: IDENT, Literal: "service"},
				{Type: RPAREN, Literal: ")"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "where clause with AND",
			input: `WHERE pod.status.phase != "Running" AND pod.metadata.name = "test"`,
			expected: []Token{
				{Type: WHERE, Literal: "WHERE"},
				{Type: IDENT, Literal: "pod"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "status"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "phase"},
				{Type: NOT_EQUALS, Literal: "!="},
				{Type: STRING, Literal: `"Running"`},
				{Type: AND, Literal: "AND"},
				{Type: IDENT, Literal: "pod"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "metadata"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "name"},
				{Type: EQUALS, Literal: "="},
				{Type: STRING, Literal: `"test"`},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "where clause with NOT",
			input: `WHERE NOT pod.status.phase = "Running" AND NOT pod.metadata.name = "test"`,
			expected: []Token{
				{Type: WHERE, Literal: "WHERE"},
				{Type: NOT, Literal: "NOT"},
				{Type: IDENT, Literal: "pod"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "status"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "phase"},
				{Type: EQUALS, Literal: "="},
				{Type: STRING, Literal: `"Running"`},
				{Type: AND, Literal: "AND"},
				{Type: NOT, Literal: "NOT"},
				{Type: IDENT, Literal: "pod"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "metadata"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "name"},
				{Type: EQUALS, Literal: "="},
				{Type: STRING, Literal: `"test"`},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "escaped dots in json paths",
			input: `d.metadata.annotations.meta\.helm\.sh/release-name`,
			expected: []Token{
				{Type: IDENT, Literal: "d"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "metadata"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "annotations"},
				{Type: DOT, Literal: "."},
				{Type: IDENT, Literal: "meta\\.helm\\.sh/release-name"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "datetime and duration functions",
			input: `datetime() duration("PT1H") datetime() - duration("P1DT2H")`,
			expected: []Token{
				{Type: DATETIME, Literal: "datetime"},
				{Type: LPAREN, Literal: "("},
				{Type: RPAREN, Literal: ")"},
				{Type: DURATION, Literal: "duration"},
				{Type: LPAREN, Literal: "("},
				{Type: STRING, Literal: `"PT1H"`},
				{Type: RPAREN, Literal: ")"},
				{Type: DATETIME, Literal: "datetime"},
				{Type: LPAREN, Literal: "("},
				{Type: RPAREN, Literal: ")"},
				{Type: MINUS, Literal: "-"},
				{Type: DURATION, Literal: "duration"},
				{Type: LPAREN, Literal: "("},
				{Type: STRING, Literal: `"P1DT2H"`},
				{Type: RPAREN, Literal: ")"},
				{Type: EOF, Literal: ""},
			},
		},
		{
			name:  "temporal comparisons",
			input: `startTime < datetime() startTime > datetime() - duration("PT1H")`,
			expected: []Token{
				{Type: IDENT, Literal: "startTime"},
				{Type: LESS_THAN, Literal: "<"},
				{Type: DATETIME, Literal: "datetime"},
				{Type: LPAREN, Literal: "("},
				{Type: RPAREN, Literal: ")"},
				{Type: IDENT, Literal: "startTime"},
				{Type: GREATER_THAN, Literal: ">"},
				{Type: DATETIME, Literal: "datetime"},
				{Type: LPAREN, Literal: "("},
				{Type: RPAREN, Literal: ")"},
				{Type: MINUS, Literal: "-"},
				{Type: DURATION, Literal: "duration"},
				{Type: LPAREN, Literal: "("},
				{Type: STRING, Literal: `"PT1H"`},
				{Type: RPAREN, Literal: ")"},
				{Type: EOF, Literal: ""},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			lexer := NewLexer(tt.input)
			for i, expected := range tt.expected {
				got := lexer.NextToken()
				if got.Type != expected.Type || got.Literal != expected.Literal {
					t.Errorf("token[%d] - got=%+v, want=%+v", i, got, expected)
				}
			}
		})
	}
}
