package models

import (
	"testing"

	"github.com/stretchr/testify/assert"
	dedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
)

func TestClusterMeta_GenerateBillingReport(t *testing.T) {
	// available cluster
	clusterMeta := ClusterMeta{
		TenantID:  "111",
		Region:    "us-west-2",
		ProjectID: "222",
		ClusterID: "333",
		Cluster:   "cluster333",
		DevTier:   false,
		Poc:       false,
		Provider:  "aws",
		Components: []*ComponentMeta{
			{
				Component:     "tidb",
				ComponentType: "COMPONENT_TYPE_TIDB",
				Count:         2,
				TiDBGroupName: "group1",
				InstanceType:  "m5.xlarge",
				CpuNum:        2,
				MemoryGiB:     8,
				State:         1,
				NodeSKU:       "[{\"skuName\":\"node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.xlarge\",\"skuValue\":\"1\",\"count\":2}]",
				StoragesSKU:   "",
			},
			{
				Component:       "tikv",
				ComponentType:   "COMPONENT_TYPE_TIKV",
				Count:           2,
				InstanceType:    "m5.2xlarge",
				CpuNum:          3,
				MemoryGiB:       8,
				StorageID:       "11111",
				VolumeType:      "GP2",
				StorageSizeGi:   1000,
				Iops:            3000,
				ThroughputMibps: 400,
				State:           1,
				NodeSKU:         "[{\"skuName\":\"node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.2xlarge\",\"skuValue\":\"1\",\"count\":2}]",
				StoragesSKU:     "[{\"skuName\":\"disk-iops:aws:us-west-2:gp2\",\"skuValue\":\"3000\",\"count\":2},{\"skuName\":\"disk-throughput:aws:us-west-2:gp2\",\"skuValue\":\"400\",\"count\":2},{\"skuName\":\"disk-storage:aws:us-west-2:gp2\",\"skuValue\":\"1000\",\"count\":2}]",
			},
			{
				Component:       "tiflash",
				ComponentType:   "COMPONENT_TYPE_TIFLASH",
				Count:           1,
				InstanceType:    "m5.4xlarge",
				CpuNum:          2,
				MemoryGiB:       8,
				StorageID:       "11111",
				VolumeType:      "GP3",
				StorageSizeGi:   2000,
				Iops:            3000,
				ThroughputMibps: 400,
				State:           1,
				NodeSKU:         "[{\"skuName\":\"node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.4xlarge\",\"skuValue\":\"1\",\"count\":1}]",
				StoragesSKU:     "[{\"skuName\":\"disk-iops:aws:us-west-2:gp3\",\"skuValue\":\"3000\",\"count\":1},{\"skuName\":\"disk-throughput:aws:us-west-2:gp3\",\"skuValue\":\"400\",\"count\":1},{\"skuName\":\"disk-storage:aws:us-west-2:gp3\",\"skuValue\":\"2000\",\"count\":1}]",
			},
			{
				Component:     "tiproxy",
				ComponentType: "COMPONENT_TYPE_TIPROXY",
				Count:         2,
				TiDBGroupName: "group1",
				InstanceType:  "m5.xlarge",
				CpuNum:        2,
				MemoryGiB:     8,
				State:         1,
				NodeSKU:       "[{\"skuName\":\"tiproxy:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.xlarge\",\"skuValue\":\"1\",\"count\":2}]",
				StoragesSKU:   "",
			},
		},
		IsScaling: false,
		State:     int32(dedicatedv1.Cluster_AVAILABLE),
	}
	reports, err := clusterMeta.GenerateBillingReport()
	assert.NoError(t, err)
	assert.Equal(t, 10, len(reports))
	reportsv2, err := clusterMeta.GenerateBillingReportV2()
	assert.NoError(t, err)
	assert.Equal(t, 10, len(reportsv2))
	assert.Equal(t, "node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.xlarge", reports[0].Unit)
	assert.Equal(t, int64(2), reports[0].Cost)
	assert.Equal(t, "node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.2xlarge", reports[1].Unit)
	assert.Equal(t, int64(2), reports[1].Cost)
	assert.Equal(t, "disk-iops:aws:us-west-2:gp2", reports[3].Unit)
	assert.Equal(t, int64(2), reports[3].Cost)
	assert.Equal(t, "node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.4xlarge", reports[5].Unit)
	assert.Equal(t, int64(1), reports[5].Cost)
	assert.Equal(t, "disk-storage:aws:us-west-2:gp3", reports[6].Unit)
	assert.Equal(t, int64(2000), reports[6].Cost)
	assert.Equal(t, "tiproxy:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.xlarge", reports[9].Unit)
	//for new sku
	assert.Equal(t, "node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.xlarge", reportsv2[0].Unit)
	assert.Equal(t, int64(2), reportsv2[0].Cost)
	assert.Equal(t, reportsv2[0].UnitValue, "1")
	assert.Equal(t, "node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.2xlarge", reportsv2[1].Unit)
	assert.Equal(t, reportsv2[1].UnitValue, "1")
	assert.Equal(t, int64(2), reportsv2[1].Cost)
	assert.Equal(t, "disk-iops:aws:us-west-2:gp2", reportsv2[2].Unit)
	assert.Equal(t, reportsv2[2].UnitValue, "3000")
	assert.Equal(t, int64(2), reportsv2[2].Cost)
	assert.Equal(t, "disk-throughput:aws:us-west-2:gp2", reportsv2[3].Unit)
	assert.Equal(t, reportsv2[3].UnitValue, "400")
	assert.Equal(t, int64(2), reportsv2[3].Cost)
	assert.Equal(t, "disk-storage:aws:us-west-2:gp2", reportsv2[4].Unit)
	assert.Equal(t, reportsv2[4].UnitValue, "1000")
	assert.Equal(t, int64(2), reportsv2[4].Cost)
	assert.Equal(t, "node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.4xlarge", reportsv2[5].Unit)
	assert.Equal(t, reportsv2[5].UnitValue, "1")
	assert.Equal(t, int64(1), reportsv2[5].Cost)
	assert.Equal(t, "disk-iops:aws:us-west-2:gp3", reportsv2[6].Unit)
	assert.Equal(t, reportsv2[6].UnitValue, "3000")
	assert.Equal(t, int64(1), reportsv2[6].Cost)
	assert.Equal(t, "disk-throughput:aws:us-west-2:gp3", reportsv2[7].Unit)
	assert.Equal(t, reportsv2[7].UnitValue, "400")
	assert.Equal(t, int64(1), reportsv2[7].Cost)
	assert.Equal(t, "disk-storage:aws:us-west-2:gp3", reportsv2[8].Unit)
	assert.Equal(t, reportsv2[8].UnitValue, "2000")
	assert.Equal(t, int64(1), reportsv2[8].Cost)
	assert.Equal(t, "tiproxy:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.xlarge", reportsv2[9].Unit)
	assert.Equal(t, reportsv2[9].UnitValue, "1")
	assert.Equal(t, int64(2), reportsv2[9].Cost)

	// pausing cluster
	clusterMeta.State = int32(dedicatedv1.Cluster_PAUSING)
	reports, err = clusterMeta.GenerateBillingReport()
	assert.NoError(t, err)
	assert.Equal(t, 6, len(reports))
	reportsv2, err = clusterMeta.GenerateBillingReportV2()
	assert.NoError(t, err)
	assert.Equal(t, 6, len(reportsv2))

	// deleted cluster
	clusterMeta.State = int32(dedicatedv1.Cluster_DELETED)
	reports, err = clusterMeta.GenerateBillingReport()
	assert.NoError(t, err)
	assert.Equal(t, 0, len(reports))
	reportsv2, err = clusterMeta.GenerateBillingReportV2()
	assert.NoError(t, err)
	assert.Equal(t, 0, len(reportsv2))

}

func TestSKUJsonConvert(t *testing.T) {
	// Test empty slice
	emptySkus := make([]*SKUJson, 0)
	str, err := SKUJsonToString(emptySkus)
	assert.NoError(t, err)
	assert.Equal(t, "[]", str)

	emptyResult, err := StringToSKUJson("")
	assert.NoError(t, err)
	assert.Equal(t, 0, len(emptyResult))

	// Test single SKU
	skus := []*SKUJson{
		{
			SKUName:  "node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m5.xlarge",
			SKUValue: "1",
			Count:    2,
		},
	}

	str, err = SKUJsonToString(skus)
	assert.NoError(t, err)

	result, err := StringToSKUJson(str)
	assert.NoError(t, err)
	assert.Equal(t, 1, len(result))
	assert.Equal(t, skus[0].SKUName, result[0].SKUName)
	assert.Equal(t, skus[0].SKUValue, result[0].SKUValue)
	assert.Equal(t, skus[0].Count, result[0].Count)

	// Test multiple SKUs
	skus = []*SKUJson{
		{
			SKUName:  "disk-iops:aws:us-west-2:gp2",
			SKUValue: "3000",
			Count:    2,
		},
		{
			SKUName:  "disk-throughput:aws:us-west-2:gp2",
			SKUValue: "400",
			Count:    2,
		},
		{
			SKUName:  "disk-storage:aws:us-west-2:gp2",
			SKUValue: "1000",
			Count:    2,
		},
	}

	str, err = SKUJsonToString(skus)
	assert.NoError(t, err)

	result, err = StringToSKUJson(str)
	assert.NoError(t, err)
	assert.Equal(t, 3, len(result))
	for i := range skus {
		assert.Equal(t, skus[i].SKUName, result[i].SKUName)
		assert.Equal(t, skus[i].SKUValue, result[i].SKUValue)
		assert.Equal(t, skus[i].Count, result[i].Count)
	}

	// Test invalid JSON
	_, err = StringToSKUJson("{invalid json}")
	assert.Error(t, err)
}
