package repoter

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/utils"
	"github.com/tidbcloud/metering/test/mockstorage"
	"go.uber.org/mock/gomock"
)

func TestLoadAzureRepoterWithTs(t *testing.T) {
	ctrl := gomock.NewController(t)
	cli := mockstorage.NewMockStorageProvider(ctrl)
	r := &BillingRepoterLoad{
		cachepath: "cachetest/repotercache",
		objcli:    cli,
	}
	blobname := "dataplane-cost-storage/sqliteresult/20240812/20240812.db"
	cli.EXPECT().ListObjectByName(gomock.Any(), blobname).Return([]string{
		blobname,
	}, nil)
	cli.EXPECT().DownloadObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
	res, err := r.LoadRepoterWithTs(context.TODO(), blobname, 0)
	assert.Nil(t, err)
	assert.NotNil(t, res)
	assert.Equal(t, 1, len(res))
}

func TestLoadCheckpoint(t *testing.T) {
	ctrl := gomock.NewController(t)
	cli := mockstorage.NewMockStorageProvider(ctrl)
	r := &BillingRepoterLoad{
		cachepath: "cachetest/repotercache",
		objcli:    cli,
	}
	path := "cachetest/repotercache"
	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/checkpointfile/checkpoint.json", filepath.Join(absCachePath, "checkpoint.json"))
	assert.Nil(t, err)
	blobname := "dataplane-cost-storage/sqliteresult/20240812/20240812.db"
	cli.EXPECT().ListObjectByName(gomock.Any(), "dataplane-cost-storage/sqliteresult/20240812/checkpoint.json").Return([]string{
		"dataplane-cost-storage/sqliteresult/20240812/checkpoint.json",
	}, nil)
	cli.EXPECT().DownloadObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
	res, err := r.LoadCheckpoint(context.TODO(), blobname)
	assert.Nil(t, err)
	assert.NotNil(t, res)
	assert.Equal(t, int64(1723487507), res)
}

func TestLoadSaveCheckpoint(t *testing.T) {
	ctrl := gomock.NewController(t)
	cli := mockstorage.NewMockStorageProvider(ctrl)
	r := &BillingRepoterLoad{
		cachepath: "cachetest/repotercache",
		objcli:    cli,
	}
	blobname := "dataplane-cost-storage/sqliteresult/20240812/20240812.db"
	cli.EXPECT().UploadObject(gomock.Any(), "dataplane-cost-storage/sqliteresult/20240812/checkpoint.json", gomock.Any()).Return(nil)
	err := r.SaveCheckpoint(context.TODO(), blobname, int64(1723487507))
	assert.Nil(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 := &BillingRepoterLoad{
		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 role arn
	b.conf.TrafficMeteringConfig.Provider.AWS.StoreProvider.RoleArn = "arn:aws"
	_, 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)
	// test invalid vendor
	b.conf.Vendor = "invalid_vendor"
	_, err = b.Init()
	assert.Error(t, err)
}

func TestLoadAzureRepoterWithTsError(t *testing.T) {
	ctrl := gomock.NewController(t)
	cli := mockstorage.NewMockStorageProvider(ctrl)
	r := &BillingRepoterLoad{
		cachepath: "cachetest/repotercache",
		objcli:    cli,
	}
	blobname := "dataplane-cost-storage/sqliteresult/20240812/20240812.db"

	// Test list objects error
	cli.EXPECT().ListObjectByName(gomock.Any(), blobname).Return(nil, assert.AnError)
	res, err := r.LoadRepoterWithTs(context.TODO(), blobname, 0)
	assert.Error(t, err)
	assert.Nil(t, res)

	// Test empty list result
	cli.EXPECT().ListObjectByName(gomock.Any(), blobname).Return([]string{}, nil)
	res, err = r.LoadRepoterWithTs(context.TODO(), blobname, 0)
	assert.Nil(t, err)
	assert.Nil(t, res)

	// Test  list result more than one
	cli.EXPECT().ListObjectByName(gomock.Any(), blobname).Return([]string{blobname, blobname}, nil)
	res, err = r.LoadRepoterWithTs(context.TODO(), blobname, 0)
	assert.Error(t, err)
	assert.Nil(t, res)

	// Test download error
	cli.EXPECT().ListObjectByName(gomock.Any(), blobname).Return([]string{blobname}, nil)
	cli.EXPECT().DownloadObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(assert.AnError)
	res, err = r.LoadRepoterWithTs(context.TODO(), blobname, 0)
	assert.Error(t, err)
	assert.Nil(t, res)
}

func TestLoadCheckpointError(t *testing.T) {
	ctrl := gomock.NewController(t)
	cli := mockstorage.NewMockStorageProvider(ctrl)
	r := &BillingRepoterLoad{
		cachepath: "cachetest/repotercache",
		objcli:    cli,
	}
	blobname := "dataplane-cost-storage/sqliteresult/20240812/20240812.db"
	checkpointPath := "dataplane-cost-storage/sqliteresult/20240812/checkpoint.json"

	// Test list objects error
	cli.EXPECT().ListObjectByName(gomock.Any(), checkpointPath).Return(nil, assert.AnError)
	res, err := r.LoadCheckpoint(context.TODO(), blobname)
	assert.Error(t, err)
	assert.Equal(t, int64(0), res)

	// Test empty list result
	cli.EXPECT().ListObjectByName(gomock.Any(), checkpointPath).Return([]string{}, nil)
	res, err = r.LoadCheckpoint(context.TODO(), blobname)
	assert.Nil(t, err)
	assert.Equal(t, int64(0), res)

	// Test list result more than one
	cli.EXPECT().ListObjectByName(gomock.Any(), checkpointPath).Return([]string{checkpointPath, checkpointPath}, nil)
	res, err = r.LoadCheckpoint(context.TODO(), blobname)
	assert.Error(t, err)
	assert.Equal(t, int64(0), res)

	// Test download error
	cli.EXPECT().ListObjectByName(gomock.Any(), checkpointPath).Return([]string{checkpointPath}, nil)
	cli.EXPECT().DownloadObjects(gomock.Any(), gomock.Any(), gomock.Any()).Return(assert.AnError)
	res, err = r.LoadCheckpoint(context.TODO(), blobname)
	assert.Error(t, err)
	assert.Equal(t, int64(0), res)
}

func TestSaveCheckpointError(t *testing.T) {
	ctrl := gomock.NewController(t)
	cli := mockstorage.NewMockStorageProvider(ctrl)
	r := &BillingRepoterLoad{
		cachepath: "cachetest/repotercache",
		objcli:    cli,
	}
	blobname := "dataplane-cost-storage/sqliteresult/20240812/20240812.db"

	// Test upload error
	cli.EXPECT().UploadObject(gomock.Any(), gomock.Any(), gomock.Any()).Return(assert.AnError)
	err := r.SaveCheckpoint(context.TODO(), blobname, int64(1723487507))
	assert.Error(t, err)
}

func TestEnsureSQLiteError(t *testing.T) {
	blobname := "notfound/dataplane-cost-storage/sqliteresult/20240812/20240812.db"
	_, err := ensureSQLite(blobname)
	assert.Error(t, err)
}
