package ostate

import (
	"errors"
	"testing"

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

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

	tests := []struct {
		name    string
		from    agg.State
		to      agg.State
		wantOK  bool
		wantErr error
	}{
		{
			name:   "unspecified to created",
			from:   agg.StateUnspecified,
			to:     agg.StateCreated,
			wantOK: true,
		},
		{
			name:   "created to executing",
			from:   agg.StateCreated,
			to:     agg.StateExecuting,
			wantOK: true,
		},
		{
			name:   "executing to needs attention",
			from:   agg.StateExecuting,
			to:     agg.StateNeedsAttention,
			wantOK: true,
		},
		{
			name:   "executing to settled",
			from:   agg.StateExecuting,
			to:     agg.StateSettled,
			wantOK: true,
		},
		{
			name:   "needs attention back to executing",
			from:   agg.StateNeedsAttention,
			to:     agg.StateExecuting,
			wantOK: true,
		},
		{
			name:   "idempotent self transition",
			from:   agg.StateFailed,
			to:     agg.StateFailed,
			wantOK: true,
		},
		{
			name:    "created to settled denied",
			from:    agg.StateCreated,
			to:      agg.StateSettled,
			wantOK:  false,
			wantErr: ErrAggregateTransitionNotAllowed,
		},
		{
			name:    "settled to executing denied",
			from:    agg.StateSettled,
			to:      agg.StateExecuting,
			wantOK:  false,
			wantErr: ErrAggregateTransitionNotAllowed,
		},
		{
			name:    "unknown from state",
			from:    agg.State("paused"),
			to:      agg.StateExecuting,
			wantOK:  false,
			wantErr: ErrUnknownAggregateState,
		},
		{
			name:    "unknown to state",
			from:    agg.StateExecuting,
			to:      agg.State("paused"),
			wantOK:  false,
			wantErr: ErrUnknownAggregateState,
		},
	}

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

			err := sm.EnsureAggregateTransition(tt.from, tt.to)
			if tt.wantOK {
				if err != nil {
					t.Fatalf("EnsureAggregateTransition 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 TestAggregateTerminalStates(t *testing.T) {
	sm := New()

	tests := []struct {
		state  agg.State
		expect bool
	}{
		{state: agg.StateUnspecified, expect: false},
		{state: agg.StateCreated, expect: false},
		{state: agg.StateExecuting, expect: false},
		{state: agg.StateNeedsAttention, expect: false},
		{state: agg.StateSettled, expect: true},
		{state: agg.StateFailed, expect: true},
	}

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