package tests

import (
	"testing"
	"time"

	"github.com/pingcap/dbaas/billing2/db"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/model/payment/reconciliation"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/suite"
	"gorm.io/gorm"
)

type ReconciliationInvoiceSuite struct {
	tests.BillingTestSuite
}

func TestReconciliationInvoiceSuite(t *testing.T) {
	suite.Run(t, new(ReconciliationInvoiceSuite))
}

var oct = time.Date(2023, 10, 3, 17, 0, 0, 0, time.UTC)
var sep = time.Date(2023, 9, 17, 0, 0, 0, 0, time.UTC)

func (suite *ReconciliationInvoiceSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	assert := suite.Assert()
	payments := []*entity.PaymentMethod{
		{Model: gorm.Model{ID: 1}, TenantID: 1, PaymentType: entity.PaymentType_AWS},
		{Model: gorm.Model{ID: 2}, TenantID: 1, PaymentType: entity.PaymentType_GCP},
		{Model: gorm.Model{ID: 3}, TenantID: 2, PaymentType: entity.PaymentType_STRIPE},
		{Model: gorm.Model{ID: 4}, TenantID: 2, PaymentType: entity.PaymentType_GCP},
		{Model: gorm.Model{ID: 5}, TenantID: 3, PaymentType: entity.PaymentType_GCP, ManagedBySales: true},
	}
	for _, ent := range payments {
		err := ent.Create(db.GetDB())
		assert.NoError(err)
	}
	oct2th := time.Date(2023, 10, 3, 7, 0, 0, 0, time.UTC)
	invoices := []*entity.Invoice{
		{
			TenantID:        1,
			InvoiceDate:     time.Date(2023, 10, 1, 0, 0, 0, 0, time.UTC),
			SentDate:        &oct,
			Status:          entity.InvoiceStatus_SENT,
			CostPrice:       decimal.RequireFromString("10"),
			PaymentMethodID: &payments[0].ID,
		},
		{
			TenantID:        1,
			InvoiceDate:     time.Date(2023, 10, 2, 0, 0, 0, 0, time.UTC),
			CostPrice:       decimal.RequireFromString("10"),
			SentDate:        &oct,
			Status:          entity.InvoiceStatus_SENT,
			PaymentMethodID: &payments[0].ID,
		},
		{
			TenantID:        1,
			InvoiceDate:     time.Date(2023, 9, 29, 0, 0, 0, 0, time.UTC),
			CostPrice:       decimal.RequireFromString("10"),
			SentDate:        &oct,
			Status:          entity.InvoiceStatus_SENT,
			PaymentMethodID: &payments[0].ID,
		},
		{
			TenantID:        1,
			InvoiceDate:     time.Date(2023, 9, 26, 0, 0, 0, 0, time.UTC),
			CostPrice:       decimal.RequireFromString("10"),
			SentDate:        &sep,
			Status:          entity.InvoiceStatus_SENT,
			PaymentMethodID: &payments[0].ID,
		},
		{
			TenantID:        2,
			InvoiceDate:     time.Date(2023, 10, 1, 0, 0, 0, 0, time.UTC),
			CostPrice:       decimal.RequireFromString("10"),
			SentDate:        &oct,
			Status:          entity.InvoiceStatus_SENT,
			PaymentMethodID: &payments[2].ID,
		},
		{
			TenantID:        2,
			InvoiceDate:     time.Date(2023, 10, 2, 0, 0, 0, 0, time.UTC),
			CostPrice:       decimal.RequireFromString("10"),
			SentDate:        &oct,
			Status:          entity.InvoiceStatus_SENT,
			PaymentMethodID: &payments[3].ID,
		},
		{
			TenantID:        2,
			InvoiceDate:     time.Date(2023, 10, 3, 0, 0, 0, 0, time.UTC),
			CostPrice:       decimal.RequireFromString("10"),
			SentDate:        &oct,
			PaymentMethodID: &payments[3].ID,
		},
		{
			TenantID:        3,
			InvoiceDate:     time.Date(2023, 10, 3, 0, 0, 0, 0, time.UTC),
			CostPrice:       decimal.RequireFromString("10"),
			SentDate:        &oct,
			Status:          entity.InvoiceStatus_SENT,
			PaymentMethodID: &payments[4].ID,
		},
		{
			TenantID:        3,
			InvoiceDate:     time.Date(2023, 10, 2, 16, 0, 0, 0, time.UTC),
			CostPrice:       decimal.RequireFromString("18"),
			Type:            entity.InvoiceType_COMMITMENT,
			Status:          entity.InvoiceStatus_SENT,
			SentDate:        &oct2th,
			PaymentMethodID: &payments[4].ID,
		},
	}
	for _, ent := range invoices {
		err := ent.Create(db.GetDB())
		assert.NoError(err)
	}
}

func (suite *ReconciliationInvoiceSuite) TestGetDailyTotalInvoice() {
	assert := suite.Assert()
	res, err := reconciliation.GetDailyTotalInvoice(time.Date(2023, 10, 3, 0, 0, 0, 0, time.UTC), entity.PaymentType_AWS)
	assert.NoError(err)
	assert.Len(res, 1)
	reconciliationInvoice := res[0]
	assert.Equal(decimal.RequireFromString("30"), reconciliationInvoice.Invoice)
	res, err = reconciliation.GetDailyTotalInvoice(time.Date(2023, 10, 2, 0, 0, 0, 0, time.UTC), entity.PaymentType_GCP)
	assert.NoError(err)
	assert.Len(res, 1)
	assert.Equal(decimal.RequireFromString("18"), res[0].Invoice)
	res, err = reconciliation.GetDailyTotalInvoice(time.Date(2023, 10, 3, 0, 0, 0, 0, time.UTC), entity.PaymentType_GCP)
	assert.NoError(err)
	assert.Len(res, 1)
	assert.Equal(decimal.RequireFromString("10"), res[0].Invoice)
}

func (suite *ReconciliationInvoiceSuite) TestGetMonthlyTotalInvoice() {
	assert := suite.Assert()
	res, err := reconciliation.GetMonthlyTotalInvoice(oct, entity.PaymentType_AWS)
	assert.NoError(err)
	assert.Len(res, 1)
	reconciliationInvoice := res[0]
	assert.Equal(decimal.RequireFromString("30"), reconciliationInvoice.Invoice)
	assert.Equal(decimal.RequireFromString("20"), reconciliationInvoice.InvoiceDetail[oct.Format("2006-01")])
	assert.Equal(decimal.RequireFromString("10"), reconciliationInvoice.InvoiceDetail[sep.Format("2006-01")])
	res, err = reconciliation.GetMonthlyTotalInvoice(oct, entity.PaymentType_GCP)
	assert.NoError(err)
	assert.Len(res, 2)
	for _, i := range res {
		if i.TenantID == 3 {
			assert.Equal(decimal.RequireFromString("18"), i.Invoice)
			assert.Equal(decimal.RequireFromString("18"), i.InvoiceDetail[oct.Format("2006-01")])
		} else {
			assert.Equal(decimal.RequireFromString("10"), i.Invoice)
			assert.Equal(decimal.RequireFromString("10"), i.InvoiceDetail[oct.Format("2006-01")])
		}
	}
}
