package utils

import (
	"reflect"
	"testing"
)

func TestParseParams(t *testing.T) {
	tests := []struct {
		name    string
		input   string
		want    []string
		wantErr bool
	}{
		{
			name:  "simple params",
			input: "BYTE x 10",
			want:  []string{"BYTE", "x", "10"},
		},
		{
			name:  "with quoted string",
			input: `PRINT "hello world"`,
			want:  []string{"PRINT", `"hello world"`},
		},
		{
			name:  "multiple spaces",
			input: "WORD    y    20",
			want:  []string{"WORD", "y", "20"},
		},
		{
			name:  "empty string",
			input: "",
			want:  []string{},
		},
		{
			name:  "only spaces",
			input: "   ",
			want:  []string{},
		},
		{
			name:    "unterminated string",
			input:   `PRINT "hello`,
			wantErr: true,
		},
		{
			name:  "string with spaces inside",
			input: `CALL print ( "hello world" )`,
			want:  []string{"CALL", "print", "(", `"hello world"`, ")"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := ParseParams(tt.input)
			if (err != nil) != tt.wantErr {
				t.Errorf("ParseParams() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !tt.wantErr && !reflect.DeepEqual(got, tt.want) {
				t.Errorf("ParseParams() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestNormalizeSpaces(t *testing.T) {
	tests := []struct {
		name  string
		input string
		want  string
	}{
		{
			name:  "multiple spaces",
			input: "BYTE    x    10",
			want:  "BYTE x 10",
		},
		{
			name:  "preserves string spaces",
			input: `PRINT "hello   world"`,
			want:  `PRINT "hello   world"`,
		},
		{
			name:  "leading trailing spaces",
			input: "  BYTE x  ",
			want:  "BYTE x",
		},
		{
			name:  "tabs",
			input: "BYTE\t\tx",
			want:  "BYTE x",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := NormalizeSpaces(tt.input)
			if got != tt.want {
				t.Errorf("NormalizeSpaces() = %q, want %q", got, tt.want)
			}
		})
	}
}

func TestIsStringLiteral(t *testing.T) {
	tests := []struct {
		input string
		want  bool
	}{
		{`"hello"`, true},
		{`"hello world"`, true},
		{`""`, true},
		{`"`, false},
		{`hello`, false},
		{`'hello'`, false},
		{`hello"`, false},
	}

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			got := IsStringLiteral(tt.input)
			if got != tt.want {
				t.Errorf("IsStringLiteral(%q) = %v, want %v", tt.input, got, tt.want)
			}
		})
	}
}

func TestStripQuotes(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{`"hello"`, "hello"},
		{`"hello world"`, "hello world"},
		{`""`, ""},
		{`hello`, "hello"},
		{`"`, `"`},
	}

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			got := StripQuotes(tt.input)
			if got != tt.want {
				t.Errorf("StripQuotes(%q) = %q, want %q", tt.input, got, tt.want)
			}
		})
	}
}

func TestValidateIdentifier(t *testing.T) {
	tests := []struct {
		input string
		want  bool
	}{
		{"x", true},
		{"_temp", true},
		{"myVar123", true},
		{"_", true},
		{"var_name", true},
		{"123abc", false},
		{"", false},
		{"my-var", false},
		{"my var", false},
		{"my.var", false},
	}

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			got := ValidateIdentifier(tt.input)
			if got != tt.want {
				t.Errorf("ValidateIdentifier(%q) = %v, want %v", tt.input, got, tt.want)
			}
		})
	}
}

func TestEvaluateExpression(t *testing.T) {
	// Simple constant lookup for testing
	constLookup := func(name string) (int64, bool) {
		constants := map[string]int64{
			"MAXVAL": 255,
			"BASE":   100,
			"OFFSET": 10,
		}
		val, ok := constants[name]
		return val, ok
	}

	tests := []struct {
		name    string
		expr    string
		lookup  ConstantLookup
		want    int64
		wantErr bool
	}{
		// Decimal
		{name: "simple decimal", expr: "42", want: 42},
		{name: "decimal zero", expr: "0", want: 0},

		// Hex
		{name: "hex lowercase", expr: "$ff", want: 255},
		{name: "hex uppercase", expr: "$FF", want: 255},
		{name: "hex zero", expr: "$00", want: 0},
		{name: "hex word", expr: "$C000", want: 0xC000},

		// Binary
		{name: "binary 8bit", expr: "%11111111", want: 255},
		{name: "binary zero", expr: "%00000000", want: 0},
		{name: "binary mixed", expr: "%10101010", want: 0xAA},

		// Addition
		{name: "add decimal", expr: "10+20", want: 30},
		{name: "add hex", expr: "$10+$20", want: 0x30},
		{name: "add binary", expr: "%1010+%0101", want: 15},
		{name: "add multiple", expr: "10+20+30", want: 60},

		// Subtraction
		{name: "subtract", expr: "100-25", want: 75},
		{name: "subtract chain", expr: "100-20-10", want: 70},

		// Multiplication
		{name: "multiply", expr: "10*5", want: 50},
		{name: "multiply chain", expr: "2*3*4", want: 24},

		// Division
		{name: "divide", expr: "100/5", want: 20},
		{name: "divide chain", expr: "100/5/2", want: 10},

		// Binary OR
		{name: "binary or", expr: "$F0|$0F", want: 0xFF},
		{name: "binary or multiple", expr: "1|2|4", want: 7},

		// Binary AND
		{name: "binary and", expr: "$FF&$0F", want: 0x0F},
		{name: "binary and multiple", expr: "255&127&63", want: 63},

		// Mixed operators
		{name: "mixed math", expr: "10+20*2", want: 60}, // left-to-right: (10+20)*2
		{name: "mixed bitwise", expr: "255&$F0|$0F", want: 0xFF},

		// Constants
		{name: "constant", expr: "MAXVAL", lookup: constLookup, want: 255},
		{name: "constant add", expr: "BASE+OFFSET", lookup: constLookup, want: 110},
		{name: "constant unknown", expr: "UNKNOWN", lookup: constLookup, wantErr: true},
		{name: "constant no lookup", expr: "MAXVAL", wantErr: true},

		// Spaces
		{name: "spaces around ops", expr: "10 + 20", want: 30},
		{name: "spaces everywhere", expr: "  100  -  20  ", want: 80},

		// Errors
		{name: "empty", expr: "", wantErr: true},
		{name: "invalid hex", expr: "$ZZ", wantErr: true},
		{name: "invalid binary", expr: "!22", wantErr: true},
		{name: "division by zero", expr: "10/0", wantErr: true},
		{name: "trailing operator", expr: "10+", wantErr: true},
		{name: "leading operator", expr: "+10", wantErr: true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := EvaluateExpression(tt.expr, tt.lookup)
			if (err != nil) != tt.wantErr {
				t.Errorf("EvaluateExpression() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !tt.wantErr && got != tt.want {
				t.Errorf("EvaluateExpression(%q) = %v, want %v", tt.expr, got, tt.want)
			}
		})
	}
}
