package parser

import (
	"testing"

	"github.com/KorAP/Koral-Mapper/ast"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestGrammarParserSimpleTerm(t *testing.T) {
	tests := []struct {
		name           string
		input          string
		defaultFoundry string
		defaultLayer   string
		expected       *SimpleTerm
		expectError    bool
	}{
		{
			name:           "Foundry layer key value",
			input:          "[opennlp/p=PIDAT:new]",
			defaultFoundry: "opennlp",
			defaultLayer:   "p",
			expected: &SimpleTerm{
				WithFoundryLayer: &FoundryLayerTerm{
					Foundry: "opennlp",
					Layer:   "p",
					Key:     "PIDAT",
					Value:   "new",
				},
			},
		},
		{
			name:           "Foundry layer key",
			input:          "[opennlp/p=PIDAT]",
			defaultFoundry: "opennlp",
			defaultLayer:   "p",
			expected: &SimpleTerm{
				WithFoundryLayer: &FoundryLayerTerm{
					Foundry: "opennlp",
					Layer:   "p",
					Key:     "PIDAT",
				},
			},
		},
		{
			name:           "Layer key",
			input:          "[p=PIDAT]",
			defaultFoundry: "opennlp",
			defaultLayer:   "p",
			expected: &SimpleTerm{
				WithLayer: &LayerTerm{
					Layer: "p",
					Key:   "PIDAT",
				},
			},
		},
		{
			name:           "Simple key",
			input:          "[PIDAT]",
			defaultFoundry: "opennlp",
			defaultLayer:   "p",
			expected: &SimpleTerm{
				SimpleKey: &KeyTerm{
					Key: "PIDAT",
				},
			},
		},
		{
			name:           "Special symbol",
			input:          "[$\\(]",
			defaultFoundry: "opennlp",
			defaultLayer:   "p",
			expected: &SimpleTerm{
				SimpleKey: &KeyTerm{
					Key: "$(",
				},
			},
		},
		{
			name:           "Multiple escaped characters",
			input:          "[\\&\\|\\=]",
			defaultFoundry: "opennlp",
			defaultLayer:   "p",
			expected: &SimpleTerm{
				SimpleKey: &KeyTerm{
					Key: "&|=",
				},
			},
		},
		{
			name:           "Foundry wildcard key",
			input:          "[opennlp/*=PIDAT]",
			defaultFoundry: "opennlp",
			defaultLayer:   "p",
			expected: &SimpleTerm{
				WithFoundryWildcard: &FoundryWildcardTerm{
					Foundry: "opennlp",
					Key:     "PIDAT",
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			parser, err := NewGrammarParser(tt.defaultFoundry, tt.defaultLayer)
			require.NoError(t, err)

			grammar, err := parser.tokenParser.ParseString("", tt.input)
			if tt.expectError {
				assert.Error(t, err)
				return
			}
			require.NoError(t, err)
			require.NotNil(t, grammar.Token, "Expected token expression")

			// For testing purposes, unescape the key in the simple term
			if grammar.Token.Expr.First.Simple.SimpleKey != nil {
				grammar.Token.Expr.First.Simple.SimpleKey.Key = unescapeString(grammar.Token.Expr.First.Simple.SimpleKey.Key)
			}

			assert.Equal(t, tt.expected, grammar.Token.Expr.First.Simple)
		})
	}
}

// TestGrammarParser was removed as the Parse method is no longer supported
// The functionality is now only available through ParseMapping method

func TestMappingRules(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected *MappingResult
		wantErr  bool
	}{
		{
			name:  "Simple PIDAT mapping",
			input: "[PIDAT] <> [opennlp/p=PIDAT & opennlp/p=AdjType:Pdt]",
			expected: &MappingResult{
				Upper: &ast.Token{
					Wrap: &ast.Term{
						Key:   "PIDAT",
						Match: ast.MatchEqual,
					},
				},
				Lower: &ast.Token{
					Wrap: &ast.TermGroup{
						Relation: ast.AndRelation,
						Operands: []ast.Node{
							&ast.Term{
								Foundry: "opennlp",
								Layer:   "p",
								Key:     "PIDAT",
								Match:   ast.MatchEqual,
							},
							&ast.Term{
								Foundry: "opennlp",
								Layer:   "p",
								Key:     "AdjType",
								Value:   "Pdt",
								Match:   ast.MatchEqual,
							},
						},
					},
				},
			},
		},
		{
			name:  "PAV mapping",
			input: "[PAV] <> [ADV & PronType:Dem]",
			expected: &MappingResult{
				Upper: &ast.Token{
					Wrap: &ast.Term{
						Key:   "PAV",
						Match: ast.MatchEqual,
					},
				},
				Lower: &ast.Token{
					Wrap: &ast.TermGroup{
						Relation: ast.AndRelation,
						Operands: []ast.Node{
							&ast.Term{
								Key:   "ADV",
								Match: ast.MatchEqual,
							},
							&ast.Term{
								Key:   "PronType",
								Value: "Dem",
								Match: ast.MatchEqual,
							},
						},
					},
				},
			},
		},
		{
			name:  "PAV mapping with special character",
			input: "[$\\(] <> [ADV & PronType:Dem]",
			expected: &MappingResult{
				Upper: &ast.Token{
					Wrap: &ast.Term{
						Key:   "$(",
						Match: ast.MatchEqual,
					},
				},
				Lower: &ast.Token{
					Wrap: &ast.TermGroup{
						Relation: ast.AndRelation,
						Operands: []ast.Node{
							&ast.Term{
								Key:   "ADV",
								Match: ast.MatchEqual,
							},
							&ast.Term{
								Key:   "PronType",
								Value: "Dem",
								Match: ast.MatchEqual,
							},
						},
					},
				},
			},
		},
		// Additional tests to cover functionality from removed TestGrammarParser
		{
			name:  "Simple term with foundry and layer",
			input: "[opennlp/p=PIDAT] <> [PIDAT]",
			expected: &MappingResult{
				Upper: &ast.Token{
					Wrap: &ast.Term{
						Foundry: "opennlp",
						Layer:   "p",
						Key:     "PIDAT",
						Match:   ast.MatchEqual,
					},
				},
				Lower: &ast.Token{
					Wrap: &ast.Term{
						Key:   "PIDAT",
						Match: ast.MatchEqual,
					},
				},
			},
		},
		{
			name:  "Term group with and relation",
			input: "[opennlp/p=PIDAT & opennlp/p=AdjType:Pdt] <> [PIDAT]",
			expected: &MappingResult{
				Upper: &ast.Token{
					Wrap: &ast.TermGroup{
						Operands: []ast.Node{
							&ast.Term{
								Foundry: "opennlp",
								Layer:   "p",
								Key:     "PIDAT",
								Match:   ast.MatchEqual,
							},
							&ast.Term{
								Foundry: "opennlp",
								Layer:   "p",
								Key:     "AdjType",
								Value:   "Pdt",
								Match:   ast.MatchEqual,
							},
						},
						Relation: ast.AndRelation,
					},
				},
				Lower: &ast.Token{
					Wrap: &ast.Term{
						Key:   "PIDAT",
						Match: ast.MatchEqual,
					},
				},
			},
		},
		{
			name:  "Term group with or relation",
			input: "[opennlp/p=PronType:Ind | opennlp/p=PronType:Neg] <> [PRON]",
			expected: &MappingResult{
				Upper: &ast.Token{
					Wrap: &ast.TermGroup{
						Operands: []ast.Node{
							&ast.Term{
								Foundry: "opennlp",
								Layer:   "p",
								Key:     "PronType",
								Value:   "Ind",
								Match:   ast.MatchEqual,
							},
							&ast.Term{
								Foundry: "opennlp",
								Layer:   "p",
								Key:     "PronType",
								Value:   "Neg",
								Match:   ast.MatchEqual,
							},
						},
						Relation: ast.OrRelation,
					},
				},
				Lower: &ast.Token{
					Wrap: &ast.Term{
						Key:   "PRON",
						Match: ast.MatchEqual,
					},
				},
			},
		},
		{
			name:  "Complex term group with nested parentheses",
			input: "[opennlp/p=PIDAT & (opennlp/p=PronType:Ind | opennlp/p=PronType:Neg)] <> [COMPLEX]",
			expected: &MappingResult{
				Upper: &ast.Token{
					Wrap: &ast.TermGroup{
						Operands: []ast.Node{
							&ast.Term{
								Foundry: "opennlp",
								Layer:   "p",
								Key:     "PIDAT",
								Match:   ast.MatchEqual,
							},
							&ast.TermGroup{
								Operands: []ast.Node{
									&ast.Term{
										Foundry: "opennlp",
										Layer:   "p",
										Key:     "PronType",
										Value:   "Ind",
										Match:   ast.MatchEqual,
									},
									&ast.Term{
										Foundry: "opennlp",
										Layer:   "p",
										Key:     "PronType",
										Value:   "Neg",
										Match:   ast.MatchEqual,
									},
								},
								Relation: ast.OrRelation,
							},
						},
						Relation: ast.AndRelation,
					},
				},
				Lower: &ast.Token{
					Wrap: &ast.Term{
						Key:   "COMPLEX",
						Match: ast.MatchEqual,
					},
				},
			},
		},
		{
			name:  "Wildcard pattern",
			input: "[opennlp/*=PIDAT] <> [PIDAT]",
			expected: &MappingResult{
				Upper: &ast.Token{
					Wrap: &ast.Term{
						Foundry: "opennlp",
						Layer:   "",
						Key:     "PIDAT",
						Match:   ast.MatchEqual,
					},
				},
				Lower: &ast.Token{
					Wrap: &ast.Term{
						Key:   "PIDAT",
						Match: ast.MatchEqual,
					},
				},
			},
		},
		{
			name:    "Invalid mapping syntax",
			input:   "[PAV] -> [ADV]",
			wantErr: true,
		},
		{
			name:    "Missing closing bracket",
			input:   "[PAV <> [ADV]",
			wantErr: true,
		},
	}

	parser, err := NewGrammarParser("", "")
	assert.NoError(t, err)

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := parser.ParseMapping(tt.input)
			if tt.wantErr {
				assert.Error(t, err)
				return
			}
			assert.NoError(t, err, "Input: %s", tt.input)
			assert.Equal(t, tt.expected, result)
		})
	}
}
