package hooks

import "testing"

func TestClassifyBashCommand(t *testing.T) {
	tests := []struct {
		name       string
		cmd        string
		wantAction BashAction
		wantExtra  string // pattern for Grep/Find, path for ReadSource, "" otherwise
	}{
		// --- grep family, primary ---
		{"grep absolute path", `grep -n foo /repo/x.go`, BashActionGrepLike, "foo"},
		{"grep -rn with .", `grep -rn "handleFoo" .`, BashActionGrepLike, "handleFoo"},
		{"grep --include=", `grep -rn --include=*.go "handleFoo" .`, BashActionGrepLike, "handleFoo"},
		{"grep -e", `grep -e handleFoo -rn .`, BashActionGrepLike, "handleFoo"},
		{"rg bare", `rg handleFoo`, BashActionGrepLike, "handleFoo"},
		{"rg with path", `rg -n handleFoo src/`, BashActionGrepLike, "handleFoo"},
		{"egrep", `egrep -rn "Handler" .`, BashActionGrepLike, "Handler"},
		{"sudo grep", `sudo grep foo /etc/x`, BashActionGrepLike, "foo"},

		// --- grep after pipe: NOT primary ---
		{"go test | grep FAIL", `go test ./... | grep FAIL`, BashActionPassthrough, ""},
		{"echo | grep", `echo hi | grep hi`, BashActionPassthrough, ""},
		{"git log | grep", `git log --oneline | grep fix`, BashActionPassthrough, ""},

		// --- grep after ; && || : primary ---
		{"semicolon then grep", `cd /tmp; grep -rn foo .`, BashActionGrepLike, "foo"},
		{"&& grep", `cd /tmp && grep -rn foo .`, BashActionGrepLike, "foo"},
		{"|| grep", `false || grep foo x`, BashActionGrepLike, "foo"},

		// --- find -name ---
		{"find . -name Handler*", `find . -name "Handler*"`, BashActionFindName, "Handler"},
		{"find -iname prefix*", `find /repo -iname "Handler*"`, BashActionFindName, "Handler"},
		{"find -name *.go (stripped)", `find . -name "*.go"`, BashActionFindName, ".go"},
		{"find -type d (no name)", `find . -maxdepth 3 -type d`, BashActionPassthrough, ""},
		{"find without -name", `find . -newer foo`, BashActionPassthrough, ""},

		// --- cat / head / tail of source files ---
		{"cat .go", `cat /repo/x.go`, BashActionReadSource, "/repo/x.go"},
		{"head -20 .ts", `head -20 src/app.ts`, BashActionReadSource, "src/app.ts"},
		{"tail -n 50 .py", `tail -n 50 mod/foo.py`, BashActionReadSource, "mod/foo.py"},
		{"cat .log (not source)", `cat /tmp/app.log`, BashActionPassthrough, ""},
		{"cat .json", `cat package.json`, BashActionPassthrough, ""},
		{"cat .go | grep", `cat /repo/x.go | grep foo`, BashActionReadSource, "/repo/x.go"},

		// --- quoting ---
		{"single-quoted pattern", `grep -rn 'foo bar' .`, BashActionGrepLike, "foo bar"},
		{"double-quoted pattern", `grep -rn "foo bar" .`, BashActionGrepLike, "foo bar"},
		{"quoted separator inside", `grep -rn 'a;b' .`, BashActionGrepLike, "a;b"},

		// --- passthroughs ---
		{"empty", ``, BashActionPassthrough, ""},
		{"whitespace only", `   `, BashActionPassthrough, ""},
		{"ls", `ls /repo`, BashActionPassthrough, ""},
		{"go build", `go build ./...`, BashActionPassthrough, ""},
		{"echo", `echo hello`, BashActionPassthrough, ""},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := classifyBashCommand(tt.cmd)
			if got.Action != tt.wantAction {
				t.Fatalf("action = %v, want %v (got=%+v)", got.Action, tt.wantAction, got)
			}
			switch tt.wantAction {
			case BashActionGrepLike, BashActionFindName:
				if got.Pattern != tt.wantExtra {
					t.Errorf("pattern = %q, want %q", got.Pattern, tt.wantExtra)
				}
			case BashActionReadSource:
				if got.Path != tt.wantExtra {
					t.Errorf("path = %q, want %q", got.Path, tt.wantExtra)
				}
			}
		})
	}
}

func TestPrimarySegments(t *testing.T) {
	tests := []struct {
		name string
		cmd  string
		want []string
	}{
		{"simple", `grep foo bar`, []string{"grep foo bar"}},
		{"pipe drops tail", `grep foo bar | wc -l`, []string{"grep foo bar"}},
		{"semicolon keeps both", `a; b`, []string{"a", "b"}},
		{"&& keeps both", `a && b`, []string{"a", "b"}},
		{"|| keeps both", `a || b`, []string{"a", "b"}},
		{"pipe then ; resumes primary", `a | b; c`, []string{"a", "c"}},
		{"pipe then && resumes primary", `a | b && c`, []string{"a", "c"}},
		{"quoted pipe char", `grep 'a|b' .`, []string{"grep 'a|b' ."}},
		{"quoted semicolon", `grep 'a;b' .`, []string{"grep 'a;b' ."}},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := primarySegments(tt.cmd)
			if len(got) != len(tt.want) {
				t.Fatalf("got %d segments %v, want %d %v", len(got), got, len(tt.want), tt.want)
			}
			for i := range got {
				if got[i] != tt.want[i] {
					t.Errorf("segment %d: %q, want %q", i, got[i], tt.want[i])
				}
			}
		})
	}
}

func TestTokenize(t *testing.T) {
	tests := []struct {
		in   string
		want []string
	}{
		{`grep foo bar`, []string{"grep", "foo", "bar"}},
		{`grep "foo bar" .`, []string{"grep", "foo bar", "."}},
		{`grep 'foo bar' .`, []string{"grep", "foo bar", "."}},
		{`  multiple   spaces  `, []string{"multiple", "spaces"}},
		{`grep --include=*.go foo`, []string{"grep", "--include=*.go", "foo"}},
	}
	for _, tt := range tests {
		got := tokenize(tt.in)
		if len(got) != len(tt.want) {
			t.Fatalf("tokenize(%q) = %v, want %v", tt.in, got, tt.want)
		}
		for i := range got {
			if got[i] != tt.want[i] {
				t.Errorf("tokenize(%q)[%d] = %q, want %q", tt.in, i, got[i], tt.want[i])
			}
		}
	}
}
