package networkmetering

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/test/mockstorage"
	"go.uber.org/mock/gomock"
	"gorm.io/gorm"
)

func TestGetCacheFileInTime(t *testing.T) {
	store := &NetworkBillingStore{
		cachepath: "cachetest/resultcache",
	}
	name, day := store.GetCacheFileInTime(1723487507)
	assert.Equal(t, "cachetest/resultcache/20240812.db", name)
	assert.Equal(t, "20240812", day)
	filaname := store.BuildObjName(day)
	assert.Equal(t, "20240812/20240812.db", filaname)
}

func TestDBGetWrite(t *testing.T) {
	ctrl := gomock.NewController(t)
	cli := mockstorage.NewMockStorageProvider(ctrl)
	store := &NetworkBillingStore{
		conf: &config.ServiceConfig{
			Vendor: constants.VendorAzure,
			TrafficMeteringConfig: &config.TrafficMeteringConfig{
				Provider: config.TrafficMeteringProvider{
					Azure: &config.TrafficMeteringProviderAzureConfig{
						StoreProvider: config.StoreProviderAzureConfig{
							StorageAccountName: "https://costmetetringstoragedev.blob.core.windows.net/",
							ContainerName:      "dataplane-cost-storage",
							Perfix:             "dataplane-cost-storage/sqliteresult",
						},
					},
				},
			},
		},
		cachepath:   "cachetest/resultcache",
		cachesqlite: make(map[string]*gorm.DB),
		objcli:      cli,
		perfix:      "dataplane-cost-storage/sqliteresult",
	}
	//test new store
	copystore := NewNetworkBillingStore(store.conf)
	assert.Equal(t, copystore.conf, store.conf)
	//test ListObjectByName failed
	cli.EXPECT().ListObjectByName(gomock.Any(), "dataplane-cost-storage/sqliteresult/20240812/20240812.db").Return([]string{}, errors.New("error"))
	_, _, err := store.Load(context.TODO(), 1723487507)
	assert.NotNil(t, err)
	cli.EXPECT().ListObjectByName(gomock.Any(), "dataplane-cost-storage/sqliteresult/20240812/20240812.db").Return([]string{}, nil)
	m, r, err := store.Load(context.TODO(), 1723487507)
	assert.Nil(t, err)
	assert.Equal(t, 0, len(m))
	assert.Equal(t, 0, len(r))
	//test load from cache
	m, r, err = store.Load(context.TODO(), 1723487507)
	assert.Nil(t, err)
	assert.Equal(t, 0, len(m))
	assert.Equal(t, 0, len(r))
	//test load from object storage failed
	store.cachesqlite = make(map[string]*gorm.DB)
	cli.EXPECT().ListObjectByName(gomock.Any(), "dataplane-cost-storage/sqliteresult/20240812/20240812.db").Return([]string{"dataplane-cost-storage/sqliteresult/20240812/20240812.db"}, nil)
	cli.EXPECT().DownloadObjects(gomock.Any(), []string{"dataplane-cost-storage/sqliteresult/20240812/20240812.db"}, "cachetest/resultcache").Return(errors.New("error"))
	m, r, err = store.Load(context.TODO(), 1723487507)
	assert.NotNil(t, err)
	assert.Equal(t, 0, len(m))
	assert.Equal(t, 0, len(r))
	//test load from object storage
	store.cachesqlite = make(map[string]*gorm.DB)
	cli.EXPECT().ListObjectByName(gomock.Any(), "dataplane-cost-storage/sqliteresult/20240812/20240812.db").Return([]string{"dataplane-cost-storage/sqliteresult/20240812/20240812.db"}, nil)
	cli.EXPECT().DownloadObjects(gomock.Any(), []string{"dataplane-cost-storage/sqliteresult/20240812/20240812.db"}, "cachetest/resultcache").Return(nil)
	m, r, err = store.Load(context.TODO(), 1723487507)
	assert.Nil(t, err)
	assert.Equal(t, 0, len(m))
	assert.Equal(t, 0, len(r))
	m = append(m, &models.BillingMetering{
		ID:          123,
		TenantID:    "123",
		ProjectID:   "123",
		ClusterID:   "123",
		UsedBy:      "123",
		BillingDate: 1723487507,
		ResourceID:  "123",
		Cost:        1100,
		Unit:        "azure:lb:outbound",
	})
	r = append(r, &models.BillingReport{
		TenantID: 123,
		CostID:   "123",
		Labels:   `{"projectID":"123","clusterID":"123"}`,
		Cost:     1100,
		Unit:     "azure:lb:outbound",
	})
	err = store.Store(context.TODO(), 1723487507, m, r)
	assert.Nil(t, err)
	// test store failed of cache
	store.cachesqlite = make(map[string]*gorm.DB)
	err = store.Store(context.TODO(), 1723487507, m, r)
	assert.NotNil(t, err)
}

func TestUpload(t *testing.T) {
	ctrl := gomock.NewController(t)
	cli := mockstorage.NewMockStorageProvider(ctrl)
	store := &NetworkBillingStore{
		conf: &config.ServiceConfig{
			Vendor: constants.VendorAzure,
			TrafficMeteringConfig: &config.TrafficMeteringConfig{
				Provider: config.TrafficMeteringProvider{
					Azure: &config.TrafficMeteringProviderAzureConfig{
						StoreProvider: config.StoreProviderAzureConfig{
							StorageAccountName: "https://costmetetringstoragedev.blob.core.windows.net/",
							ContainerName:      "dataplane-cost-storage",
							Perfix:             "dataplane-cost-storage/sqliteresult",
						},
					},
				},
			},
		},
		cachepath:   "cachetest/resultcache",
		cachesqlite: make(map[string]*gorm.DB),
		objcli:      cli,
		perfix:      "dataplane-cost-storage/sqliteresult",
	}
	cli.EXPECT().UploadObject(gomock.Any(), "dataplane-cost-storage/sqliteresult/20240812/20240812.db", "cachetest/resultcache/20240812.db").Return(nil)
	objpath, err := store.Upload(context.TODO(), 1723487507)
	assert.Nil(t, err)
	assert.Equal(t, "dataplane-cost-storage/sqliteresult/20240812/20240812.db", objpath)
	//test upload failed
	cli.EXPECT().UploadObject(gomock.Any(), "dataplane-cost-storage/sqliteresult/20240812/20240812.db", "cachetest/resultcache/20240812.db").Return(errors.New("error"))
	_, err = store.Upload(context.TODO(), 1723487507)
	assert.NotNil(t, err)
}

func TestInitClient(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")
	b := &NetworkBillingStore{
		conf: &config.ServiceConfig{
			Vendor: constants.VendorAWS,
			Region: "us-west-2",
			TrafficMeteringConfig: &config.TrafficMeteringConfig{
				CachePath: "cachetest_init",
				Provider: config.TrafficMeteringProvider{
					AWS: &config.TrafficMeteringProviderAWSConfig{
						StoreProvider: config.StoreProviderAWSConfig{
							Region:     "us-west-2",
							BucketName: "costmetetringstoragedev",
						},
					},
					GCP: &config.TrafficMeteringProviderGCPConfig{
						StoreProvider: config.StoreProviderGCPConfig{
							ProjectId:  "costmetetringstoragedev",
							BucketName: "costmetetringstoragedev",
						},
					},
					Azure: &config.TrafficMeteringProviderAzureConfig{
						StoreProvider: config.StoreProviderAzureConfig{
							StorageAccountName: "https://costmetetringstoragedev.blob.core.windows.net/",
							ContainerName:      "dataplane-cost-storage",
							Perfix:             "dataplane-cost-storage/sqliteresult",
						},
					},
				},
			},
		},
	}
	// test aws
	_, err := b.Init()
	assert.Nil(t, err)
	//test aws with RoleArn
	b.conf.TrafficMeteringConfig.Provider.AWS.StoreProvider.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 TestEnsureSQLiteFailed(t *testing.T) {
	store := &NetworkBillingStore{
		conf: &config.ServiceConfig{
			Vendor: constants.VendorAWS,
		},
	}
	_, err := store.ensureSQLite("https:://128.0.0.1/test.db")
	assert.NotNil(t, err)
}
