package disable

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 markDisabledTestSuite struct {
	suite.Suite
	act             *markDisabledAction
	crrfSettingRepo *crrfRepoMock.CRRFSettingRepo
	logger          *zap.Logger
}

func TestMarkDisabledSuite(t *testing.T) {
	suite.Run(t, new(markDisabledTestSuite))
}

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

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

func (s *markDisabledTestSuite) params() *markDisabledParams {
	return &markDisabledParams{ClusterID: 1001}
}

func (s *markDisabledTestSuite) crrfSetting(status entity.CRRFSettingStatus) *entity.CRRFSetting {
	secondaryRegion := "us-west-2"
	errMsg := "some error"
	cs := &entity.CRRFSetting{
		ClusterID:         entity.ID(1001),
		Status:            status,
		SecondaryRegion:   secondaryRegion,
		MasterRegion:      "us-east-1",
		MasterBucketID:    entity.ID(11),
		SecondaryBucketID: entity.ID(22),
		ErrMsg:            &errMsg,
	}
	cs.ID = 5
	return cs
}

func (s *markDisabledTestSuite) 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 disabled — idempotent skip": {
			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.CRRFSettingStatusDisabling)
				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: fields cleared and status set to disabled": {
			setup: func() {
				cs := s.crrfSetting(entity.CRRFSettingStatusDisabling)
				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.CRRFSettingStatusDisabled &&
						c.SecondaryRegion == "" &&
						c.MasterBucketID == 0 &&
						c.SecondaryBucketID == 0 &&
						c.MasterRegion == "" &&
						c.ErrMsg == nil
				})).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)
			}
		})
	}
}
