package helpers

import (
	"testing"

	"github.com/jfraeys/fetch_ml/internal/api/helpers"
	"github.com/jfraeys/fetch_ml/internal/manifest"
	"github.com/jfraeys/fetch_ml/internal/queue"
)

func TestValidateCommitIDFormat(t *testing.T) {
	tests := []struct {
		name     string
		commitID string
		wantOk   bool
		wantErr  string
	}{
		{
			name:     "valid commit ID",
			commitID: "aabbccddeeff00112233445566778899aabbccdd",
			wantOk:   true,
			wantErr:  "",
		},
		{
			name:     "too short",
			commitID: "aabbcc",
			wantOk:   false,
			wantErr:  "invalid commit_id length",
		},
		{
			name:     "too long",
			commitID: "aabbccddeeff00112233445566778899aabbccddeeff",
			wantOk:   false,
			wantErr:  "invalid commit_id length",
		},
		{
			name:     "invalid hex",
			commitID: "gggggggggggggggggggggggggggggggggggggggg",
			wantOk:   false,
			wantErr:  "invalid commit_id hex",
		},
		{
			name:     "mixed case valid",
			commitID: "AABBCCDDEEFF00112233445566778899AABBCCDD",
			wantOk:   true,
			wantErr:  "",
		},
		{
			name:     "empty",
			commitID: "",
			wantOk:   false,
			wantErr:  "invalid commit_id length",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotOk, gotErr := helpers.ValidateCommitIDFormat(tt.commitID)
			if gotOk != tt.wantOk {
				t.Errorf("ValidateCommitIDFormat() ok = %v, want %v", gotOk, tt.wantOk)
			}
			if gotErr != tt.wantErr {
				t.Errorf("ValidateCommitIDFormat() err = %q, want %q", gotErr, tt.wantErr)
			}
		})
	}
}

func TestShouldRequireRunManifest(t *testing.T) {
	tests := []struct {
		name   string
		status string
		want   bool
	}{
		{"running", "running", true},
		{"completed", "completed", true},
		{"failed", "failed", true},
		{"queued", "queued", false},
		{"pending", "pending", false},
		{"cancelled", "cancelled", false},
		{"unknown", "unknown", false},
		{"empty", "", false},
		{"RUNNING uppercase", "RUNNING", true},
		{"Completed mixed", "Completed", true},
		{" Running with space", " running", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			task := &queue.Task{Status: tt.status}
			got := helpers.ShouldRequireRunManifest(task)
			if got != tt.want {
				t.Errorf("ShouldRequireRunManifest() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestExpectedRunManifestBucketForStatus(t *testing.T) {
	tests := []struct {
		name       string
		status     string
		wantBucket string
		wantOk     bool
	}{
		{"queued", "queued", "pending", true},
		{"pending", "pending", "pending", true},
		{"running", "running", "running", true},
		{"completed", "completed", "finished", true},
		{"finished", "finished", "finished", true},
		{"failed", "failed", "failed", true},
		{"unknown", "unknown", "", false},
		{"empty", "", "", false},
		{"RUNNING uppercase", "RUNNING", "running", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotBucket, gotOk := helpers.ExpectedRunManifestBucketForStatus(tt.status)
			if gotBucket != tt.wantBucket {
				t.Errorf("ExpectedRunManifestBucketForStatus() bucket = %q, want %q", gotBucket, tt.wantBucket)
			}
			if gotOk != tt.wantOk {
				t.Errorf("ExpectedRunManifestBucketForStatus() ok = %v, want %v", gotOk, tt.wantOk)
			}
		})
	}
}

func TestValidateTaskIDMatch(t *testing.T) {
	tests := []struct {
		name         string
		rmTaskID     string
		expectedID   string
		wantOk       bool
		wantExpected string
		wantActual   string
	}{
		{
			name:         "match",
			rmTaskID:     "task-123",
			expectedID:   "task-123",
			wantOk:       true,
			wantExpected: "task-123",
			wantActual:   "task-123",
		},
		{
			name:         "mismatch",
			rmTaskID:     "task-123",
			expectedID:   "task-456",
			wantOk:       false,
			wantExpected: "task-456",
			wantActual:   "task-123",
		},
		{
			name:         "empty rm task ID",
			rmTaskID:     "",
			expectedID:   "task-123",
			wantOk:       false,
			wantExpected: "task-123",
			wantActual:   "",
		},
		{
			name:         "whitespace trimmed - match",
			rmTaskID:     "task-123",
			expectedID:   "task-123",
			wantOk:       true,
			wantExpected: "task-123",
			wantActual:   "task-123",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rm := &manifest.RunManifest{TaskID: tt.rmTaskID}
			got := helpers.ValidateTaskIDMatch(rm, tt.expectedID)
			if got.OK != tt.wantOk {
				t.Errorf("ValidateTaskIDMatch() OK = %v, want %v", got.OK, tt.wantOk)
			}
			if got.Expected != tt.wantExpected {
				t.Errorf("ValidateTaskIDMatch() Expected = %q, want %q", got.Expected, tt.wantExpected)
			}
			if got.Actual != tt.wantActual {
				t.Errorf("ValidateTaskIDMatch() Actual = %q, want %q", got.Actual, tt.wantActual)
			}
		})
	}
}

func TestValidateCommitIDMatch(t *testing.T) {
	tests := []struct {
		name         string
		rmCommitID   string
		expectedID   string
		wantOk       bool
		wantExpected string
		wantActual   string
	}{
		{
			name:         "both empty",
			rmCommitID:   "",
			expectedID:   "",
			wantOk:       true,
			wantExpected: "",
			wantActual:   "",
		},
		{
			name:         "match",
			rmCommitID:   "abc123",
			expectedID:   "abc123",
			wantOk:       true,
			wantExpected: "abc123",
			wantActual:   "abc123",
		},
		{
			name:         "mismatch",
			rmCommitID:   "abc123",
			expectedID:   "def456",
			wantOk:       false,
			wantExpected: "def456",
			wantActual:   "abc123",
		},
		{
			name:         "expected empty",
			rmCommitID:   "abc123",
			expectedID:   "",
			wantOk:       true,
			wantExpected: "",
			wantActual:   "",
		},
		{
			name:         "rm empty",
			rmCommitID:   "",
			expectedID:   "abc123",
			wantOk:       true,
			wantExpected: "abc123",
			wantActual:   "",
		},
		{
			name:         "whitespace trimmed",
			rmCommitID:   "  abc123  ",
			expectedID:   "abc123",
			wantOk:       true,
			wantExpected: "abc123",
			wantActual:   "abc123",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := helpers.ValidateCommitIDMatch(tt.rmCommitID, tt.expectedID)
			if got.OK != tt.wantOk {
				t.Errorf("ValidateCommitIDMatch() OK = %v, want %v", got.OK, tt.wantOk)
			}
			if got.Expected != tt.wantExpected {
				t.Errorf("ValidateCommitIDMatch() Expected = %q, want %q", got.Expected, tt.wantExpected)
			}
			if got.Actual != tt.wantActual {
				t.Errorf("ValidateCommitIDMatch() Actual = %q, want %q", got.Actual, tt.wantActual)
			}
		})
	}
}

func TestValidateDepsProvenance(t *testing.T) {
	tests := []struct {
		name         string
		wantName     string
		wantSHA      string
		gotName      string
		gotSHA       string
		wantOk       bool
		wantExpected string
		wantActual   string
	}{
		{
			name:         "match",
			wantName:     "requirements.txt",
			wantSHA:      "abc123",
			gotName:      "requirements.txt",
			gotSHA:       "abc123",
			wantOk:       true,
			wantExpected: "requirements.txt:abc123",
			wantActual:   "requirements.txt:abc123",
		},
		{
			name:         "name mismatch",
			wantName:     "requirements.txt",
			wantSHA:      "abc123",
			gotName:      "Pipfile",
			gotSHA:       "abc123",
			wantOk:       false,
			wantExpected: "requirements.txt:abc123",
			wantActual:   "Pipfile:abc123",
		},
		{
			name:         "sha mismatch",
			wantName:     "requirements.txt",
			wantSHA:      "abc123",
			gotName:      "requirements.txt",
			gotSHA:       "def456",
			wantOk:       false,
			wantExpected: "requirements.txt:abc123",
			wantActual:   "requirements.txt:def456",
		},
		{
			name:         "want empty",
			wantName:     "",
			wantSHA:      "",
			gotName:      "requirements.txt",
			gotSHA:       "abc123",
			wantOk:       true,
			wantExpected: "",
			wantActual:   "",
		},
		{
			name:         "got empty",
			wantName:     "requirements.txt",
			wantSHA:      "abc123",
			gotName:      "",
			gotSHA:       "",
			wantOk:       true,
			wantExpected: "",
			wantActual:   "",
		},
		{
			name:         "both empty",
			wantName:     "",
			wantSHA:      "",
			gotName:      "",
			gotSHA:       "",
			wantOk:       true,
			wantExpected: "",
			wantActual:   "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := helpers.ValidateDepsProvenance(tt.wantName, tt.wantSHA, tt.gotName, tt.gotSHA)
			if got.OK != tt.wantOk {
				t.Errorf("ValidateDepsProvenance() OK = %v, want %v", got.OK, tt.wantOk)
			}
			if got.Expected != tt.wantExpected {
				t.Errorf("ValidateDepsProvenance() Expected = %q, want %q", got.Expected, tt.wantExpected)
			}
			if got.Actual != tt.wantActual {
				t.Errorf("ValidateDepsProvenance() Actual = %q, want %q", got.Actual, tt.wantActual)
			}
		})
	}
}

func TestValidateSnapshotID(t *testing.T) {
	tests := []struct {
		name         string
		wantID       string
		gotID        string
		wantOk       bool
		wantExpected string
		wantActual   string
	}{
		{
			name:         "match",
			wantID:       "snap-123",
			gotID:        "snap-123",
			wantOk:       true,
			wantExpected: "snap-123",
			wantActual:   "snap-123",
		},
		{
			name:         "mismatch",
			wantID:       "snap-123",
			gotID:        "snap-456",
			wantOk:       false,
			wantExpected: "snap-123",
			wantActual:   "snap-456",
		},
		{
			name:         "want empty",
			wantID:       "",
			gotID:        "snap-123",
			wantOk:       true,
			wantExpected: "",
			wantActual:   "snap-123",
		},
		{
			name:         "got empty",
			wantID:       "snap-123",
			gotID:        "",
			wantOk:       true,
			wantExpected: "snap-123",
			wantActual:   "",
		},
		{
			name:         "both empty",
			wantID:       "",
			gotID:        "",
			wantOk:       true,
			wantExpected: "",
			wantActual:   "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := helpers.ValidateSnapshotID(tt.wantID, tt.gotID)
			if got.OK != tt.wantOk {
				t.Errorf("ValidateSnapshotID() OK = %v, want %v", got.OK, tt.wantOk)
			}
			if got.Expected != tt.wantExpected {
				t.Errorf("ValidateSnapshotID() Expected = %q, want %q", got.Expected, tt.wantExpected)
			}
			if got.Actual != tt.wantActual {
				t.Errorf("ValidateSnapshotID() Actual = %q, want %q", got.Actual, tt.wantActual)
			}
		})
	}
}

func TestValidateSnapshotSHA(t *testing.T) {
	tests := []struct {
		name         string
		wantSHA      string
		gotSHA       string
		wantOk       bool
		wantExpected string
		wantActual   string
	}{
		{
			name:         "match",
			wantSHA:      "sha256:abc123",
			gotSHA:       "sha256:abc123",
			wantOk:       true,
			wantExpected: "sha256:abc123",
			wantActual:   "sha256:abc123",
		},
		{
			name:         "mismatch",
			wantSHA:      "sha256:abc123",
			gotSHA:       "sha256:def456",
			wantOk:       false,
			wantExpected: "sha256:abc123",
			wantActual:   "sha256:def456",
		},
		{
			name:         "want empty",
			wantSHA:      "",
			gotSHA:       "sha256:abc123",
			wantOk:       true,
			wantExpected: "",
			wantActual:   "sha256:abc123",
		},
		{
			name:         "got empty",
			wantSHA:      "sha256:abc123",
			gotSHA:       "",
			wantOk:       true,
			wantExpected: "sha256:abc123",
			wantActual:   "",
		},
		{
			name:         "both empty",
			wantSHA:      "",
			gotSHA:       "",
			wantOk:       true,
			wantExpected: "",
			wantActual:   "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := helpers.ValidateSnapshotSHA(tt.wantSHA, tt.gotSHA)
			if got.OK != tt.wantOk {
				t.Errorf("ValidateSnapshotSHA() OK = %v, want %v", got.OK, tt.wantOk)
			}
			if got.Expected != tt.wantExpected {
				t.Errorf("ValidateSnapshotSHA() Expected = %q, want %q", got.Expected, tt.wantExpected)
			}
			if got.Actual != tt.wantActual {
				t.Errorf("ValidateSnapshotSHA() Actual = %q, want %q", got.Actual, tt.wantActual)
			}
		})
	}
}
