package tests

import (
	"errors"
	"testing"
	"time"

	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/db"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/centralhandler"
	"github.com/pingcap/dbaas/billing2/handler/paymenthandler"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/model/billingconfig"
	"github.com/pingcap/dbaas/billing2/model/payment"
	"github.com/pingcap/dbaas/billing2/model/payment/paymentsub"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/pingcap/dbaas/billing2/utils/tests/datainit"
	"github.com/pingcap/dbaas/billing2/utils/timeutils"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/suite"
)

var invoiceModel = &payment.InvoiceModel{}

type InvoiceSuite struct {
	tests.BillingTestSuite
}

func TestInvoiceSuite(t *testing.T) {
	suite.Run(t, new(InvoiceSuite))
}

func (suite *InvoiceSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	datainit.CreateBillingConfig(1, entity.BillingConfigType_DirectCustomer)
	config.Get().Set(config.EnableTaxCalculation, false)
}

func (suite *InvoiceSuite) TestListAllInvoices() {
	assert := suite.Assert()
	const tenantID uint = 1
	infos, err := invoiceModel.ListAllInvoices(tenantID, 1, false)
	assert.NoError(err)
	assert.Len(infos, 0)

	_, err = datainit.CreateInvoice(uint(1))
	assert.NoError(err)

	infos, err = invoiceModel.ListAllInvoices(tenantID, 1, false)
	assert.Nil(err)
	assert.Len(infos, 1)

	assert.Len(infos[0].CategoryUnitsCosts, 1)

	infos, err = invoiceModel.ListAllInvoices(tenantID, 1, true)
	assert.Nil(err)
	assert.Len(infos, 1)

	assert.Nil(infos[0].CategoryUnitsCosts)
}

func (suite *InvoiceSuite) TestConfirmInvoice() {
	assert := suite.Assert()
	const tenantID uint = 1

	invoiceEntity := entity.Invoice{
		TenantID:    tenantID,
		Status:      entity.InvoiceStatus_GENERATED,
		Type:        entity.InvoiceType_MONTHLY,
		InvoiceDate: time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
	}
	assert.Nil(invoiceEntity.Create(db.GetDB()))
	info, err := invoiceModel.ConfirmInvoice(invoiceEntity.ID)
	assert.Nil(err)
	assert.Equal(info.InvoiceID, invoiceEntity.ID)
	assert.Equal(info.Status, entity.InvoiceStatus_CONFIRMED)
	_, err = invoiceModel.SuspendInvoice(invoiceEntity.ID)
	assert.Error(err)
}

// TestConfirmAndPayInvoiceHappyPath covers the synchronous single-invoice
// confirm path: GENERATED + non-managed-by-sales + non-commitment type
// (monthly in this case) flips through CONFIRMED to SENT in one call
// via the inline sendInvoice. Stubs the payment handler with the mock
// so the Pay call resolves without a real Stripe round-trip.
//
// Important: by the time the function returns, status should be SENT —
// not CONFIRMED. That is the difference vs the legacy ConfirmInvoice
// path, which left the row in CONFIRMED for the next worker tick.
func (suite *InvoiceSuite) TestConfirmAndPayInvoiceHappyPath() {
	tests.MockPaymentHandler()
	tests.MockCentralHandler()
	assert := suite.Assert()
	const tenantID uint = 1
	billingConfigID := uint(1)

	paymentModel := &paymentsub.PaymentModel{}
	_, err := paymentModel.CreatePaymentMethodAndActive(
		tenantID,
		&forms.PaymentMethodCreateQuery{
			PaymentType:           "mock",
			BindedBillingConfigID: &billingConfigID,
		},
	)
	assert.NoError(err)
	err = (&paymentsub.BillingInformationModel{}).CreateBillingCompany(&forms.BillingCompany{
		CompanyKey:     config.Get().GetString(config.DefaultBillingCompanyKey),
		Name:           "PINGCAP",
		PaymentSubCode: "",
	})
	assert.NoError(err)

	invoiceEntity := entity.Invoice{
		TenantID:              tenantID,
		Status:                entity.InvoiceStatus_GENERATED,
		Type:                  entity.InvoiceType_MONTHLY,
		InvoiceDate:           time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
		CostPrice:             decimal.RequireFromString("1.37"),
		BindedBillingConfigID: billingConfigID,
	}
	assert.NoError(invoiceEntity.Create(db.GetDB()))

	info, err := invoiceModel.ConfirmAndPayInvoice(invoiceEntity.ID)
	assert.NoError(err)
	assert.Equal(invoiceEntity.ID, info.InvoiceID)
	// Direct-pay path: status went through CONFIRMED inline and ended at SENT,
	// not the legacy CONFIRMED-pending-tick state.
	assert.Equal(entity.InvoiceStatus_SENT, info.Status,
		"direct-pay must land at SENT, not leave at CONFIRMED (the legacy batch-tick contract)")
}

// TestConfirmAndPayInvoiceSuspendsManagedBySalesNonCommitment is the Phase 4
// parity check for the direct-pay path. A non-commitment invoice under a
// managed_by_sales payment method must NOT trigger Stripe (sales handles
// those). The function should park the row in SUSPENDED and return without
// invoking sendInvoice.
func (suite *InvoiceSuite) TestConfirmAndPayInvoiceSuspendsManagedBySalesNonCommitment() {
	tests.MockPaymentHandler()
	tests.MockCentralHandler()
	assert := suite.Assert()
	const tenantID uint = 1
	billingConfigID := uint(1)

	paymentModel := &paymentsub.PaymentModel{}
	_, err := paymentModel.CreatePaymentMethodAndActive(
		tenantID,
		&forms.PaymentMethodCreateQuery{
			PaymentType:           "mock",
			BindedBillingConfigID: &billingConfigID,
			ManagedBySales:        true,
		},
	)
	assert.NoError(err)
	err = (&paymentsub.BillingInformationModel{}).CreateBillingCompany(&forms.BillingCompany{
		CompanyKey:     config.Get().GetString(config.DefaultBillingCompanyKey),
		Name:           "PINGCAP",
		PaymentSubCode: "",
	})
	assert.NoError(err)

	invoiceEntity := entity.Invoice{
		TenantID:              tenantID,
		Status:                entity.InvoiceStatus_GENERATED,
		Type:                  entity.InvoiceType_MONTHLY, // non-commitment
		InvoiceDate:           time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
		CostPrice:             decimal.RequireFromString("1.37"),
		BindedBillingConfigID: billingConfigID,
	}
	assert.NoError(invoiceEntity.Create(db.GetDB()))

	info, err := invoiceModel.ConfirmAndPayInvoice(invoiceEntity.ID)
	assert.NoError(err)
	// Phase 4 contract: managed-by-sales non-commitment routes to SUSPEND,
	// not SENT, regardless of whether the call comes from the Stripe paying
	// worker or the direct-pay API path.
	assert.Equal(entity.InvoiceStatus_SUSPENDED, info.Status,
		"managed-by-sales non-commitment must land at SUSPENDED, not SENT")
}

// TestConfirmAndPayInvoiceTerminalNoop asserts the function does not
// re-fire Stripe on an already-sent row. Returns the row as-is.
func (suite *InvoiceSuite) TestConfirmAndPayInvoiceTerminalNoop() {
	tests.MockPaymentHandler()
	tests.MockCentralHandler()
	assert := suite.Assert()
	const tenantID uint = 1
	billingConfigID := uint(1)

	paymentModel := &paymentsub.PaymentModel{}
	_, err := paymentModel.CreatePaymentMethodAndActive(
		tenantID,
		&forms.PaymentMethodCreateQuery{
			PaymentType:           "mock",
			BindedBillingConfigID: &billingConfigID,
		},
	)
	assert.NoError(err)
	err = (&paymentsub.BillingInformationModel{}).CreateBillingCompany(&forms.BillingCompany{
		CompanyKey:     config.Get().GetString(config.DefaultBillingCompanyKey),
		Name:           "PINGCAP",
		PaymentSubCode: "",
	})
	assert.NoError(err)

	now := timeutils.GetNow()
	invoiceEntity := entity.Invoice{
		TenantID:              tenantID,
		Status:                entity.InvoiceStatus_SENT,
		Type:                  entity.InvoiceType_MONTHLY,
		InvoiceDate:           time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
		CostPrice:             decimal.RequireFromString("1.37"),
		BindedBillingConfigID: billingConfigID,
		SentDate:              &now,
	}
	assert.NoError(invoiceEntity.Create(db.GetDB()))

	info, err := invoiceModel.ConfirmAndPayInvoice(invoiceEntity.ID)
	assert.NoError(err)
	assert.Equal(entity.InvoiceStatus_SENT, info.Status)
}

func (suite *InvoiceSuite) TestSuspendInvoice() {
	assert := suite.Assert()
	const tenantID uint = 1

	invoiceEntity := entity.Invoice{
		TenantID:    tenantID,
		Status:      entity.InvoiceStatus_GENERATED,
		Type:        entity.InvoiceType_MONTHLY,
		InvoiceDate: time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
	}
	assert.Nil(invoiceEntity.Create(db.GetDB()))
	info, err := invoiceModel.SuspendInvoice(invoiceEntity.ID)
	assert.Nil(err)
	assert.Equal(info.InvoiceID, invoiceEntity.ID)
	assert.Equal(info.Status, entity.InvoiceStatus_SUSPENDED)
	_, err = invoiceModel.ConfirmInvoice(invoiceEntity.ID)
	assert.Error(err)
}

func (suite *InvoiceSuite) TestConfirmInvoiceNegative() {
	assert := suite.Assert()
	const tenantID uint = 1

	invoiceEntity := entity.Invoice{
		TenantID:    tenantID,
		Status:      entity.InvoiceStatus_INITED,
		Type:        entity.InvoiceType_MONTHLY,
		InvoiceDate: time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
	}
	assert.Nil(invoiceEntity.Create(db.GetDB()))
	_, err := invoiceModel.ConfirmInvoice(invoiceEntity.ID)
	assert.NotNil(err)

	invoiceEntity = entity.Invoice{
		TenantID:    tenantID,
		Status:      entity.InvoiceStatus_SENT,
		Type:        entity.InvoiceType_MONTHLY,
		InvoiceDate: time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
	}
	assert.Nil(invoiceEntity.Create(db.GetDB()))
	info, err := invoiceModel.ConfirmInvoice(invoiceEntity.ID)
	assert.Nil(err)
	assert.Equal(info.Status, entity.InvoiceStatus_SENT)
}

func (suite *InvoiceSuite) TestCreateMonthlyInvoice() {
	assert := suite.Assert()
	invoice, err := datainit.CreateInvoice(uint(1))
	assert.NoError(err)
	assert.Equal(invoice.Status, entity.InvoiceStatus_GENERATED)
	assert.Zero(invoice.CostPrice.Cmp(decimal.NewFromInt(1)))
	assert.Len(invoice.CategoryUnitsCosts, 1)
	assert.Equal(invoice.CategoryUnitsCosts[config.Get().GetString(config.DefaultUnitCategory)].CostPrice, decimal.NewFromInt(1))
	assert.Equal(invoice.CategoryUnitsCosts[config.Get().GetString(config.KeeyShowingCategory)].CostPrice, decimal.Zero)
	assert.Equal(invoice.StartDate, datainit.BillingDate)
	assert.Equal(invoice.Type, entity.InvoiceType_MONTHLY)
	assert.Equal(invoice.EndDate, datainit.BillingDate.AddDate(0, 1, 0))
}

func (suite *InvoiceSuite) TestCreateMontlyNoCost() {
	assert := suite.Assert()
	const tenantID uint = 1
	var billingDate = time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC)
	_, err := invoiceModel.CreateMonthlyInvoice(tenantID, 1, billingDate)
	assert.True(errors.Is(err, billing_errors.InvoiceMonthlyNoCosts))
}

func (suite *InvoiceSuite) TestCreateAutoConfirmedHourlyInvoice() {
	assert := suite.Assert()

	// not normal time to test all logic
	measureTime := datainit.BillingDate.AddDate(0, 0, 10).Add(time.Hour * 10)
	err := datainit.CreateMeasuresWithTime(uint(1), measureTime)
	assert.NoError(err)
	invoice, err := invoiceModel.CreateAutoConfirmedHourlyInvoice(uint(1), 1, measureTime)
	assert.NoError(err)
	assert.Equal(invoice.Status, entity.InvoiceStatus_CONFIRMED)
	assert.Zero(invoice.CostPrice.Cmp(decimal.NewFromInt(1)))
	assert.Len(invoice.CategoryUnitsCosts, 1)
	assert.Equal(invoice.CategoryUnitsCosts[config.Get().GetString(config.DefaultUnitCategory)].CostPrice, decimal.NewFromInt(1))
	assert.Equal(invoice.StartDate, measureTime)
	assert.Equal(invoice.EndDate, measureTime.Add(time.Hour))
}

func (suite *InvoiceSuite) TestCreateMontlyDuplicate() {
	assert := suite.Assert()
	const tenantID uint = 1
	datainit.CreatePaymentMock(tenantID)
	var billingDate = time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC)
	err := datainit.CreatePricePackageAndBind(tenantID)
	assert.Nil(err)
	measureModel := &billing.MeasureModel{}
	_, err = measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: billingDate.Unix(),
		Unit:          datainit.TestUnit,
		Cost:          1,
	})
	assert.NoError(err)
	invoice, err := invoiceModel.CreateMonthlyInvoice(tenantID, 1, billingDate)
	assert.NoError(err)
	assert.Equal(invoice.Status, entity.InvoiceStatus_GENERATED)

	_, err = invoiceModel.CreateMonthlyInvoice(tenantID, 1, billingDate)
	assert.Error(err)
	assert.ErrorIs(err, billing_errors.ObjectAlreadyExists)
}

func (suite *InvoiceSuite) TestCreateMontlyDuplicateWithHourly() {
	assert := suite.Assert()
	const tenantID uint = 1
	datainit.CreatePaymentMock(tenantID)
	var billingDate = time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC)
	err := datainit.CreatePricePackageAndBind(tenantID)
	assert.Nil(err)
	measureModel := &billing.MeasureModel{}
	_, err = measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: billingDate.Unix(),
		Unit:          datainit.TestUnit,
		Cost:          1,
	})
	assert.NoError(err)

	_, err = measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: billingDate.Add(time.Hour * 48).Unix(),
		Unit:          datainit.TestUnit,
		Cost:          1,
	})
	assert.NoError(err)

	invoice, err := invoiceModel.CreateAutoConfirmedHourlyInvoice(tenantID, 1, billingDate)
	assert.NoError(err)
	assert.Equal(invoice.Status, entity.InvoiceStatus_CONFIRMED)

	_, err = invoiceModel.CreateAutoConfirmedMontlyInvoice(tenantID, 1, billingDate)
	assert.NoError(err)
	assert.Equal(invoice.Status, entity.InvoiceStatus_CONFIRMED)
}

func (suite *InvoiceSuite) TestSendInvoice() {
	tests.MockPaymentHandler()
	tests.MockCentralHandler()
	assert := suite.Assert()
	const tenantID uint = 1
	billingConfigID := uint(1)

	paymentModel := &paymentsub.PaymentModel{}
	_, err := paymentModel.CreatePaymentMethodAndActive(
		tenantID,
		&forms.PaymentMethodCreateQuery{
			PaymentType:           "mock",
			BindedBillingConfigID: &billingConfigID,
		},
	)
	assert.NoError(err)
	err = (&paymentsub.BillingInformationModel{}).CreateBillingCompany(&forms.BillingCompany{
		CompanyKey:     config.Get().GetString(config.DefaultBillingCompanyKey),
		Name:           "PINGCAP",
		PaymentSubCode: "",
	})
	assert.NoError(err)

	invoiceEntity := entity.Invoice{
		TenantID:              tenantID,
		Status:                entity.InvoiceStatus_CONFIRMED,
		Type:                  entity.InvoiceType_COMMITMENT,
		InvoiceDate:           time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
		CostPrice:             decimal.RequireFromString("1.37"),
		BindedBillingConfigID: billingConfigID,
	}

	assert.NoError(invoiceEntity.Create(db.GetDB()))
	inv, err := invoiceModel.SendInvoice(1, 1, invoiceEntity.ID)
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("0.37"), inv.ChannelLoss)
	assert.Equal(billingConfigID, *inv.BindedBillingConfigID)
}

func (suite *InvoiceSuite) TestSendTenantAllInvoices() {
	tests.MockPaymentHandler()
	tests.MockCentralHandler()
	assert := suite.Assert()
	const tenantID uint = 1
	billingConfigID := uint(1)

	paymentModel := &paymentsub.PaymentModel{}
	_, err := paymentModel.CreatePaymentMethodAndActive(
		tenantID,
		&forms.PaymentMethodCreateQuery{
			PaymentType:           "mock",
			BindedBillingConfigID: &billingConfigID,
		},
	)
	assert.NoError(err)
	err = (&paymentsub.BillingInformationModel{}).CreateBillingCompany(&forms.BillingCompany{
		CompanyKey:     config.Get().GetString(config.DefaultBillingCompanyKey),
		Name:           "PINGCAP",
		PaymentSubCode: "",
	})
	assert.NoError(err)

	invoiceEntity := entity.Invoice{
		TenantID:              tenantID,
		Status:                entity.InvoiceStatus_CONFIRMED,
		Type:                  entity.InvoiceType_MONTHLY,
		InvoiceDate:           time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
		CostPrice:             decimal.NewFromInt(1),
		BindedBillingConfigID: billingConfigID,
	}

	assert.NoError(invoiceEntity.Create(db.GetDB()))
	//first check SendAllUnsentInvoice no sent invoice.
	err = invoiceModel.SendAllUnsentInvoice(tenantID, 1)
	assert.NoError(err)

	invoice, err := invoiceModel.GetInvoice(invoiceEntity.ID)
	assert.NoError(err)
	assert.Equal(invoice.Status, entity.InvoiceStatus_SENT)

	invoiceEntity2 := entity.Invoice{
		TenantID:              tenantID,
		Status:                entity.InvoiceStatus_CONFIRMED,
		Type:                  entity.InvoiceType_MONTHLY,
		InvoiceDate:           time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
		CostPrice:             decimal.NewFromInt(1),
		BindedBillingConfigID: billingConfigID,
	}
	assert.NoError(invoiceEntity2.Create(db.GetDB()))
	//second check SendAllUnsentInvoice tenant have sent invoice.
	err = invoiceModel.SendAllUnsentInvoice(tenantID, 1)
	assert.NoError(err)

}
func (suite *InvoiceSuite) TestDeleteInvoice() {
	assert := suite.Assert()
	invoice, err := datainit.CreateInvoice(uint(1))
	assert.NoError(err)
	assert.Equal(invoice.Status, entity.InvoiceStatus_GENERATED)
	assert.Zero(invoice.CostPrice.Cmp(decimal.NewFromInt(1)))
	err = invoiceModel.DeleteInvoice(invoice.InvoiceID)
	assert.NoError(err)
	var billingDate = time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC)
	_, err = invoiceModel.GetInvoice(invoice.InvoiceID)
	assert.ErrorIs(err, billing_errors.ObjectNotExistsError)
	info, err := invoiceModel.CreateMonthlyInvoice(uint(1), 1, billingDate)
	assert.NoError(err)
	assert.Equal(info.Status, entity.InvoiceStatus_GENERATED)
	assert.Zero(info.CostPrice.Cmp(decimal.NewFromInt(1)))
}

func (suite *InvoiceSuite) TestCreateCommitmentInvoice() {
	assert := suite.Assert()
	billingConfigID := uint(1)

	invoice, err := invoiceModel.CreateCommitmentInvoice(1, &forms.CommitmentInvoiceInput{
		Amount:                decimal.RequireFromString("1234.56"),
		Date:                  datainit.BillingDate,
		Owner:                 "test@pingcap.com",
		BindedBillingConfigID: &billingConfigID,
	})
	assert.NoError(err)

	assert.Equal(invoice.CostPrice, decimal.RequireFromString("1234.56"))
	assert.Equal(invoice.RawPrice, decimal.RequireFromString("1234.56"))
	assert.Equal(invoice.StartDate, datainit.BillingDate)
	assert.Equal(invoice.EndDate, datainit.BillingDate)
	assert.Equal(invoice.Type, entity.InvoiceType_COMMITMENT)
	assert.Equal(invoice.Status, entity.InvoiceStatus_GENERATED)

	record := &entity.CommitmentRecords{}
	err = entity.NewCommitmentRecordsQuerySet(db.GetDB()).One(record)
	assert.NoError(err)
	assert.Equal(record.Owner, "test@pingcap.com")
	assert.Equal(record.Date, datainit.BillingDate)
	assert.Equal(record.Amount, decimal.RequireFromString("1234.56"))

	invoice, err = invoiceModel.GetInvoice(invoice.InvoiceID)
	assert.NoError(err)

	assert.Equal(invoice.CostPrice, decimal.RequireFromString("1234.56"))
	assert.Equal(invoice.RawPrice, decimal.RequireFromString("1234.56"))
	assert.Equal(invoice.StartDate, datainit.BillingDate)
	assert.Equal(invoice.EndDate, datainit.BillingDate)
	assert.Equal(invoice.Type, entity.InvoiceType_COMMITMENT)
	assert.Equal(invoice.Status, entity.InvoiceStatus_GENERATED)
}

func (suite *InvoiceSuite) TestSendMultiInvoiceOnce() {
	tests.MockPaymentHandler()
	tests.MockCentralHandler()
	assert := suite.Assert()
	const tenantID uint = 1
	billingConfigID := uint(1)

	paymentModel := &paymentsub.PaymentModel{}
	_, err := paymentModel.CreatePaymentMethodAndActive(
		tenantID,
		&forms.PaymentMethodCreateQuery{
			PaymentType:           "mock",
			BindedBillingConfigID: &billingConfigID,
		},
	)
	assert.NoError(err)
	err = (&paymentsub.BillingInformationModel{}).CreateBillingCompany(&forms.BillingCompany{
		CompanyKey:     config.Get().GetString(config.DefaultBillingCompanyKey),
		Name:           "PINGCAP",
		PaymentSubCode: "",
	})
	assert.NoError(err)
	provider, err := paymenthandler.GetPaymentHandler().GetProvider("mock")
	assert.NoError(err)
	providerImpl, ok := provider.(*tests.MockPaymentProviderImpl)
	assert.True(ok)
	providerImpl.ExpectedCalls = nil
	providerImpl.On("NeedMultiInvoiceSendOnce").Return(true)
	providerImpl.On("PayMultiInvoice", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
	providerImpl.On("Pay", mock.Anything, mock.Anything, mock.Anything).Return(billing_errors.SystemError)

	invoiceEntity := entity.Invoice{
		TenantID:              tenantID,
		Status:                entity.InvoiceStatus_CONFIRMED,
		Type:                  entity.InvoiceType_MONTHLY,
		InvoiceDate:           time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
		CostPrice:             decimal.NewFromInt(1),
		BindedBillingConfigID: billingConfigID,
	}

	assert.NoError(invoiceEntity.Create(db.GetDB()))

	invoiceEntity1 := entity.Invoice{
		TenantID:              tenantID,
		Status:                entity.InvoiceStatus_CONFIRMED,
		Type:                  entity.InvoiceType_MONTHLY,
		InvoiceDate:           time.Date(2021, 11, 1, 0, 0, 0, 0, time.UTC),
		CostPrice:             decimal.NewFromInt(1),
		BindedBillingConfigID: billingConfigID,
	}

	assert.NoError(invoiceEntity1.Create(db.GetDB()))

	err = invoiceModel.SendAllUnsentInvoice(tenantID, 1)
	assert.NoError(err)

	info, err := invoiceModel.GetInvoice(invoiceEntity.ID)
	assert.NoError(err)
	assert.Equal(info.Status, entity.InvoiceStatus_SENT)

	info, err = invoiceModel.GetInvoice(invoiceEntity1.ID)
	assert.NoError(err)
	assert.Equal(info.Status, entity.InvoiceStatus_SENT)

}

func (suite *InvoiceSuite) TestCreateInvoice() {
	var err error
	assert := suite.Assert()
	tenantID := uint(1)
	err = datainit.CreateMeasures(tenantID)
	assert.NoError(err)
	info, err := invoiceModel.CreateConfirmedRecalInvoice(tenantID, 1, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(entity.InvoiceType_Recal, info.Type)
	assert.Equal(entity.InvoiceStatus_CONFIRMED, info.Status)
}

type MockMarketplacePaymentProvider struct {
	tests.MockPaymentProviderImpl
}

func (*MockMarketplacePaymentProvider) EnableTaxCalculation() bool {
	return false
}

func (m *MockMarketplacePaymentProvider) PayMultiInvoice(paymentInfo *forms.PaymentMethod, billingCompany *forms.BillingCompany, invoices []forms.Invoice) ([]forms.Invoice, error) {
	return invoices, nil
}

func (m *MockMarketplacePaymentProvider) NeedMultiInvoiceSendOnce() bool {
	return true
}

type MockPaymentHandler1 struct {
	Provider paymenthandler.PaymentProvider
}

func (m *MockPaymentHandler1) GetProvider(string) (paymenthandler.PaymentProvider, error) {
	return m.Provider, nil
}

func (m *MockPaymentHandler1) AllProviders() []string {
	return []string{"mock"}
}

type MockCentralHandlerImpl1 struct {
	tests.MockCentralHandlerImpl
}

func (*MockCentralHandlerImpl1) GetTenant(tenantID uint) (*forms.Tenant, error) {
	return &forms.Tenant{
		TenantID:                  tenantID,
		ThirdPartyAccountProvider: "mock",
		Status:                    centralhandler.TenantStatusActive,
	}, nil
}

func (suite *InvoiceSuite) TestMarketplaceUnsubscribedProcess() {
	var err error
	assert := suite.Assert()
	tenantID := uint(1)
	timeutils.SetMockNow(datainit.BillingDate)
	m := &MockMarketplacePaymentProvider{}
	billingConfigID, err := (&billingconfig.BillingConfigModel{}).GetActiveBillingConfigID(tenantID)
	assert.NoError(err)
	paymenthandler.SetPaymentHandler(&MockPaymentHandler1{Provider: m})
	centralhandler.SetCentralHandler(&MockCentralHandlerImpl1{})
	_, err = (&paymentsub.PaymentModel{}).CreatePaymentMethodAndActive(tenantID, &forms.PaymentMethodCreateQuery{PaymentType: "mock", BindedBillingConfigID: &billingConfigID})
	assert.NoError(err)
	assert.NoError(datainit.CreateCompany())
	assert.NoError(datainit.CreatePricePackageAndBind(tenantID))
	assert.NoError(datainit.SendMeasureWithTime(tenantID, datainit.BillingDate.AddDate(0, 0, -3), 1))
	assert.NoError(datainit.SendMeasureWithTime(tenantID, datainit.BillingDate, 1))
	err = invoiceModel.MarketplaceUnsubscribedProcess(tenantID)
	assert.NoError(err)
	invoices, err := invoiceModel.ListAllInvoicesWithType(tenantID, billingConfigID, true, entity.InvoiceType_COSTQUOTA)
	assert.NoError(err)
	assert.Len(invoices, 2)
	assert.Equal(datainit.BillingDate, invoices[0].StartDate)
	assert.Equal(datainit.BillingDate.AddDate(0, -1, 0), invoices[1].StartDate)
	assert.NoError(datainit.SendMeasureWithTime(tenantID, datainit.BillingDate.AddDate(0, 0, 3), 1))
	inv, err := invoiceModel.CreateMonthlyInvoice(tenantID, billingConfigID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(decimal.NewFromInt(1), inv.CostPrice)
}
