package taskboard

import (
	"errors"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestValidateKnownStatus(t *testing.T) {
	t.Parallel()

	require.NoError(t, validateKnownStatus(StatusTodo))
	require.NoError(t, validateKnownStatus(StatusInProgress))
	require.NoError(t, validateKnownStatus(StatusInReview))
	require.NoError(t, validateKnownStatus(StatusDone))

	err := validateKnownStatus(Status("paused"))
	require.Error(t, err)
	require.True(t, errors.Is(err, ErrBadRequest))
	require.Equal(t, "invalid task status", err.Error())
}

func TestValidateStatusTransition(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name            string
		current         Status
		desired         Status
		expectedError   error
		expectedMessage string
	}{
		{
			name:    "todo can move directly to done",
			current: StatusTodo,
			desired: StatusDone,
		},
		{
			name:    "todo can move directly to review",
			current: StatusTodo,
			desired: StatusInReview,
		},
		{
			name:    "in progress can return to todo",
			current: StatusInProgress,
			desired: StatusTodo,
		},
		{
			name:    "in review can be completed by default",
			current: StatusInReview,
			desired: StatusDone,
		},
		{
			name:    "done can reopen to todo",
			current: StatusDone,
			desired: StatusTodo,
		},
		{
			name:    "done can reopen to in progress",
			current: StatusDone,
			desired: StatusInProgress,
		},
		{
			name:    "same status is valid",
			current: StatusInProgress,
			desired: StatusInProgress,
		},
		{
			name:            "unknown current state is rejected",
			current:         Status("paused"),
			desired:         StatusInProgress,
			expectedError:   ErrConflict,
			expectedMessage: "invalid task status transition",
		},
		{
			name:            "unknown desired state is rejected",
			current:         StatusTodo,
			desired:         Status("paused"),
			expectedError:   ErrBadRequest,
			expectedMessage: "invalid task status",
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			t.Parallel()

			err := validateStatusTransition(test.current, test.desired)
			if test.expectedError == nil {
				require.NoError(t, err)
				return
			}

			require.Error(t, err)
			require.True(t, errors.Is(err, test.expectedError))
			if test.expectedMessage != "" {
				require.Equal(t, test.expectedMessage, err.Error())
			}
		})
	}
}

func TestInvalidTaskStatusTransitionErrorCarriesDetails(t *testing.T) {
	t.Parallel()

	err := validateStatusTransition(Status("paused"), StatusDone)

	require.Error(t, err)
	require.True(t, errors.Is(err, ErrConflict))
	var transitionErr InvalidTaskStatusTransitionError
	require.ErrorAs(t, err, &transitionErr)
	require.Equal(t, "invalid_task_status_transition", InvalidTaskStatusTransitionCode)

	details := transitionErr.Details()
	require.Equal(t, Status("paused"), details.CurrentStatus)
	require.Equal(t, StatusDone, details.DesiredStatus)
	require.Equal(t, []Status{StatusTodo, StatusInProgress, StatusInReview, StatusDone}, details.AllowedNextStatuses)
	require.Contains(t, details.Hint, "Refresh")
}
