package tests

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/api"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/model/payment"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/pingcap/dbaas/billing2/utils/tests/datainit"
	"github.com/shopspring/decimal"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/suite"
)

type CostExplorerTestSuite struct {
	tests.BillingTestSuite
	router *gin.Engine
}

func (suite *CostExplorerTestSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	suite.router = gin.New()
	suite.router.Use(ginutils.ErrorMiddleWare)
	api.InitializeCostExplorerRouter(ginutils.MyRoute{Route: suite.router})
}

func TestCostExplorerSuite(t *testing.T) {
	suite.Run(t, new(CostExplorerTestSuite))
}

func (suite *CostExplorerTestSuite) TestCostExplorerQuery() {
	assert := suite.Assert()
	datainit.CreateInvoice(uint(1))
	query := &forms.CostExplorerQuery{
		StartDate: datainit.BillingDate,
		EndDate:   datainit.BillingDate.Add(time.Hour),
	}
	w := tests.PerformRequest(suite.router, "POST", "/tenants/1/costexplorer", query)
	assert.Equal(http.StatusOK, w.Code)
	res := &forms.CostExplorerResult{}
	assert.NoError(json.Unmarshal(w.Body.Bytes(), res))
	assert.Equal(res.BindedInvoiceCosts.CostPrice, decimal.NewFromInt(1))

}

func (suite *CostExplorerTestSuite) TestCostExplorerMontlyQuery() {
	assert := suite.Assert()
	datainit.CreateInvoice(uint(1))
	path := fmt.Sprintf("/tenants/1/costexplorer/monthly_cost/year/%d/month/%d", datainit.BillingDate.Year(), datainit.BillingDate.Month())
	w := tests.PerformRequest(suite.router, "GET", path, nil)
	assert.Equal(http.StatusOK, w.Code, w.Body.String())
	res := &forms.MonthlycostExplorerResult{}
	assert.NoError(json.Unmarshal(w.Body.Bytes(), res))
	assert.Equal(res.BindedInvoiceCosts.CostPrice, decimal.NewFromInt(1))
	assert.Equal(res.InvoiceGenerated, true)

	path = fmt.Sprintf("/tenants/1/costexplorer/monthly_cost/year/%d/month/%d", datainit.BillingDate.Year(), datainit.BillingDate.Month()+1)
	w = tests.PerformRequest(suite.router, "GET", path, nil)
	assert.Equal(http.StatusOK, w.Code, w.Body.String())
	res = &forms.MonthlycostExplorerResult{}
	assert.NoError(json.Unmarshal(w.Body.Bytes(), res))
	assert.Equal(res.InvoiceGenerated, true)

}

func (suite *CostExplorerTestSuite) TestCostExplorerMontlyQueryNotGenearted() {
	assert := suite.Assert()
	datainit.CreateMeasures(uint(1))
	path := fmt.Sprintf("/tenants/1/costexplorer/monthly_cost/year/%d/month/%d", datainit.BillingDate.Year(), datainit.BillingDate.Month()+1)
	w := tests.PerformRequest(suite.router, "GET", path, nil)
	assert.Equal(http.StatusOK, w.Code, w.Body.String())
	res := &forms.MonthlycostExplorerResult{}
	assert.NoError(json.Unmarshal(w.Body.Bytes(), res))
	assert.Equal(res.UnbindedInvoiceCosts.CostPrice, decimal.Zero)
	assert.Equal(res.InvoiceGenerated, false)
}

func (suite *CostExplorerTestSuite) TestCostExplorerMontlyQueryExport() {
	assert := suite.Assert()

	viper.Set(config.KeeyShowingCategory, "")
	_, err := (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:  "test_unitA",
		Category: "category_A",
	})
	assert.NoError(err)

	_, err = (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:  "test_unitB",
		Category: "category_B",
	})
	assert.NoError(err)

	var billingDate = datainit.BillingDate

	measureModel := &billing.MeasureModel{}
	const tenantID uint = 1
	assert.NoError(datainit.CreatePaymentMock(tenantID))
	packageInfo := &forms.PackageInfo{
		UnitPrices: forms.UnitPrices{
			"test_unitA": {Price: decimal.RequireFromString("0.001")},
			"test_unitB": {Price: decimal.RequireFromString("0.001")},
		},
		Discount: decimal.NewFromInt(1),
	}
	info1, err := (&billing.PackageModel{}).CreatePackage(packageInfo)
	assert.NoError(err)

	bindedInfo1, _ := (&billing.BindedPackageModel{}).BindPackage(tenantID, info1.PackageID)
	bindedID1 := bindedInfo1.BindedPackageID
	_ = bindedID1
	unitCosts := forms.UnitCosts{
		CostTimeStamp: billingDate.Unix(),
		Unit:          "test_unitA",
		Cost:          1004,
		Labels:        map[string]string{},
	}

	unitCosts.Labels["project"] = "A"
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	unitCosts.Labels["project"] = "B"
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	unitCosts.Labels["project"] = "C"
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)

	unitCosts.Unit = "test_unitB"

	unitCosts.Labels["project"] = "A"
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	unitCosts.Labels["project"] = "B"
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)
	unitCosts.Labels["project"] = "C"
	_, err = measureModel.CostUnits(tenantID, &unitCosts)
	assert.NoError(err)

	path := fmt.Sprintf("/tenants/1/costexplorer/monthly_cost/year/%d/month/%d/export", datainit.BillingDate.Year(), datainit.BillingDate.Month())
	w := tests.PerformRequest(suite.router, "POST", path, nil)
	assert.Equal(http.StatusOK, w.Code, w.Body.String())
	records, err := csv.NewReader(w.Body).ReadAll()
	assert.NoError(err)
	lastLine := records[len(records)-1]
	lastValue := lastLine[len(lastLine)-1]
	assert.Equal(lastValue, "6")
	assert.Equal(records[0][0], "Project")
	assert.Equal(records[0][1], "category_A")
	assert.Equal(records[0][2], "category_B")
	assert.Equal(records[0][3], "Total")
	assert.Equal(records[1][0], "A")
	assert.Equal(records[2][0], "B")
	assert.Equal(records[3][0], "C")
	assert.Equal(records[4][0], "Total")

	invoiceModel := &payment.InvoiceModel{}

	_, err = invoiceModel.CreateMonthlyInvoice(uint(1), datainit.BillingDate)
	assert.NoError(err)
	w = tests.PerformRequest(suite.router, "POST", path, nil)
	records, err = csv.NewReader(w.Body).ReadAll()
	assert.NoError(err)
	lastLine = records[len(records)-1]
	lastValue = lastLine[len(lastLine)-1]
	assert.Equal(lastValue, "6")
}
