package terminal

import (
	"fmt"
	"testing"
)

func TestBasicValid(t *testing.T) {
	line := ParseLine("toaster --long_arg test -t a", 0)

	if line.Command == nil {
		t.Fatal("Line command is nil")
	}

	if line.Command.Value() != "toaster" {
		t.Fatalf("Expected 'toaster' as command, got %s", line.Command.Value())
	}

	if len(line.Arguments) != 2 {
		t.Fatalf("Parsed line should have 2 arguments, has %d", len(line.Arguments))
	}

	c, err := line.GetArgString("long_arg")
	if err != nil {
		t.Fatalf("Did not expect to get an error here: %s", err)
	}

	if c != "test" {
		t.Fatalf("Expected --long_arg to have value 'test', has %s", c)
	}

	c, err = line.GetArgString("t")
	if err != nil {
		t.Fatalf("Did not expect to get an error here: %s", err)
	}

	if c != "a" {
		t.Fatalf("Expected -t to have value 'a', has %s", c)
	}

	if line.Focus.Type() != line.Command.Type() && line.Focus.Value() == line.Command.Value() {
		t.Fatalf("Cursor is set to focus on command, however focus variable is not set to command: focus: %v", line.Focus)
	}

}

func TestNoFlags(t *testing.T) {
	line := ParseLine("kill 127.0.0.1:49962", 0)

	fmt.Println(line)

	if len(line.Arguments) != 1 {
		t.Fatalf("Expected one argument, got %d", len(line.Arguments))
	}

	if line.Arguments[0].Value() != "127.0.0.1:49962" {
		t.Fatalf("Expected argument to be '127.0.0.1:49962', got %s", line.Arguments[0].Value())
	}
}

func TestNoCommand(t *testing.T) {
	line := ParseLine("--long_arg test -t a", 0)

	if line.Command != nil {
		t.Fatalf("If line has no command, command should be nil, is instead focused on %+v", line.Command)
	}
}

func TestMixedArgs(t *testing.T) {
	line := ParseLine("toaster --long_arg 1 2 3 4 -t a -t abcd --noot", 0)

	c, err := line.GetArgs("long_arg")
	if err != nil {
		t.Fatalf("Did not expect to get an error here: %s", err)
	}

	if len(c) != 4 {
		t.Fatalf("long_args should have 4 args: has: %d args", len(c))
	}
	fmt.Println(line)

	if !line.IsSet("noot") {
		t.Fatalf("Expect to noot to be set")
	}

	c, err = line.GetArgs("t")
	if err != nil {
		t.Fatalf("Did not expect to get an error here: %s", err)
	}

	if len(c) != 2 {

		t.Fatalf("-t should have two arguments (concat) has %d", len(c))

	}

}

func TestHelperFunctionsErrors(t *testing.T) {
	line := ParseLine("lala --long_arg test -t a --zero -m", 0)

	_, err := line.GetArgString("long_arg")
	if err != nil {
		t.Fatalf("Getting single string arg on 'long_arg' should work: %s", err)
	}

	_, err = line.GetArgString("t")
	if err != nil {
		t.Fatalf("Getting single string arg on 't' should work: %s", err)
	}

	_, err = line.GetArgString("zero")
	if err == nil {
		t.Fatalf("Expected error, 'zero' has no arg, yet we are still nil")
	}

	_, err = line.GetArgString("m")
	if err == nil {
		t.Fatalf("Expected error, 'm' has no arg, yet we are still nil")
	}

	e, err := line.ExpectArgs("long_arg", 1)
	if err != nil {
		t.Fatalf("Long arg supplies correct number of expected args, should not error: %s", err)
	}

	if len(e) != 1 || e[0].Value() != "test" {
		t.Fatal("Output args was not correct from expect")
	}

	_, err = line.ExpectArgs("long_arg", 2)
	if err == nil {
		t.Fatal("long_args has < 2 arguments, should fail", err)
	}

	_, err = line.GetArg("bleep")
	if err != ErrFlagNotSet {
		t.Fatal("Error should be ErrFlagNotSet")
	}

}

func TestStrings(t *testing.T) {
	line := ParseLine("lala --long_arg \"test ''``-t a\" --zero -m", 0)

	_, err := line.GetArgString("long_arg")
	if err != nil {
		t.Fatalf("Getting single string arg on 'long_arg' should work: %s", err)
	}

	_, err = line.GetArgString("zero")
	if err == nil {
		t.Fatalf("Expected error, 'zero' has no arg, yet we are still nil")
	}

	_, err = line.GetArgString("m")
	if err == nil {
		t.Fatalf("Expected error, 'm' has no arg, yet we are still nil")
	}

	e, err := line.ExpectArgs("long_arg", 1)
	if err != nil {
		t.Fatalf("Long arg supplies correct number of expected args, should not error: %s", err)
	}

	if len(e) != 1 || e[0].Value() != "test ''``-t a" {
		t.Fatal("String was not handled correct as the value is incorrect")
	}

	if line.Chunks[0] != "lala" || line.Chunks[0] != line.Command.Value() {
		t.Fatal("First chunk should be command")
	}

	if line.Chunks[1] != "--long_arg" {
		t.Fatal("Next chunk should be flag")
	}

	if line.Chunks[2] != "test ''``-t a" {
		t.Fatal("Next chunk should be argument string")
	}
}

func TestQuoteHandling(t *testing.T) {
	tests := []struct {
		input    string
		expected []string
	}{
		// Single quotes
		{`echo 'hello world'`, []string{"echo", "hello world"}},
		{`echo 'don\'t escape'`, []string{"echo", "don\\'t escape"}},
		{`echo 'preserve "double" quotes'`, []string{"echo", `preserve "double" quotes`}},
		
		// Double quotes
		{`echo "hello world"`, []string{"echo", "hello world"}},
		{`echo "escape \"quotes\""`, []string{"echo", `escape "quotes"`}},
		{`echo "allow 'single' quotes"`, []string{"echo", `allow 'single' quotes`}},
		
		// Escaping
		{`echo hello\ world`, []string{"echo", "hello world"}},
		{`echo "hello\ world"`, []string{"echo", "hello\\ world"}},
		{`echo \$PATH`, []string{"echo", "\\$PATH"}},
		{`echo "\"quoted\""`, []string{"echo", `"quoted"`}},
		
		// Mixed quotes
		{`echo "mixed 'quotes'"`, []string{"echo", `mixed 'quotes'`}},
		{`echo 'mixed "quotes"'`, []string{"echo", `mixed "quotes"`}},
		{`echo "escaped \' single quote"`, []string{"echo", `escaped ' single quote`}},
	}

	for i, test := range tests {
		line := ParseLine(test.input, 0)
		
		// Convert parsed line to string slice for comparison
		var got []string
		if line.Command != nil {
			got = append(got, line.Command.Value())
		}
		got = append(got, line.ArgumentsAsStrings()...)

		if len(got) != len(test.expected) {
			t.Errorf("Test %d (%q):\n got %d parts %q\n want %d parts %q",
				i, test.input, len(got), got, len(test.expected), test.expected)
			continue
		}

		for j := range got {
			if got[j] != test.expected[j] {
				t.Errorf("Test %d (%q) part %d:\n got %q\n want %q",
					i, test.input, j, got[j], test.expected[j])
			}
		}
	}
}