package logredactionpolicy

import (
	"context"
	"strconv"
	"testing"

	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver"
	mockregionalserver "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver/mock_regionalserver"
	mockremoteencryptor "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/remoteencryptor/mock_remoteencryptor"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	logredactionpolicyrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/logredactionpolicy"
	tidbgrouprepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/tidbgroup"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/pkg/config"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func TestGetLogRedactionPolicy_AgentBranch(t *testing.T) {
	cluster, region, tidbgroup := testAgentEntities()
	clusterIDStr := strconv.FormatUint(cluster.ClusterId, 10)

	testCases := []struct {
		name         string
		cfg          *config.Config
		cachedPolicy *globalngv1.LogRedactionPolicy
		prepare      func(regionalCli *mockregionalserver.MockClient, encryptor *mockremoteencryptor.MockClient)
		wantErr      bool
		wantRedact   bool
	}{
		{
			name:         "cached policy short-circuits both paths",
			cfg:          &config.Config{BizConfig: config.BizConfig{EnableAuditLogViaClusterAgent: true}},
			cachedPolicy: &globalngv1.LogRedactionPolicy{ClusterId: cluster.ClusterId, RedactLog: boolPtr(true)},
			prepare:      func(*mockregionalserver.MockClient, *mockremoteencryptor.MockClient) {},
			wantErr:      false,
			wantRedact:   true,
		},
		{
			name: "flag enabled takes agent path",
			cfg:  &config.Config{BizConfig: config.BizConfig{EnableAuditLogViaClusterAgent: true}},
			prepare: func(regionalCli *mockregionalserver.MockClient, encryptor *mockremoteencryptor.MockClient) {
				setupAgentCredsExpectations(t, regionalCli, encryptor, cluster, region, tidbgroup)
				regionalCli.EXPECT().GetLogRedactionPolicyViaAgent(
					gomock.Any(), region, clusterIDStr, "sp-1", gomock.Any(),
				).Return(&regionalserver.GetLogRedactionPolicyViaAgentResponse{RedactLog: true}, nil)
			},
			wantErr:    false,
			wantRedact: true,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			defer ctrl.Finish()

			regionalCli := mockregionalserver.NewMockClient(ctrl)
			encryptor := mockremoteencryptor.NewMockClient(ctrl)
			tc.prepare(regionalCli, encryptor)

			svc := &service{
				regionalServerCli: regionalCli,
				encryptor:         encryptor,
				tidbGroupRepo:     &fakeGetTidbGroupRepo{groups: []*entity.TidbGroup{tidbgroup}},
				logRedactionPolicyRepo: &fakeGetPolicyRepo{
					policy: tc.cachedPolicy,
					notFoundErr: func() bizerr.BizError {
						if tc.cachedPolicy != nil {
							return nil
						}
						return bizerr.New(bizerr.ErrCodeRecordNotFound, "not found")
					}(),
				},
				cfg: tc.cfg,
			}

			got, err := svc.getLogRedactionPolicy(context.Background(), cluster, region)
			if tc.wantErr {
				require.Error(t, err)
				return
			}
			require.NoError(t, err)
			require.Equal(t, tc.wantRedact, got.GetRedactLog())
		})
	}
}

func boolPtr(b bool) *bool { return &b }

type fakeGetPolicyRepo struct {
	policy      *globalngv1.LogRedactionPolicy
	notFoundErr bizerr.BizError
}

func (f *fakeGetPolicyRepo) GetByClusterID(context.Context, uint64) (*globalngv1.LogRedactionPolicy, bizerr.BizError) {
	if f.policy != nil {
		return f.policy, nil
	}
	return nil, f.notFoundErr
}
func (f *fakeGetPolicyRepo) Upsert(context.Context, *globalngv1.LogRedactionPolicy) bizerr.BizError {
	panic("not implemented")
}
func (f *fakeGetPolicyRepo) DeleteByClusterID(context.Context, uint64) bizerr.BizError {
	panic("not implemented")
}

type fakeGetTidbGroupRepo struct {
	groups []*entity.TidbGroup
}

func (f *fakeGetTidbGroupRepo) Create(context.Context, *entity.TidbGroup) bizerr.BizError {
	panic("not implemented")
}
func (f *fakeGetTidbGroupRepo) Save(context.Context, *entity.TidbGroup) bizerr.BizError {
	panic("not implemented")
}
func (f *fakeGetTidbGroupRepo) Load(context.Context, uint64, uint64) (*entity.TidbGroup, bizerr.BizError) {
	panic("not implemented")
}
func (f *fakeGetTidbGroupRepo) Delete(context.Context, *entity.TidbGroup) bizerr.BizError {
	panic("not implemented")
}
func (f *fakeGetTidbGroupRepo) DeleteByClusterId(context.Context, uint64) bizerr.BizError {
	panic("not implemented")
}
func (f *fakeGetTidbGroupRepo) Query(context.Context, *tidbgrouprepo.Filter) ([]*entity.TidbGroup, bizerr.BizError) {
	return f.groups, nil
}

var (
	_ logredactionpolicyrepo.Repo = (*fakeGetPolicyRepo)(nil)
	_ tidbgrouprepo.Repo          = (*fakeGetTidbGroupRepo)(nil)
)
