package core

import (
	"errors"
	"testing"
)

func TestParseCompareSpec(t *testing.T) {
	tests := []struct {
		name     string
		raw      string
		wantBase string
		wantHead string
		want3Dot bool
		wantErr  bool
	}{
		// Happy paths.
		{name: "three-dot", raw: "main...feature", wantBase: "main", wantHead: "feature", want3Dot: true},
		{name: "two-dot", raw: "main..feature", wantBase: "main", wantHead: "feature", want3Dot: false},
		{name: "slash branch head", raw: "main...feature/foo", wantBase: "main", wantHead: "feature/foo", want3Dot: true},
		{name: "slash branch both", raw: "feature/with-slash...release/v1", wantBase: "feature/with-slash", wantHead: "release/v1", want3Dot: true},
		{name: "two-dot slash", raw: "feature/a..feature/b", wantBase: "feature/a", wantHead: "feature/b", want3Dot: false},
		{name: "sha40 vs branch", raw: "0123456789abcdef0123456789abcdef01234567...main",
			wantBase: "0123456789abcdef0123456789abcdef01234567", wantHead: "main", want3Dot: true},
		{name: "sha256 head", raw: "main.." +
			"0000000000000000000000000000000000000000000000000000000000000000",
			wantBase: "main", wantHead: "0000000000000000000000000000000000000000000000000000000000000000"},
		{name: "percent-escaped slash", raw: "feature%2Ffoo...main", wantBase: "feature/foo", wantHead: "main", want3Dot: true},
		{name: "tag with dot", raw: "v1.0.0...v1.1.0", wantBase: "v1.0.0", wantHead: "v1.1.0", want3Dot: true},

		// Separator handling.
		{name: "no separator", raw: "mainfeature", wantErr: true},
		{name: "empty", raw: "", wantErr: true},
		{name: "empty base", raw: "...main", wantErr: true},
		{name: "empty head", raw: "main...", wantErr: true},
		{name: "both empty two-dot", raw: "..", wantErr: true},
		{name: "four dots splits on three then leading dot", raw: "a....b", wantErr: true},

		// Hostile input — must be rejected via ValidRef.
		{name: "option injection head", raw: "main...--upload-pack=x", wantErr: true},
		{name: "option injection base", raw: "--output=/etc/passwd...main", wantErr: true},
		{name: "revision at-brace", raw: "main...@{u}", wantErr: true},
		{name: "trailing .lock", raw: "main...foo.lock", wantErr: true},
		{name: "leading dot head", raw: "main....hidden", wantErr: true},
		{name: "caret ref", raw: "main...HEAD^", wantErr: true},
		{name: "tilde ref", raw: "main...HEAD~3", wantErr: true},
		{name: "colon ref", raw: "main...refs:x", wantErr: true},
		{name: "space in ref", raw: "main...my branch", wantErr: true},
		{name: "control char", raw: "main...he\x00ad", wantErr: true},
		{name: "escaped dotdot becomes traversal", raw: "main...%2E%2E", wantErr: true},
		{name: "escaped traversal path", raw: "main...%2e%2e%2Fetc", wantErr: true},
		{name: "bad percent-encoding", raw: "main...%zz", wantErr: true},
		{name: "double slash", raw: "main...a//b", wantErr: true},
		{name: "at shorthand", raw: "main...@", wantErr: true},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			got, err := ParseCompareSpec(tc.raw)
			if tc.wantErr {
				if err == nil {
					t.Fatalf("ParseCompareSpec(%q) = %+v, want error", tc.raw, got)
				}
				if !errors.Is(err, ErrBadRef) {
					t.Fatalf("ParseCompareSpec(%q) error = %v, want wrapping ErrBadRef", tc.raw, err)
				}
				return
			}
			if err != nil {
				t.Fatalf("ParseCompareSpec(%q) unexpected error: %v", tc.raw, err)
			}
			if got.Base != tc.wantBase || got.Head != tc.wantHead || got.ThreeDot != tc.want3Dot {
				t.Fatalf("ParseCompareSpec(%q) = {Base:%q Head:%q ThreeDot:%v}, want {Base:%q Head:%q ThreeDot:%v}",
					tc.raw, got.Base, got.Head, got.ThreeDot, tc.wantBase, tc.wantHead, tc.want3Dot)
			}
		})
	}
}
