package enable

import (
	"context"
	"testing"

	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/suite"
	crrfRepoMock "github.com/tidbcloud/dataflow-service/app/repo/crrf/mock"
	"github.com/tidbcloud/dataflow-service/app/repo/entity"
	"github.com/tidbcloud/pkgv2/bizerr"
	"go.uber.org/zap"
)

type markEnabledTestSuite struct {
	suite.Suite
	act             *markEnabledAction
	crrfSettingRepo *crrfRepoMock.CRRFSettingRepo
	logger          *zap.Logger
}

func TestMarkEnabledSuite(t *testing.T) {
	suite.Run(t, new(markEnabledTestSuite))
}

func (s *markEnabledTestSuite) SetupSuite() {
	s.crrfSettingRepo = crrfRepoMock.NewCRRFSettingRepo(s.T())
	s.logger = zap.NewNop()
	s.act = &markEnabledAction{crrfSettingRepo: s.crrfSettingRepo}
}

func (s *markEnabledTestSuite) TearDownTest() {
	s.crrfSettingRepo.AssertExpectations(s.T())
}

func (s *markEnabledTestSuite) params() *markEnabledParams {
	return &markEnabledParams{ClusterID: 1001}
}

func (s *markEnabledTestSuite) crrfSetting(status entity.CRRFSettingStatus) *entity.CRRFSetting {
	cs := &entity.CRRFSetting{ClusterID: entity.ID(1001), Status: status}
	cs.ID = 5
	return cs
}

func (s *markEnabledTestSuite) TestRun() {
	ctx := context.Background()
	p := s.params()

	tests := map[string]struct {
		setup   func()
		wantErr bool
		errIs   error
	}{
		"repo error": {
			setup: func() {
				s.crrfSettingRepo.On("GetByClusterID", mock.Anything, p.ClusterID).
					Return(nil, bizerr.New(bizerr.ErrCodeDB, "db error")).Once()
			},
			wantErr: true,
		},
		"setting not found": {
			setup: func() {
				s.crrfSettingRepo.On("GetByClusterID", mock.Anything, p.ClusterID).
					Return(nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "crrf setting not found")).Once()
			},
			wantErr: true,
		},
		"already enabled — idempotent skip": {
			setup: func() {
				cs := s.crrfSetting(entity.CRRFSettingStatusEnabled)
				s.crrfSettingRepo.On("GetByClusterID", mock.Anything, p.ClusterID).Return(cs, nil).Once()
			},
			wantErr: false,
		},
		"unexpected status — skip with no error": {
			setup: func() {
				cs := s.crrfSetting(entity.CRRFSettingStatusDisabled)
				s.crrfSettingRepo.On("GetByClusterID", mock.Anything, p.ClusterID).Return(cs, nil).Once()
			},
			wantErr: false,
		},
		"save fails": {
			setup: func() {
				cs := s.crrfSetting(entity.CRRFSettingStatusEnabling)
				s.crrfSettingRepo.On("GetByClusterID", mock.Anything, p.ClusterID).Return(cs, nil).Once()
				s.crrfSettingRepo.On("Save", mock.Anything, mock.Anything).
					Return(bizerr.New(bizerr.ErrCodeDB, "save error")).Once()
			},
			wantErr: true,
		},
		"success: status set to enabled": {
			setup: func() {
				cs := s.crrfSetting(entity.CRRFSettingStatusEnabling)
				s.crrfSettingRepo.On("GetByClusterID", mock.Anything, p.ClusterID).Return(cs, nil).Once()
				s.crrfSettingRepo.On("Save", mock.Anything, mock.MatchedBy(func(c *entity.CRRFSetting) bool {
					return c.Status == entity.CRRFSettingStatusEnabled
				})).Return(nil).Once()
			},
			wantErr: false,
		},
	}

	for name, tc := range tests {
		s.Run(name, func() {
			tc.setup()
			err := s.act.Run(ctx, s.logger, p)
			if tc.wantErr {
				s.Error(err)
				if tc.errIs != nil {
					s.Equal(tc.errIs, err)
				}
			} else {
				s.NoError(err)
			}
		})
	}
}
