package repoter

import (
	"context"
	"errors"
	"os"
	"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/test/mockrepoter"
	"go.uber.org/mock/gomock"
	"go.uber.org/zap"
)

func TestReport(t *testing.T) {
	ctrl := gomock.NewController(t)
	loger, _ := zap.NewDevelopment()
	blobname := "dataplane-cost-storage/sqliteresult/20240812/20240812.db"
	loader := mockrepoter.NewMockBillingRepoterLoader(ctrl)
	sender := mockrepoter.NewMockMeasureSender(ctrl)
	r := &BillingRepoter{
		loader: loader,
		sender: sender,
		log:    loger,
		conf: &config.ServiceConfig{
			Vendor: constants.VendorAzure,
			TrafficMeteringConfig: &config.TrafficMeteringConfig{
				BillingReportSwitchTime: 1723487407,
			},
		},
	}
	loader.EXPECT().LoadCheckpoint(gomock.Any(), blobname).Return(int64(0), nil)
	loader.EXPECT().LoadRepoterWithTs(gomock.Any(), blobname, int64(0)).Return([]*models.BillingReport{
		{
			TenantID:      123,
			CostID:        utils.GenerateID(),
			Cost:          100000000,
			CostTimestamp: 1723487507,
			Labels:        `{"projectID":"123","clusterID":"123"}`,
			Unit:          "network-traffic:azure:eastus:cross_region",
			CreatedAt:     1723487507,
		},
		{
			TenantID:      123,
			CostID:        utils.GenerateID(),
			Cost:          100000000,
			CostTimestamp: 1723486407,
			Labels:        `{"projectID":"123","clusterID":"123"}`,
			Unit:          "network-traffic:azure:eastus:cross_region",
			CreatedAt:     1723487507,
		},
	}, nil)
	//only one report
	sender.EXPECT().SendMeasure(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	loader.EXPECT().SaveCheckpoint(gomock.Any(), blobname, int64(1723487507)).Return(nil)
	err := r.Report(context.TODO(), blobname)
	assert.Nil(t, err)
}

func TestReport_Failure(t *testing.T) {
	ctrl := gomock.NewController(t)
	loger, _ := zap.NewDevelopment()
	blobname := "dataplane-cost-storage/sqliteresult/20240812/20240812.db"
	loader := mockrepoter.NewMockBillingRepoterLoader(ctrl)
	sender := mockrepoter.NewMockMeasureSender(ctrl)
	r := &BillingRepoter{
		loader: loader,
		sender: sender,
		log:    loger,
		conf: &config.ServiceConfig{
			Vendor: constants.VendorAzure,
			TrafficMeteringConfig: &config.TrafficMeteringConfig{
				BillingReportSwitchTime: 1723487407,
			},
		},
	}
	// loader load failed
	loader.EXPECT().LoadCheckpoint(gomock.Any(), blobname).Return(int64(0), errors.New("load checkpoint failed"))
	err := r.Report(context.TODO(), blobname)
	assert.NotNil(t, err)
	// loader load repoter failed
	loader.EXPECT().LoadCheckpoint(gomock.Any(), blobname).Return(int64(0), nil)
	loader.EXPECT().LoadRepoterWithTs(gomock.Any(), blobname, int64(0)).Return(nil, errors.New("load repoter failed"))
	err = r.Report(context.TODO(), blobname)
	assert.NotNil(t, err)
	// sender send failed
	loader.EXPECT().LoadCheckpoint(gomock.Any(), blobname).Return(int64(0), nil)
	loader.EXPECT().LoadRepoterWithTs(gomock.Any(), blobname, int64(0)).Return([]*models.BillingReport{
		{
			TenantID:      123,
			CostID:        utils.GenerateID(),
			CostTimestamp: 1723487408,
		},
	}, nil)
	sender.EXPECT().SendMeasure(gomock.Any(), gomock.Any()).Return(errors.New("send measure failed"))
	err = r.Report(context.TODO(), blobname)
	assert.NotNil(t, err)
	// save checkpoint failed
	loader.EXPECT().LoadCheckpoint(gomock.Any(), blobname).Return(int64(0), nil)
	loader.EXPECT().LoadRepoterWithTs(gomock.Any(), blobname, int64(0)).Return([]*models.BillingReport{
		{
			TenantID:      123,
			CostID:        utils.GenerateID(),
			CostTimestamp: 1723487408,
		},
	}, nil)
	sender.EXPECT().SendMeasure(gomock.Any(), gomock.Any()).Return(nil)
	loader.EXPECT().SaveCheckpoint(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("save checkpoint failed"))
	err = r.Report(context.TODO(), blobname)
	assert.NotNil(t, err)
}

func TestSkipRepoter(t *testing.T) {
	loger, _ := zap.NewDevelopment()
	r := &BillingRepoter{
		log: loger,
		conf: &config.ServiceConfig{
			Vendor: constants.VendorAzure,
			TrafficMeteringConfig: &config.TrafficMeteringConfig{
				BillingReportSwitchTime: 1723487407,
				TenantWithSwitchTimes: []config.TenantFilter{
					{TenantId: "123", SwitchTime: 1723487405},
				},
			},
		},
	}
	// on repoter after full switch time
	repoter := &models.BillingReport{
		TenantID:      456,
		CostID:        utils.GenerateID(),
		Cost:          100000000,
		CostTimestamp: 1723487407,
		Labels:        `{"projectID":"456","clusterID":"123"}`,
		Unit:          "network-traffic:azure:eastus:cross_region",
		CreatedAt:     1723487407,
	}
	ok := r.skipRepoter(repoter)
	assert.False(t, ok)
	// on repoter before full switch time
	repoter2 := &models.BillingReport{
		TenantID:      456,
		CostID:        utils.GenerateID(),
		Cost:          100000000,
		CostTimestamp: 1723487406,
		Labels:        `{"projectID":"456","clusterID":"123"}`,
		Unit:          "network-traffic:azure:eastus:cross_region",
		CreatedAt:     1723487406,
	}
	ok = r.skipRepoter(repoter2)
	assert.True(t, ok)
	// on repoter after project switch time
	repoter3 := &models.BillingReport{
		TenantID:      123,
		CostID:        utils.GenerateID(),
		Cost:          100000000,
		CostTimestamp: 1723487405,
		Labels:        `{"projectID":"4567","clusterID":"123"}`,
		Unit:          "network-traffic:azure:eastus:cross_region",
		CreatedAt:     1723487405,
	}
	ok = r.skipRepoter(repoter3)
	assert.False(t, ok)
	// on repoter before project switch time
	repoter4 := &models.BillingReport{
		TenantID:      123,
		CostID:        utils.GenerateID(),
		Cost:          100000000,
		CostTimestamp: 1723487404,
		Labels:        `{"projectID":"4567","clusterID":"123"}`,
		Unit:          "network-traffic:azure:eastus:cross_region",
		CreatedAt:     1723487404,
	}
	ok = r.skipRepoter(repoter4)
	assert.True(t, ok)
}

func TestNewBillingRepoter_Init(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	os.Setenv("AWS_ACCESS_KEY_ID", "test1")
	os.Setenv("AWS_SECRET_ACCESS_KEY", "testsec")
	defer os.Unsetenv("AWS_SECRET_ACCESS_KEY")
	defer os.Unsetenv("AWS_ACCESS_KEY_ID")
	loger, _ := zap.NewDevelopment()
	conf := &config.ServiceConfig{
		Vendor: constants.VendorAWS,
		Env:    "dev",
		TrafficMeteringConfig: &config.TrafficMeteringConfig{
			BillingReportSwitchTime: 1723487407,
			CachePath:               "cachetest_init",
			Provider: config.TrafficMeteringProvider{
				AWS: &config.TrafficMeteringProviderAWSConfig{
					StoreProvider: config.StoreProviderAWSConfig{
						Region:     "us-west-2",
						BucketName: "costmetetringstoragedev",
					},
				},
			},
		},
	}

	_, err := NewBillingRepoter(conf, loger)
	assert.Nil(t, err)
}
