package impl_test

import (
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
	"github.com/tidbcloud/dataflow-service-ng/app/internal/status_matrix"
	"github.com/tidbcloud/dataflow-service-ng/app/internal/status_matrix/impl"
)

type Status string

const (
	StatusCreating Status = "creating"
	StatusNormal   Status = "normal"
	StatusEditing  Status = "editing"
	StatusDeleting Status = "deleting"
	StatusDeleted  Status = "deleted"
)

type Action string

const (
	ActionCreate Action = "create"
	ActionDelete Action = "delete"
	ActionEdit   Action = "edit"
)

var statusMatrix = map[string]map[string]string{
	string(StatusCreating): {
		string(ActionDelete): string(StatusDeleting),
	},
	string(StatusNormal): {
		string(ActionDelete): string(StatusDeleting),
		string(ActionEdit):   string(StatusEditing),
	},
	string(StatusEditing): {
		string(ActionDelete): string(StatusDeleting),
	},
	string(StatusDeleting): {
		string(ActionDelete): string(StatusDeleting),
	},
	string(StatusDeleted): {},
}

var friendlyActionMap = map[string]map[string]status_matrix.ActionStrategy{
	string(ActionCreate): {
		string(ActionDelete): status_matrix.Grab,
	},
	string(ActionDelete): {},
	string(ActionEdit): {
		string(ActionDelete): status_matrix.Grab,
	},
}

var steadyStateMatrix = map[string]string{
	string(StatusCreating): string(StatusNormal),
	string(StatusEditing):  string(StatusNormal),
	string(StatusDeleting): string(StatusDeleted),
}

type statusMatrixTestSuite struct {
	suite.Suite
}

func (s *statusMatrixTestSuite) SetupSuite() {
}

func (s *statusMatrixTestSuite) TestCalculateNextStatusForActionJustStatus() {
	matrix := impl.NewStatusMatrix(statusMatrix, friendlyActionMap, steadyStateMatrix)

	nextStatus := matrix.CalculateNextStatusForAction(status_matrix.CurrentStatus{
		Status: string(StatusCreating),
	}, string(ActionDelete))

	require.Equal(s.T(), string(StatusDeleting), nextStatus.Status)

	nextStatus = matrix.CalculateNextStatusForAction(status_matrix.CurrentStatus{
		Status: string(StatusNormal),
	}, string(ActionCreate))
	require.True(s.T(), nextStatus == nil)
}

func (s *statusMatrixTestSuite) TestCalculateNextStatusIfActionDoneJustStatus() {
	matrix := impl.NewStatusMatrix(statusMatrix, friendlyActionMap, steadyStateMatrix)

	nextStatus := matrix.CalculateNextStatusIfActionDone(status_matrix.CurrentStatus{
		Status: string(StatusCreating),
	}, string(ActionCreate))

	require.Equal(s.T(), string(StatusNormal), nextStatus.Status)

	nextStatus = matrix.CalculateNextStatusIfActionDone(status_matrix.CurrentStatus{
		Status: string(StatusCreating) + "111",
	}, string(ActionDelete))
	require.True(s.T(), nextStatus == nil)
}

func (s *statusMatrixTestSuite) TestCalculateNextStatusForAction() {
	matrix := impl.NewStatusMatrix(statusMatrix, friendlyActionMap, steadyStateMatrix)

	nextStatus := matrix.CalculateNextStatusForAction(status_matrix.CurrentStatus{
		Status: string(StatusNormal),
	}, string(ActionEdit))
	require.Equal(s.T(), string(StatusEditing), nextStatus.Status)
	require.True(s.T(), len(nextStatus.Actions) == 1)
	require.Equal(s.T(), string(ActionEdit), nextStatus.Actions[0])

	nextStatus = matrix.CalculateNextStatusForAction(status_matrix.CurrentStatus{
		Status: string(StatusCreating),
		Actions: []string{
			string(ActionCreate),
		},
	}, string(ActionDelete))

	require.Equal(s.T(), string(StatusDeleting), nextStatus.Status)
	require.True(s.T(), len(nextStatus.Actions) == 1)
	require.Equal(s.T(), string(ActionDelete), nextStatus.Actions[0])

	nextStatus = matrix.CalculateNextStatusForAction(status_matrix.CurrentStatus{
		Status: string(StatusCreating),
		Actions: []string{
			string(ActionCreate),
		},
	}, string(ActionEdit))
	require.True(s.T(), nextStatus == nil)
}

func (s *statusMatrixTestSuite) TestCalculateNextStatusIfActionDone() {
	matrix := impl.NewStatusMatrix(statusMatrix, friendlyActionMap, steadyStateMatrix)

	nextStatus := matrix.CalculateNextStatusIfActionDone(status_matrix.CurrentStatus{
		Status: string(StatusCreating),
		Actions: []string{
			string(ActionCreate),
		},
	}, string(ActionCreate))
	require.Equal(s.T(), string(StatusNormal), nextStatus.Status)
	require.True(s.T(), len(nextStatus.Actions) == 0)

	nextStatus = matrix.CalculateNextStatusIfActionDone(status_matrix.CurrentStatus{
		Status: string(StatusDeleting),
		Actions: []string{
			string(ActionDelete),
		},
	}, string(ActionCreate))
	require.Equal(s.T(), string(StatusDeleting), nextStatus.Status)
	require.True(s.T(), len(nextStatus.Actions) == 1)
	require.Equal(s.T(), string(ActionDelete), nextStatus.Actions[0])
}

func TestStatusMatrixTestSuite(t *testing.T) {
	suite.Run(t, new(statusMatrixTestSuite))
}
