package ostate

import (
	"errors"
	"testing"

	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
)

func TestStepTransitionMatrix(t *testing.T) {
	sm := New()

	tests := []struct {
		name    string
		from    agg.StepState
		to      agg.StepState
		wantOK  bool
		wantErr error
	}{
		{
			name:   "unspecified to pending",
			from:   agg.StepStateUnspecified,
			to:     agg.StepStatePending,
			wantOK: true,
		},
		{
			name:   "pending to running",
			from:   agg.StepStatePending,
			to:     agg.StepStateRunning,
			wantOK: true,
		},
		{
			name:   "running to completed",
			from:   agg.StepStateRunning,
			to:     agg.StepStateCompleted,
			wantOK: true,
		},
		{
			name:   "failed to running retry",
			from:   agg.StepStateFailed,
			to:     agg.StepStateRunning,
			wantOK: true,
		},
		{
			name:   "needs attention to completed",
			from:   agg.StepStateNeedsAttention,
			to:     agg.StepStateCompleted,
			wantOK: true,
		},
		{
			name:   "idempotent self transition",
			from:   agg.StepStateSkipped,
			to:     agg.StepStateSkipped,
			wantOK: true,
		},
		{
			name:    "pending to completed denied",
			from:    agg.StepStatePending,
			to:      agg.StepStateCompleted,
			wantOK:  false,
			wantErr: ErrStepTransitionNotAllowed,
		},
		{
			name:    "completed to running denied",
			from:    agg.StepStateCompleted,
			to:      agg.StepStateRunning,
			wantOK:  false,
			wantErr: ErrStepTransitionNotAllowed,
		},
		{
			name:    "unknown from state",
			from:    agg.StepState("waiting"),
			to:      agg.StepStateRunning,
			wantOK:  false,
			wantErr: ErrUnknownStepState,
		},
		{
			name:    "unknown to state",
			from:    agg.StepStateRunning,
			to:      agg.StepState("waiting"),
			wantOK:  false,
			wantErr: ErrUnknownStepState,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := sm.CanTransitionStep(tt.from, tt.to); got != tt.wantOK {
				t.Fatalf("CanTransitionStep mismatch: got=%v want=%v", got, tt.wantOK)
			}

			err := sm.EnsureStepTransition(tt.from, tt.to)
			if tt.wantOK {
				if err != nil {
					t.Fatalf("EnsureStepTransition returned error: %v", err)
				}
				return
			}
			if err == nil {
				t.Fatal("expected error")
			}
			if !errors.Is(err, tt.wantErr) {
				t.Fatalf("expected error %v, got %v", tt.wantErr, err)
			}
		})
	}
}

func TestStepTerminalStates(t *testing.T) {
	sm := New()

	tests := []struct {
		state  agg.StepState
		expect bool
	}{
		{state: agg.StepStateUnspecified, expect: false},
		{state: agg.StepStatePending, expect: false},
		{state: agg.StepStateRunning, expect: false},
		{state: agg.StepStateNeedsAttention, expect: false},
		{state: agg.StepStateFailed, expect: false},
		{state: agg.StepStateCompleted, expect: true},
		{state: agg.StepStateSkipped, expect: true},
	}

	for _, tt := range tests {
		if got := sm.IsStepTerminal(tt.state); got != tt.expect {
			t.Fatalf("IsStepTerminal(%q) mismatch: got=%v want=%v", tt.state, got, tt.expect)
		}
	}
}
