package tests

import (
	"testing"

	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/stretchr/testify/suite"
)

var subscriptionModel = &billing.SubscriptionModel{}

type SubscriptionTestSuite struct {
	tests.BillingTestSuite
}

func TestSubscriptionSuite(t *testing.T) {
	suite.Run(t, new(SubscriptionTestSuite))
}

func (s *SubscriptionTestSuite) TestCRUDSubscription() {
	assert := s.Assert()
	subscription := &forms.Subscription{
		FilePath:      "file_path",
		ContractEmail: "contract_email",
	}
	tenantID := uint(1)
	info, err := subscriptionModel.CreateSubscription(tenantID, subscription)
	assert.NoError(err)
	assert.Equal(info.Status, entity.SubscriptionStatus_DRAFT)
	assert.NotEqualValues(info.SubscriptionID, 0)
	assert.Equal(info.TenantID, tenantID)
	assert.Equal(info.ContractEmail, "contract_email")
	assert.Equal(info.FilePath, "file_path")
	assert.Nil(info.AcceptedUser)
	assert.Nil(info.AcceptedDate)

	info, err = subscriptionModel.GetSubscription(info.SubscriptionID)
	assert.NoError(err)
	assert.Equal(info.Status, entity.SubscriptionStatus_DRAFT)
	assert.NotEqualValues(info.SubscriptionID, 0)
	assert.Equal(info.TenantID, tenantID)
	assert.Equal(info.ContractEmail, "contract_email")
	assert.Equal(info.FilePath, "file_path")
	assert.Nil(info.AcceptedUser)
	assert.Nil(info.AcceptedDate)

	err = subscriptionModel.DeleteSubscription(info.SubscriptionID)
	assert.NoError(err)
	_, err = subscriptionModel.GetSubscription(info.SubscriptionID)
	assert.ErrorIs(err, billing_errors.ObjectNotExistsError)
}

func (s *SubscriptionTestSuite) TestUpdateSubscrption() {
	assert := s.Assert()
	subscription := &forms.Subscription{
		FilePath:      "file_path",
		ContractEmail: "contract_email",
	}
	tenantID := uint(1)
	info, err := subscriptionModel.CreateSubscription(tenantID, subscription)
	assert.NoError(err)
	info.FilePath = "new_file_path"
	updatedInfo, err := subscriptionModel.UpdateSubscription(info.SubscriptionID, info)
	assert.NoError(err)
	assert.Equal(updatedInfo.FilePath, "new_file_path")
	assert.Equal(updatedInfo.Status, entity.SubscriptionStatus_DRAFT)

	_, err = subscriptionModel.SubmitSubscription(info.SubscriptionID)
	assert.NoError(err)
	updatedInfo, err = subscriptionModel.UpdateSubscription(info.SubscriptionID, info)
	assert.NoError(err)
	assert.Equal(updatedInfo.Status, entity.SubscriptionStatus_DRAFT)

	_, err = subscriptionModel.SubmitSubscription(info.SubscriptionID)
	assert.NoError(err)
	_, err = subscriptionModel.AcceptSubscription(info.SubscriptionID, &forms.SubscriptionChange{})
	assert.NoError(err)
	_, err = subscriptionModel.UpdateSubscription(info.SubscriptionID, info)
	assert.Error(err, billing_errors.InvalidInputError)
}

func (s *SubscriptionTestSuite) TestSubmitAndAcceptSubscription() {
	assert := s.Assert()
	subscription := &forms.Subscription{
		FilePath:      "file_path",
		ContractEmail: "contract_email",
	}
	tenantID := uint(1)
	info, err := subscriptionModel.CreateSubscription(tenantID, subscription)
	assert.NoError(err)
	info, err = subscriptionModel.SubmitSubscription(info.SubscriptionID)
	assert.NoError(err)
	assert.Equal(info.Status, entity.SubscriptionStatus_PENDING)
	info, err = subscriptionModel.AcceptSubscription(info.SubscriptionID, &forms.SubscriptionChange{})
	assert.NoError(err)
	assert.Equal(info.Status, entity.SubscriptionStatus_ACCEPTED)
}

func (s *SubscriptionTestSuite) TestSubmitAndRejectSubscription() {
	assert := s.Assert()
	subscription := &forms.Subscription{
		FilePath:      "file_path",
		ContractEmail: "contract_email",
	}
	tenantID := uint(1)
	info, err := subscriptionModel.CreateSubscription(tenantID, subscription)
	assert.NoError(err)
	info, err = subscriptionModel.SubmitSubscription(info.SubscriptionID)
	assert.NoError(err)
	assert.Equal(info.Status, entity.SubscriptionStatus_PENDING)
	info, err = subscriptionModel.RejectSubscription(info.SubscriptionID, &forms.SubscriptionChange{
		ChangeUser: "test_user",
	})
	assert.NoError(err)
	assert.Equal(info.Status, entity.SubscriptionStatus_REJECTED)
	assert.Equal(*info.RejectedUser, "test_user")
}

func (s *SubscriptionTestSuite) TestSuspendSubscription() {
	assert := s.Assert()
	subscription := &forms.Subscription{
		FilePath:      "file_path",
		ContractEmail: "contract_email",
	}
	tenantID := uint(1)
	info, err := subscriptionModel.CreateSubscription(tenantID, subscription)
	assert.NoError(err)
	info, err = subscriptionModel.SubmitSubscription(info.SubscriptionID)
	assert.NoError(err)
	assert.Equal(info.Status, entity.SubscriptionStatus_PENDING)
	info, err = subscriptionModel.AcceptSubscription(info.SubscriptionID, &forms.SubscriptionChange{
		ChangeUser: "test_user",
	})
	assert.NoError(err)
	info, err = subscriptionModel.SuspendSubscription(info.SubscriptionID)
	assert.NoError(err)
	assert.Equal(info.Status, entity.SubscriptionStatus_SUSPENDED)
}

func (s *SubscriptionTestSuite) TestCanNotDeleteAcceptedSubscription() {
	assert := s.Assert()
	subscription := &forms.Subscription{
		FilePath:      "file_path",
		ContractEmail: "contract_email",
	}
	tenantID := uint(1)
	info, err := subscriptionModel.CreateSubscription(tenantID, subscription)
	assert.NoError(err)
	info, err = subscriptionModel.SubmitSubscription(info.SubscriptionID)
	assert.NoError(err)
	assert.Equal(info.Status, entity.SubscriptionStatus_PENDING)
	info, err = subscriptionModel.AcceptSubscription(info.SubscriptionID, &forms.SubscriptionChange{})
	assert.NoError(err)
	assert.Equal(info.Status, entity.SubscriptionStatus_ACCEPTED)

	err = subscriptionModel.DeleteSubscription(info.SubscriptionID)
	assert.ErrorIs(err, billing_errors.InvalidInputError)
}

func (s *SubscriptionTestSuite) TestListSubscription() {
	assert := s.Assert()
	tenantID := uint(1)
	for i := 0; i < 10; i++ {
		subscription := &forms.Subscription{
			FilePath:      "file_path",
			ContractEmail: "contract_email",
		}
		_, err := subscriptionModel.CreateSubscription(tenantID, subscription)
		assert.NoError(err)
	}

	infos, err := subscriptionModel.ListSubscriptions(tenantID)
	assert.NoError(err)
	assert.Len(infos, 10)
}

func (s *SubscriptionTestSuite) TestListSubmitedSubscription() {
	assert := s.Assert()
	tenantID := uint(1)
	for i := 0; i < 3; i++ {
		subscription := &forms.Subscription{
			FilePath:      "file_path",
			ContractEmail: "contract_email",
		}
		_, err := subscriptionModel.CreateSubscription(tenantID, subscription)
		assert.NoError(err)
	}
	for i := 0; i < 3; i++ {
		subscription := &forms.Subscription{
			FilePath:      "file_path",
			ContractEmail: "contract_email",
		}
		info, err := subscriptionModel.CreateSubscription(tenantID, subscription)
		assert.NoError(err)
		info, err = subscriptionModel.SubmitSubscription(info.SubscriptionID)
		assert.NoError(err)
		_ = info
	}
	for i := 0; i < 3; i++ {
		subscription := &forms.Subscription{
			FilePath:      "file_path",
			ContractEmail: "contract_email",
		}
		info, err := subscriptionModel.CreateSubscription(tenantID, subscription)
		assert.NoError(err)
		info, err = subscriptionModel.SubmitSubscription(info.SubscriptionID)
		assert.NoError(err)
		info, err = subscriptionModel.AcceptSubscription(info.SubscriptionID, &forms.SubscriptionChange{})
		assert.NoError(err)
		_ = info
	}

	infos, err := subscriptionModel.ListSubmitedSubscriptions(tenantID)
	assert.NoError(err)
	assert.Len(infos, 6)
}
