package core

import "testing"

func TestOperationTypeValid(t *testing.T) {
	valid := []OperationType{
		OperationClone,
		OperationFetch,
		OperationCommit,
		OperationPush,
		OperationAgentRun,
	}
	for _, typ := range valid {
		if !typ.Valid() {
			t.Fatalf("operation type %q should be valid", typ)
		}
	}
	if OperationType("unknown").Valid() {
		t.Fatal("unknown operation type should be invalid")
	}
}

func TestOperationStateValidTerminalAndTransitions(t *testing.T) {
	tests := []struct {
		name     string
		from     OperationState
		to       OperationState
		allowed  bool
		terminal bool
	}{
		{name: "queued starts running", from: OperationQueued, to: OperationRunning, allowed: true},
		{name: "queued cancels", from: OperationQueued, to: OperationCancelled, allowed: true},
		{name: "running succeeds", from: OperationRunning, to: OperationSucceeded, allowed: true},
		{name: "running fails", from: OperationRunning, to: OperationFailed, allowed: true},
		{name: "running cancels", from: OperationRunning, to: OperationCancelled, allowed: true},
		{name: "queued cannot succeed directly", from: OperationQueued, to: OperationSucceeded},
		{name: "running cannot go back to queued", from: OperationRunning, to: OperationQueued},
		{name: "succeeded is terminal", from: OperationSucceeded, to: OperationRunning, terminal: true},
		{name: "failed is terminal", from: OperationFailed, to: OperationRunning, terminal: true},
		{name: "cancelled is terminal", from: OperationCancelled, to: OperationRunning, terminal: true},
		{name: "same state is not a transition", from: OperationQueued, to: OperationQueued},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if !tt.from.Valid() {
				t.Fatalf("from state %q should be valid", tt.from)
			}
			if !tt.to.Valid() {
				t.Fatalf("to state %q should be valid", tt.to)
			}
			if got := tt.from.CanTransitionTo(tt.to); got != tt.allowed {
				t.Fatalf("CanTransitionTo: got %v want %v", got, tt.allowed)
			}
			if got := tt.from.Terminal(); got != tt.terminal {
				t.Fatalf("Terminal: got %v want %v", got, tt.terminal)
			}
		})
	}
	if OperationState("unknown").Valid() {
		t.Fatal("unknown operation state should be invalid")
	}
}

func TestChangeRequestStateValidAndTerminal(t *testing.T) {
	tests := []struct {
		state    ChangeRequestState
		valid    bool
		terminal bool
	}{
		{state: ChangeRequestOpen, valid: true},
		{state: ChangeRequestMerged, valid: true, terminal: true},
		{state: ChangeRequestClosed, valid: true, terminal: true},
		{state: ChangeRequestState("draft"), valid: false},
	}
	for _, tt := range tests {
		if got := tt.state.Valid(); got != tt.valid {
			t.Fatalf("Valid(%q): got %v want %v", tt.state, got, tt.valid)
		}
		if got := tt.state.Terminal(); got != tt.terminal {
			t.Fatalf("Terminal(%q): got %v want %v", tt.state, got, tt.terminal)
		}
	}
}

func TestChangeRequestActionValid(t *testing.T) {
	valid := []ChangeRequestAction{
		ChangeRequestActionCreate,
		ChangeRequestActionUpdate,
		ChangeRequestActionComment,
		ChangeRequestActionRequestReview,
		ChangeRequestActionMerge,
		ChangeRequestActionClose,
	}
	for _, action := range valid {
		if !action.Valid() {
			t.Fatalf("change request action %q should be valid", action)
		}
	}
	if ChangeRequestAction("list").Valid() {
		t.Fatal("list is a registry action, not a mutating change request action")
	}
}

func TestChangeRequestShapeFields(t *testing.T) {
	cr := ChangeRequest{
		Provider:     "github",
		ExternalID:   "42",
		RepoID:       "repo-1",
		SourceBranch: "feature/a",
		TargetBranch: "main",
		Title:        "Add CR model",
		Body:         "Provider-neutral body",
		Draft:        true,
		State:        ChangeRequestOpen,
	}

	if cr.Provider != "github" || cr.ExternalID != "42" || cr.RepoID != "repo-1" {
		t.Fatalf("identity fields: %+v", cr)
	}
	if cr.SourceBranch != "feature/a" || cr.TargetBranch != "main" {
		t.Fatalf("branch fields: %+v", cr)
	}
	if cr.Title == "" || cr.Body == "" || !cr.Draft || cr.State != ChangeRequestOpen {
		t.Fatalf("content fields: %+v", cr)
	}
}
