//lint:ignore file
package opworker

import (
	"context"
	"testing"
	"time"

	"github.com/tidbcloud/account/commons/eventbus"

	"github.com/tidbcloud/account/internel/svc/org/handler"

	"github.com/golang/mock/gomock"
	"github.com/shopspring/decimal"
	. "github.com/smartystreets/goconvey/convey"
	"github.com/stretchr/testify/suite"
	"github.com/tidbcloud/account/central/test"
	"github.com/tidbcloud/account/commons/database"
	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/account/thirdparty/account"
	"github.com/tidbcloud/account/thirdparty/billing2"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

func TestMarketplaceWorker_notifyOpsAndRD(t *testing.T) {
	billing2.SetHandler(&MockBillingHandler{})
	Convey("TestMarketplaceWorker_notifyOpsAndRD", t, func() {
		ctrl := gomock.NewController(t)
		defer ctrl.Finish()
		mockAccountAPI := account.NewMockAccountAPIHandler(ctrl)

		ctx := context.Background()
		w := MarketplaceWorker{
			GlobalWorker: GlobalWorker{},
			accountAPI:   mockAccountAPI,
		}
		accountProvider := "aws"
		accountCode := "accountCode"
		tenant := &entity.Tenant{
			Updatable: entity.Updatable{
				Base: entity.Base{ID: 1},
			},
			Alias:                     "alias",
			AccountManager:            "accountManager",
			ThirdPartyAccountProvider: &accountProvider,
			ThirdPartyAccountCode:     &accountCode,
		}
		input := handler.SubscribeChange{
			AccountProvider: accountProvider,
			AccountCode:     accountCode,
			SubscribeStatus: handler.SubscribeStatusUnsubscribed,
		}
		Convey("normal case", func() {
			mockAccountAPI.EXPECT().SendEmail(gomock.Any(), gomock.Any()).Return(nil, nil)

			err := w.notifyOpsAndRD(ctx, tenant, input, "")
			So(err, ShouldBeNil)
		})
	})
}

type MockEventBusHandler struct {
	eventbus.EventBusHandler
	Messages []*eventbus.Event
}

func (h *MockEventBusHandler) PublishEventWithTransaction(eventType string, event *eventbus.Event, gTx *gorm.DB) (err error) {
	h.Messages = append(h.Messages, event)
	return nil
}

type MockBillingHandler struct {
	billing2.BillingHandler
}

func (*MockBillingHandler) GetTenantPlanInfo(ctx context.Context, tenantID uint64) (*billing2.TenantPlanInfo, error) {
	return &billing2.TenantPlanInfo{
		POCCredits:        decimal.Zero,
		EffectiveDiscount: decimal.NewFromInt(1),
		EffectivePlan:     "on_demand",
		DevTierPlanInfo: &billing2.PlanInfo{
			Status: "active",
		},
	}, nil
}

func (*MockBillingHandler) GetMonthlyBills(ctx context.Context, tenantID uint64, year int, month time.Month) (*billing2.CostExplorerResult, error) {
	return &billing2.CostExplorerResult{
		InvoiceCosts: billing2.Price{CostPrice: decimal.NewFromInt(1), RawPrice: decimal.NewFromInt(1)},
		TotalCosts:   billing2.Price{CostPrice: decimal.NewFromInt(1), RawPrice: decimal.NewFromInt(1)},
	}, nil
}

type MarketplaceWorkerTestSuite struct {
	test.CentralTestSuite
	ctrl           *gomock.Controller
	mockAccountAPI *account.MockAccountAPIHandler
}

func TestTenantSuite(t *testing.T) {
	suite.Run(t, new(MarketplaceWorkerTestSuite))
}

func (s *MarketplaceWorkerTestSuite) SetupTest() {
	s.CentralTestSuite.SetupTest()
	s.ctrl = gomock.NewController(s.T())
	s.mockAccountAPI = account.NewMockAccountAPIHandler(s.ctrl)
	eventbus.Set(&MockEventBusHandler{})
	billing2.SetHandler(&MockBillingHandler{})
}

func (s *MarketplaceWorkerTestSuite) TestMarketplaceWorkerSubscribed() {
	assert := s.Assert()
	ctx := context.Background()
	w := MarketplaceWorker{
		GlobalWorker: GlobalWorker{
			logger: tilog.With(zap.String("worker", "marketplace")),
		},
		accountAPI: s.mockAccountAPI,
	}
	accountProvider := "aws1"
	accountCode := "accountCode1"
	tenant := &entity.Tenant{
		Updatable: entity.Updatable{
			Base: entity.Base{ID: 5},
		},
		Status:                    "active",
		Alias:                     "alias",
		AccountManager:            "accountManager",
		ThirdPartyAccountProvider: &accountProvider,
		ThirdPartyAccountCode:     &accountCode,
		ThirdPartyFreeTrial:       true,
	}
	err := database.Create(tenant)
	assert.NoError(err)
	input := handler.SubscribeChange{
		AccountProvider: accountProvider,
		AccountCode:     accountCode,
		SubscribeStatus: handler.SubscribeStatusSubscribed,
		FreeTrial:       true,
	}
	success, err := w.ProcessSubscribeChange(ctx, input)
	assert.NoError(err)
	assert.True(success)

	err = database.Transaction(func(tx *gorm.DB) error {
		return entity.NewTenantQuerySet(tx).IDEq(tenant.ID).One(tenant)
	})
	assert.NoError(err)
	assert.True(tenant.ThirdPartyFreeTrial)
	assert.Len(eventbus.Get().(*MockEventBusHandler).Messages, 0)

	input.FreeTrial = false
	success, err = w.ProcessSubscribeChange(ctx, input)
	assert.NoError(err)
	assert.True(success)

	err = database.Transaction(func(tx *gorm.DB) error {
		return entity.NewTenantQuerySet(tx).IDEq(tenant.ID).One(tenant)
	})
	assert.NoError(err)
	assert.False(tenant.ThirdPartyFreeTrial)
	msgs := eventbus.Get().(*MockEventBusHandler).Messages
	assert.Len(msgs, 1)
	assert.Equal(ThirdPartyNormalSubscribed, msgs[0].EventType)
}

func (s *MarketplaceWorkerTestSuite) TestMarketplaceWorkerUnsubscribed() {
	assert := s.Assert()
	ctx := context.Background()
	w := MarketplaceWorker{
		GlobalWorker: GlobalWorker{
			logger: tilog.With(zap.String("worker", "marketplace")),
		},
		accountAPI: s.mockAccountAPI,
	}
	accountProvider := "gcp"
	accountCode := "accountCode2"
	tenant := &entity.Tenant{
		Updatable: entity.Updatable{
			Base: entity.Base{ID: 5},
		},
		Status:                    "active",
		Alias:                     "alias",
		AccountManager:            "accountManager",
		ThirdPartyAccountProvider: &accountProvider,
		ThirdPartyAccountCode:     &accountCode,
		ThirdPartyFreeTrial:       true,
	}
	err := database.Create(tenant)
	assert.NoError(err)
	input := handler.SubscribeChange{
		AccountProvider: accountProvider,
		AccountCode:     accountCode,
		SubscribeStatus: handler.SubscribeStatusUnsubscribed,
		FreeTrial:       true,
	}
	success, err := w.ProcessSubscribeChange(ctx, input)
	assert.NoError(err)
	assert.True(success)
	assert.Len(eventbus.Get().(*MockEventBusHandler).Messages, 0)

	err = database.Transaction(func(tx *gorm.DB) error {
		return entity.NewTenantQuerySet(tx).IDEq(tenant.ID).GetUpdater().SetThirdPartyFreeTrial(false).Update()
	})
	assert.NoError(err)

	s.mockAccountAPI.EXPECT().SendEmail(gomock.Any(), gomock.Any()).Return(nil, nil)
	s.mockAccountAPI.EXPECT().SendEmailByLabels(gomock.Any(), gomock.Any()).Return(nil, nil)
	input.FreeTrial = false
	success, err = w.ProcessSubscribeChange(ctx, input)
	assert.NoError(err)
	assert.True(success)
	msgs := eventbus.Get().(*MockEventBusHandler).Messages
	assert.Len(msgs, 1)
	assert.Equal(ThirdPartyNormalUnsubscribed, msgs[0].EventType)
	err = database.Transaction(func(tx *gorm.DB) error {
		return entity.NewTenantQuerySet(tx).IDEq(tenant.ID).One(tenant)
	})
	assert.NoError(err)
	assert.True(tenant.ThirdPartyFreeTrial)
}
