package networkmetering

import (
	"context"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/metering/pkg/common/config"
	"github.com/tidbcloud/metering/pkg/common/constants"
	"github.com/tidbcloud/metering/pkg/common/models"
	"github.com/tidbcloud/metering/pkg/common/utils"
	"github.com/tidbcloud/metering/pkg/common/utils/bigquery"
	"github.com/tidbcloud/metering/pkg/common/utils/storage"
	"github.com/tidbcloud/metering/test/mockbigquery"
	"github.com/tidbcloud/metering/test/mockstorage"
	"go.uber.org/mock/gomock"
	"go.uber.org/zap"
)

func TestLoadAzure(t *testing.T) {
	ctrl := gomock.NewController(t)
	conf := &config.ServiceConfig{
		Vendor: constants.VendorAzure,
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			Provider: config.TrafficMeteringProvider{
				Azure: &config.TrafficMeteringProviderAzureConfig{
					BillingProvider: config.BillingProviderAzureConfig{
						StorageAccountName: "https://costmetetringstoragedev.blob.core.windows.net/",
						ContainerName:      "dataplane-cost-storage",
						Perfix: []string{
							"dataplane-cost-storage/dedicated-nonprod-dataplane-actual-cost",
						},
					},
				},
			},
			CachePath: "cachetest",
		},
	}
	cli := mockstorage.NewMockStorageProvider(ctrl)
	log, _ := zap.NewDevelopment()
	loader := &NetworkBillingLoad{
		cachepath:    "cachetest/billingcache",
		conf:         conf,
		objclis:      map[string]storage.StorageProvider{defaultClient: cli},
		cachebilling: make(map[string][]*models.AzureBilling),
		log:          log,
	}
	//prepare the mock cache path
	path, _ := loader.GetCachePathInTime(1723487507)
	path = filepath.Join(path, "dataplane-cost-storage/dedicated-nonprod-dataplane-actual-cost")
	absCachePath, err := filepath.Abs(path)
	assert.Nil(t, err)
	err = utils.EnsurePath(absCachePath)
	assert.Nil(t, err)
	// copy the test data to the cache path
	err = copyFile("../../testdata/azurebillingfile/part_0_0001.csv.gz", filepath.Join(absCachePath, "part_0_0001.csv.gz"))
	assert.Nil(t, err)
	err = copyFile("../../testdata/azurebillingfile/manifest.json", filepath.Join(absCachePath, "manifest.json"))
	assert.Nil(t, err)
	cli.EXPECT().ListObjectInTime(gomock.Any(), int64(1723487507), "dataplane-cost-storage/dedicated-nonprod-dataplane-actual-cost").Return([]string{
		"/dataplane-cost-storage/dedicated-nonprod-dataplane-actual-cost/20240801-20240831/manifest.json",
	}, nil)
	cli.EXPECT().DownloadObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	res, err := loader.LoadAzure(context.TODO(), 1723487507)
	assert.Nil(t, err)
	assert.Equal(t, 27, len(res))
	//clean up the test data
	err = os.RemoveAll(absCachePath)
	assert.Nil(t, err)
}

func TestLoadAWS(t *testing.T) {
	ctrl := gomock.NewController(t)
	conf := &config.ServiceConfig{
		Vendor: constants.VendorAWS,
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			Provider: config.TrafficMeteringProvider{
				AWS: &config.TrafficMeteringProviderAWSConfig{
					BillingProvider: []config.BillingProviderAWSConfig{{
						Account:    "123456",
						Region:     "us-east-1",
						BucketName: "dataplane-cost-storage",
						Perfix:     "dedicated-nonprod-dataplane-actual-cost",
						SourceType: "csv",
					},
					},
				},
			},
			CachePath: "cachetest",
		},
	}
	log, _ := zap.NewDevelopment()
	cli := mockstorage.NewMockStorageProvider(ctrl)
	loader := &NetworkBillingLoad{
		cachepath:      "cachetest/billingcache",
		conf:           conf,
		objclis:        map[string]storage.StorageProvider{getAWScliKey("123456", "dataplane-cost-storage"): cli},
		cachebilling:   make(map[string][]*models.AzureBilling),
		cacheawsloaded: make(map[string]bool),
		log:            log,
	}
	//prepare the mock cache path
	path, _ := loader.GetCachePathInTime(1723487507, "123456")
	absCachePath, err := filepath.Abs(path)
	assert.Nil(t, err)
	err = utils.EnsurePath(absCachePath)
	assert.Nil(t, err)
	// copy the test data to the cache path
	err = copyFile("../../testdata/awsbillingfile/cost-usage-report-00001.csv.gz", filepath.Join(absCachePath, "cost-usage-report-00001.csv.gz"))
	assert.Nil(t, err)
	cli.EXPECT().ListObjectInTime(gomock.Any(), int64(1723487507), "dedicated-nonprod-dataplane-actual-cost").Return([]string{
		"/dedicated-nonprod-dataplane-actual-cost/20240801-20240901/cost-usage-report-00001.csv.gz",
	}, nil)
	cli.EXPECT().DownloadObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
	res, err := loader.LoadAWS(context.TODO(), 1723487507)
	assert.Nil(t, err)
	assert.Equal(t, 2, len(res))
	//clean up the test data
	err = os.RemoveAll(absCachePath)
	assert.Nil(t, err)
}

func TestLoadfilterOneDayAzureBilling(t *testing.T) {
	var billings []*models.AzureBilling = []*models.AzureBilling{
		{
			AccountOwnerId: "1",
			Date:           "08/11/2024",
		},
		{
			AccountOwnerId: "1",
			Date:           "08/12/2024",
		},
		{
			AccountOwnerId: "1",
			Date:           "08/14/2024",
		},
	}
	// in this case, the result should be 1
	//timestamp := 1723487507 , which utc is Mon Aug 12 2024 18:31:47
	res := filterOneDayAzureBilling(billings, 1723487507)
	assert.Equal(t, 1, len(res))
}

func TestFilterAzureBlobNameSuffix(t *testing.T) {
	blobs := []string{
		"/dataplane-cost-storage/dedicated-nonprod-dataplane-actual-cost/20240801-20240831/part_0_0001.csv.gz",
		"/dataplane-cost-storage/dedicated-nonprod-dataplane-actual-cost/20240801-20240831/part_0_0001.csv",
		"/dataplane-cost-storage/dedicated-nonprod-dataplane-actual-cost/20240801-20240831/part_0_0001.csv.checkpoint",
		"/dataplane-cost-storage/dedicated-nonprod-dataplane-actual-cost/20240801-20240831/part_0_0001.sqlite.checkpoint",
	}
	res := filterObjectNameSuffix(blobs, azureBlobNameSuffix)
	assert.Equal(t, 1, len(res))
	assert.Equal(t, "/dataplane-cost-storage/dedicated-nonprod-dataplane-actual-cost/20240801-20240831/part_0_0001.csv.gz", res[0])
}

type mockCSVResult struct {
	Item1 string  `json:"item1"`
	Item2 float64 `json:"item2"`
	Item3 bool    `json:"item3"`
}

func TestLoadCSVFromGzip(t *testing.T) {
	log, _ := zap.NewDevelopment()
	res, err := loadCSVFromGzip[models.AzureBilling](log, "../../testdata/azurebillingfile/part_0_0001.csv.gz", &EmptyCSVFilter{})
	assert.Nil(t, err)
	assert.Equal(t, 397, len(res))
	mres, err := loadCSVFromGzip[mockCSVResult](log, "../../testdata/otherfile/base.csv.gz", &EmptyCSVFilter{})
	assert.Nil(t, err)
	assert.Equal(t, 3, len(mres))
	//test file not found
	_, err = loadCSVFromGzip[mockCSVResult](log, "notfound.csv.gz", &EmptyCSVFilter{})
	assert.NotNil(t, err)
}
func TestLoadCSVFromGzipStream(t *testing.T) {
	log, _ := zap.NewDevelopment()
	mres, err := loadCSVFromGzipStream[mockCSVResult](log, "../../testdata/otherfile/base.csv.gz", &EmptyCSVFilter{})
	assert.Nil(t, err)
	assert.Equal(t, 3, len(mres))
	//test file not found
	_, err = loadCSVFromGzipStream[mockCSVResult](log, "notfound.csv.gz", &EmptyCSVFilter{})
	assert.NotNil(t, err)
}

func TestGetCachePathInTime(t *testing.T) {
	loader := &NetworkBillingLoad{
		cachepath: "cachetest",
		conf: &config.ServiceConfig{
			Vendor: constants.VendorAzure,
		},
	}
	path, name := loader.GetCachePathInTime(1723487507)
	assert.Equal(t, "cachetest/20240801-20240831", path)
	assert.Equal(t, "20240801-20240831", name)
}

func TestLoadCSVFromGzipAWS(t *testing.T) {
	log, _ := zap.NewDevelopment()
	res, err := loadCSVFromGzip[models.AWSBilling](log, "../../testdata/awsbillingfile/cost-usage-report-00001.csv.gz", &AWSCSVFilter{})
	assert.Nil(t, err)
	assert.Equal(t, 2, len(res))
}

func TestGetGCPBillingSQL(t *testing.T) {
	p := gcpsqlParams{
		Tables:   gcptables,
		Projects: []string{"project1", "project2"},
		Date:     "2024-08-12",
	}
	sql, err := GetGCPBillingSQL(p)
	assert.Nil(t, err)
	excepted := `WITH
billsource AS (
    SELECT
    IFNULL((select value from UNNEST(labels) where key='tenant' limit 1), '') as user_label_tenant_id,
    IFNULL((select value from UNNEST(labels) where key='project' limit 1), '') as user_label_project_id,
    IFNULL((select value from UNNEST(labels) where key='cluster' limit 1), '') as user_label_cluster_id,
    IFNULL((select value from UNNEST(labels) where key='environment' limit 1), '') as user_label_environment_id,
    IFNULL((select value from UNNEST(labels) where key='component' limit 1), '') as user_label_component_id,
    IFNULL((select value from UNNEST(labels) where key='usedby' limit 1), '') as user_label_usedby,
    IFNULL((select value from UNNEST(labels) where key='servicetype' limit 1), '') as user_label_servicetype,
	IFNULL((select value from UNNEST(tags) where key='usedby' LIMIT 1), '') AS user_tag_usedby,
    usage_start_time,usage_end_time,_PARTITIONTIME,
    location.region AS region,sku.description AS description,service.description AS service_name,
    usage.pricing_unit AS unit,usage.amount_in_pricing_units AS amount,cost,cost_at_list,resource.Name as resource_name,CAST(usage_start_time AS DATE) AS usage_start_date
    FROM
      ` + "`gcp-digital-bi.gcp_billing_detailed.gcp_billing_export_resource_v1*`" + `
    WHERE
        project.id IN ("project1", "project2")
    AND cost > 0
    AND (
        (
            (sku.description LIKE "Network Internet %" OR sku.description LIKE "Network Inter Region %" 
            OR sku.description LIKE "Network Inter Zone %") 
            AND service.description = "Compute Engine" OR (sku.description LIKE "%Network Load Balancer%" AND service.description = "Networking")
        )
    )
)
SELECT
    region,sum(amount) as amount,unit,description,service_name,sum(cost) as cost,sum(cost_at_list) as cost_at_list,
    user_label_tenant_id,user_label_project_id,user_label_cluster_id,user_label_environment_id,user_label_component_id,
    user_label_usedby,user_tag_usedby,user_label_servicetype,usage_start_date,resource_name
FROM
    billsource
WHERE
    (user_label_usedby LIKE "customer%" OR user_tag_usedby = "customer-tidb")
    AND user_label_component_id != "extra-tidb"
    AND user_label_component_id != "tiflow"
    AND user_label_component_id != "rg_ticdc"
    AND usage_start_date = DATE('2024-08-12') 
GROUP BY
    region,unit,description,service_name,
    user_label_tenant_id,user_label_project_id,user_label_cluster_id,user_label_environment_id,user_label_component_id,
    user_label_usedby,user_tag_usedby,user_label_servicetype,usage_start_date,resource_name`
	assert.Equal(t, excepted, sql)
}

func TestGetGCPBillingSQL_AddsPartitionWindow(t *testing.T) {
	p := gcpsqlParams{
		Tables:             gcptables,
		Projects:           []string{"project1"},
		Date:               "2026-07-31",
		PartitionPredicate: "_PARTITIONTIME >= TIMESTAMP(DATE '2026-07-31') AND _PARTITIONTIME < TIMESTAMP(DATE_ADD(DATE '2026-07-31', INTERVAL 8 DAY))",
	}

	sql, err := GetGCPBillingSQL(p)
	assert.NoError(t, err)
	assert.Contains(t, sql, "_PARTITIONTIME >= TIMESTAMP(DATE '2026-07-31')")
	assert.Contains(t, sql, "_PARTITIONTIME < TIMESTAMP(DATE_ADD(DATE '2026-07-31', INTERVAL 8 DAY))")
	assert.Contains(t, sql, "AND usage_start_date = DATE('2026-07-31')")
}

func TestReadAWSParquetMeta(t *testing.T) {
	res, err := ReadAWSParquetMeta(context.TODO(), "../../testdata/awsbillingfile", "dev-billing-Manifest.json")
	assert.Nil(t, err)
	assert.Equal(t, 12, len(res))
}

func TestCoverage(t *testing.T) {
	type test struct {
		Name string `json:"name"`
	}
	excepted := &test{
		Name: "test",
	}
	tt := &test{}
	var res = map[string]interface{}{
		"Name": "test",
	}
	err := Coverage(res, tt)
	assert.Nil(t, err)
	assert.Equal(t, excepted, tt)
	//test the error case in marshal
	err = Coverage(func() {}, nil)
	assert.NotNil(t, err)
	//test the error case in unmarshal
	err = Coverage("dsadsa", nil)
	assert.NotNil(t, err)
}

func TestLoadParquetStream(t *testing.T) {
	log, _ := zap.NewDevelopment()
	res, err := loadParquetStream(log, "../../testdata/awsbillingfile/dev-billing-00001-sub.snappy.parquet")
	assert.Nil(t, err)
	assert.Equal(t, 3, len(res))
	//test file not found
	_, err = loadParquetStream(log, "notfound.parquet")
	assert.NotNil(t, err)
}

func TestLoadGCP(t *testing.T) {
	ctrl := gomock.NewController(t)
	conf := &config.ServiceConfig{
		Vendor: constants.VendorGCP,
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			Provider: config.TrafficMeteringProvider{
				GCP: &config.TrafficMeteringProviderGCPConfig{
					BillingProvider: config.BillingProviderGCPConfig{
						ProjectId:        "project1",
						Location:         "US",
						MeteringProjects: []string{"project1", "project2"},
					},
				},
			},
			CachePath: "cachetest",
		},
	}
	log, _ := zap.NewDevelopment()
	cli := mockbigquery.NewMockBigQueryProvider(ctrl)
	cli.EXPECT().ExecQuery(gomock.Any(), gomock.Any()).Return(bigquery.BigQueryRows{
		{
			"region": "us-east1",
			"amount": 1.0,
		},
		{
			"region": "us-east1",
			"amount": 2.0,
		},
	}, nil)
	loader := &NetworkBillingLoad{
		cachepath:   "cachetest/billingcache",
		conf:        conf,
		bigquerycli: cli,
		log:         log,
	}
	res, err := loader.LoadGCP(context.TODO(), 1723487507)
	assert.Nil(t, err)
	assert.Equal(t, 2, len(res))
}

func TestLoadAWSParquet(t *testing.T) {
	ctrl := gomock.NewController(t)
	conf := &config.ServiceConfig{
		Vendor: constants.VendorAWS,
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			Provider: config.TrafficMeteringProvider{
				AWS: &config.TrafficMeteringProviderAWSConfig{
					BillingProvider: []config.BillingProviderAWSConfig{{
						Account:    "123456",
						Region:     "us-east-1",
						BucketName: "dataplane-cost-storage",
						Perfix:     "dedicated-nonprod-dataplane-actual-cost",
						SourceType: "parquet",
					},
					},
				},
			},
			CachePath: "cachetest",
		},
	}
	log, _ := zap.NewDevelopment()
	cli := mockstorage.NewMockStorageProvider(ctrl)
	loader := &NetworkBillingLoad{
		cachepath:      "cachetest/billingcache",
		conf:           conf,
		objclis:        map[string]storage.StorageProvider{getAWScliKey("123456", "dataplane-cost-storage"): cli},
		cachebilling:   make(map[string][]*models.AzureBilling),
		cacheawsloaded: make(map[string]bool),
		log:            log,
	}
	//prepare the mock cache path
	path, _ := loader.GetCachePathInTime(1729123200, "123456")
	absCachePath, err := filepath.Abs(path)
	assert.Nil(t, err)
	err = utils.EnsurePath(absCachePath)
	assert.Nil(t, err)
	// copy the test data to the cache path
	err = copyFile("../../testdata/awsbillingfile/dev-billing-00001-sub.snappy.parquet", filepath.Join(absCachePath, "dev-billing-00001-sub.snappy.parquet"))
	assert.Nil(t, err)
	err = copyFile("../../testdata/awsbillingfile/dev-billing-sub-Manifest.json", filepath.Join(absCachePath, "dev-billing-sub-Manifest.json"))
	assert.Nil(t, err)
	cli.EXPECT().ListObjectInTime(gomock.Any(), int64(1729123200), "dedicated-nonprod-dataplane-actual-cost").Return([]string{
		"/dedicated-nonprod-dataplane-actual-cost/20241001-20241101/dev-billing-sub-Manifest.json",
	}, nil)
	cli.EXPECT().DownloadObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	res, err := loader.LoadAWS(context.TODO(), 1729123200)
	assert.Nil(t, err)
	assert.Equal(t, 1, len(res))
	//clean up the test data
	err = os.RemoveAll(absCachePath)
	assert.Nil(t, err)
}

func TestLoadAWSParquetMetaNotFound(t *testing.T) {
	ctrl := gomock.NewController(t)
	conf := &config.ServiceConfig{
		Vendor: constants.VendorAWS,
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			Provider: config.TrafficMeteringProvider{
				AWS: &config.TrafficMeteringProviderAWSConfig{
					BillingProvider: []config.BillingProviderAWSConfig{{
						Account:    "123456",
						Region:     "us-east-1",
						BucketName: "dataplane-cost-storage",
						Perfix:     "dedicated-nonprod-dataplane-actual-cost",
						SourceType: "parquet",
					},
					},
				},
			},
			CachePath: "cachetest",
		},
	}
	log, _ := zap.NewDevelopment()
	cli := mockstorage.NewMockStorageProvider(ctrl)
	loader := &NetworkBillingLoad{
		cachepath:      "cachetest/billingcache",
		conf:           conf,
		objclis:        map[string]storage.StorageProvider{getAWScliKey("123456", "dataplane-cost-storage"): cli},
		cachebilling:   make(map[string][]*models.AzureBilling),
		cacheawsloaded: make(map[string]bool),
		log:            log,
	}
	//prepare the mock cache path
	path, _ := loader.GetCachePathInTime(1729123200, "123456")
	absCachePath, err := filepath.Abs(path)
	assert.Nil(t, err)
	err = utils.EnsurePath(absCachePath)
	assert.Nil(t, err)
	cli.EXPECT().ListObjectInTime(gomock.Any(), int64(1729123200), "dedicated-nonprod-dataplane-actual-cost").Return([]string{}, nil)
	cli.EXPECT().DownloadObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	res, err := loader.LoadAWS(context.TODO(), 1729123200)
	assert.Equal(t, err, errNometaFound)
	assert.Equal(t, 0, len(res))
}

func TestInitLoaderClient(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	os.Setenv("AWS_ACCESS_KEY_ID", "test1")
	os.Setenv("AWS_SECRET_ACCESS_KEY", "testsec")
	os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "../../testdata/checkpointfile/application_default_credentials.json")
	os.Setenv("AZURE_TENANT_ID", "test1")
	os.Setenv("AZURE_CLIENT_ID", "test1")
	os.Setenv("AZURE_CLIENT_SECRET", "ttt")
	defer os.Unsetenv("GOOGLE_APPLICATION_CREDENTIALS")
	defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
	defer os.Unsetenv("AWS_ACCESS_KEY_ID")
	defer os.Unsetenv("AZURE_TENANT_ID")
	defer os.Unsetenv("AZURE_CLIENT_ID")
	defer os.Unsetenv("AZURE_CLIENT_SECRET")
	conf := &config.ServiceConfig{
		Vendor: constants.VendorAWS,
		Region: "us-west-2",
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			CachePath: "cachetest_init",
			Provider: config.TrafficMeteringProvider{
				AWS: &config.TrafficMeteringProviderAWSConfig{
					BillingProvider: []config.BillingProviderAWSConfig{{
						Account:    "123456",
						Region:     "us-west-2",
						BucketName: "costmetetringstoragedev",
						Perfix:     "dedicated-nonprod-dataplane-actual-cost",
						SourceType: "csv",
					},
					},
				},
				GCP: &config.TrafficMeteringProviderGCPConfig{
					BillingProvider: config.BillingProviderGCPConfig{
						ProjectId:        "costmetetringstoragedev",
						Location:         "US",
						MeteringProjects: []string{"costmetetringstoragedev"},
					},
				},
				Azure: &config.TrafficMeteringProviderAzureConfig{
					BillingProvider: config.BillingProviderAzureConfig{
						StorageAccountName: "https://costmetetringstoragedev.blob.core.windows.net/",
						ContainerName:      "dataplane-cost-storage",
						Perfix:             []string{"dataplane-cost-storage/sqlitebilling"},
					},
				},
			},
		},
	}
	log, _ := zap.NewDevelopment()
	b := NewNetworkBillingLoad(conf, log)
	// test aws
	_, err := b.Init()
	assert.Nil(t, err)
	//test aws with RoleArn
	b.conf.TrafficMeteringConfig.Provider.AWS.BillingProvider[0].RoleArn = "arn:aws:iam::123456789012:role/test"
	_, err = b.Init()
	assert.Nil(t, err)
	// test gcp
	b.conf.Vendor = constants.VendorGCP
	_, err = b.Init()
	assert.Nil(t, err)
	// test azure
	b.conf.Vendor = constants.VendorAzure
	_, err = b.Init()
	assert.Nil(t, err)
}

func TestFilterOneDayAzureBillingEmpty(t *testing.T) {
	var billings []*models.AzureBilling = []*models.AzureBilling{}
	res := filterOneDayAzureBilling(billings, 1723487507)
	assert.Equal(t, 0, len(res))
}

func TestFilterOneDayAWSBillingFailed(t *testing.T) {
	var billings []*models.AWSBilling = []*models.AWSBilling{}
	res := filterOneDayAWSBilling(billings, 1723487507)
	assert.Equal(t, 0, len(res))
	billings = append(billings, &models.AWSBilling{
		LineItemUsageStartDate: "dsajoi",
	})
	res = filterOneDayAWSBilling(billings, 1723487507)
	assert.Equal(t, 0, len(res))
}

func TestAWSCSVFilter(t *testing.T) {
	filter := &AWSCSVFilter{}
	err := filter.Init([]string{})
	assert.NotNil(t, err)
	filter.productCodeCursor = 0
	filter.productServicenameCursor = 1
	filter.tagComponentCursor = 2
	b := filter.Filter([]string{"AWSELB", "productServicename", "extra-tidb"})
	assert.False(t, b)
}

func TestReadMetaNotFound(t *testing.T) {
	_, err := ReadAWSParquetMeta(context.TODO(), "../../testdata/awsbillingfile", "notfound.json")
	assert.NotNil(t, err)
	//test this case in azure
	_, err = ReadAzureMeta(context.TODO(), "../../testdata/azurebillingfile", "notfound.json")
	assert.NotNil(t, err)
}

func TestParsePremiumReservedMeta(t *testing.T) {
	raw := []byte(`{
		"items": [
			{"tidb_cluster_id": "10663489082666642842", "resource_pool_id": "pool-a"},
			{"cluster_id": 2062125181228285952, "shared_pool_id": "pool-b"}
		]
	}`)
	meta, err := parsePremiumReservedMeta(raw)
	assert.NoError(t, err)
	assert.Len(t, meta.clusterIDs, 2)
	assert.Len(t, meta.resourcePoolIDs, 2)
	_, ok := meta.clusterIDs["10663489082666642842"]
	assert.True(t, ok)
	_, ok = meta.clusterIDs["2062125181228285952"]
	assert.True(t, ok)
	_, ok = meta.resourcePoolIDs["pool-a"]
	assert.True(t, ok)
	_, ok = meta.resourcePoolIDs["pool-b"]
	assert.True(t, ok)
}

func TestMatchPremiumReservedBilling(t *testing.T) {
	meta := &premiumReservedMeta{
		clusterIDs: map[string]struct{}{
			"10663489082666642842": {},
		},
		resourcePoolIDs: map[string]struct{}{
			"pool-1": {},
		},
	}
	billingByCluster := &models.AWSBilling{ResourceTagsCluster: "10663489082666642842"}
	billingByPool := &models.AWSBilling{ResourceTagsSharedPool: "pool-1"}
	billingMiss := &models.AWSBilling{ResourceTagsCluster: "not-found", ResourceTagsSharedPool: "pool-x"}

	assert.True(t, matchPremiumReservedBilling(billingByCluster, meta))
	assert.True(t, matchPremiumReservedBilling(billingByPool, meta))
	assert.False(t, matchPremiumReservedBilling(billingMiss, meta))
	assert.False(t, matchPremiumReservedBilling(nil, meta))
	assert.False(t, matchPremiumReservedBilling(billingByCluster, nil))
}
