package nextgenmetering

import (
	"context"
	"fmt"
	"math"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/pingcap/metering_sdk/common"
	sdkconfig "github.com/pingcap/metering_sdk/config"
	"github.com/pingcap/metering_sdk/storage"
	meteringwriter "github.com/pingcap/metering_sdk/writer/metering"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/metering/pkg/common/config"
	"github.com/tidbcloud/metering/pkg/common/constants"
	"go.uber.org/zap"
)

// testMeteringWriter is a helper for writing test data
type testMeteringWriter struct {
	writer *meteringwriter.MeteringWriter
}

// newTestMeteringWriter creates a writer for test data
func newTestMeteringWriter(storage storage.ObjectStorageProvider, log *zap.Logger) *testMeteringWriter {
	sdkCfg := sdkconfig.DefaultConfig()
	sdkCfg = sdkCfg.WithLogger(log)
	return &testMeteringWriter{
		writer: meteringwriter.NewMeteringWriter(storage, sdkCfg),
	}
}

// Write writes metering data for testing
func (w *testMeteringWriter) Write(ctx context.Context, data *common.MeteringData) error {
	return w.writer.Write(ctx, data)
}

// Close closes the writer
func (w *testMeteringWriter) Close() error {
	return w.writer.Close()
}

// createMeteringValue creates a metering value with proper unit (test helper)
func createMeteringValue(value interface{}, unit string) *common.MeteringValue {
	var uintValue uint64

	switch v := value.(type) {
	case uint64:
		uintValue = v
	case int64:
		if v >= 0 {
			uintValue = uint64(v)
		} else {
			uintValue = 0
		}
	case int:
		if v >= 0 {
			uintValue = uint64(v)
		} else {
			uintValue = 0
		}
	case float64:
		if v >= 0 {
			if v > math.MaxUint64 {
				uintValue = math.MaxUint64
			} else {
				uintValue = uint64(v)
			}
		} else {
			uintValue = 0
		}
	default:
		uintValue = 0
	}

	return &common.MeteringValue{
		Value: uintValue,
		Unit:  unit,
	}
}

// testLoaderWithWriter holds both loader and writer for tests that need to write data
type testLoaderWithWriter struct {
	loader *MeteringLoader
	writer *testMeteringWriter
}

// setupTestLoaderWithWriter creates a test loader with writer for tests that need to write data
func setupTestLoaderWithWriter(t *testing.T) *testLoaderWithWriter {
	logger := zap.NewNop() // Silent logger for tests

	// Use test name and nano time to create unique prefix
	testName := strings.ReplaceAll(t.Name(), "/", "_")
	testPrefix := fmt.Sprintf("test_loader_%s_%d/", testName, time.Now().UnixNano())

	conf := &config.ServiceConfig{
		Vendor:   "local", // Use local filesystem for testing
		LogLevel: "info",
		NextGenMeteringConfig: &config.NextGenMeteringConfig{
			MeteringPrefix: testPrefix,
		},
	}

	loader := NewMeteringLoader(conf, logger)
	err := loader.Init()
	require.NoError(t, err)

	// Create writer using the same storage provider
	writer := newTestMeteringWriter(loader.storage, logger)

	return &testLoaderWithWriter{
		loader: loader,
		writer: writer,
	}
}

// Close closes both loader and writer
func (tlw *testLoaderWithWriter) Close() error {
	var errs []error
	if err := tlw.writer.Close(); err != nil {
		errs = append(errs, err)
	}
	if err := tlw.loader.Close(); err != nil {
		errs = append(errs, err)
	}
	if len(errs) > 0 {
		return fmt.Errorf("errors during close: %v", errs)
	}
	return nil
}

// setupTestLoader creates a test loader with local filesystem storage
func setupTestLoader(t *testing.T) *MeteringLoader {
	logger := zap.NewNop() // Silent logger for tests

	// Use test name and nano time to create unique prefix
	testName := strings.ReplaceAll(t.Name(), "/", "_")
	testPrefix := fmt.Sprintf("test_loader_%s_%d/", testName, time.Now().UnixNano())

	conf := &config.ServiceConfig{
		Vendor:   "local", // Use local filesystem for testing
		LogLevel: "info",
		NextGenMeteringConfig: &config.NextGenMeteringConfig{
			MeteringPrefix: testPrefix,
		},
	}

	loader := NewMeteringLoader(conf, logger)
	err := loader.Init()
	require.NoError(t, err)

	return loader
}

func TestMeteringLoader_CreateStorageConfig_Alicloud(t *testing.T) {
	const (
		accessKey = "test-ak"
		secretKey = "test-sk"
		endpoint  = "https://oss-cn-test.aliyuncs.com"
	)

	t.Setenv(constants.AlicloudAccessKeyIDEnv, accessKey)
	t.Setenv(constants.AlicloudAccessKeySecretEnv, secretKey)

	conf := &config.ServiceConfig{
		Vendor: constants.VendorAlicloud,
		Region: "oss-cn-test",
		Bucket: "test-bucket",
		NextGenMeteringConfig: &config.NextGenMeteringConfig{
			MeteringPrefix: "metering/",
			BucketEndpoint: endpoint,
		},
	}

	loader := NewMeteringLoader(conf, zap.NewNop())
	storageConfig, err := loader.createStorageConfig()
	require.NoError(t, err)
	require.NotNil(t, storageConfig)

	assert.Equal(t, storage.ProviderTypeOSS, storageConfig.Type)
	assert.Equal(t, conf.Bucket, storageConfig.Bucket)
	assert.Equal(t, conf.Region, storageConfig.Region)
	assert.Equal(t, conf.NextGenMeteringConfig.MeteringPrefix, storageConfig.Prefix)
	assert.Equal(t, endpoint, storageConfig.Endpoint)
	require.NotNil(t, storageConfig.OSS)
	assert.Equal(t, accessKey, storageConfig.OSS.AccessKey)
	assert.Equal(t, secretKey, storageConfig.OSS.SecretAccessKey)
}

func TestMeteringLoader_CreateStorageConfig_AlicloudMissingEnv(t *testing.T) {
	t.Setenv(constants.AlicloudAccessKeyIDEnv, "")
	t.Setenv(constants.AlicloudAccessKeySecretEnv, "")

	conf := &config.ServiceConfig{
		Vendor: constants.VendorAlicloud,
		NextGenMeteringConfig: &config.NextGenMeteringConfig{
			BucketEndpoint: "https://oss-cn-test.aliyuncs.com",
		},
	}

	loader := NewMeteringLoader(conf, zap.NewNop())
	storageConfig, err := loader.createStorageConfig()
	assert.Error(t, err)
	assert.Nil(t, storageConfig)
	assert.Contains(t, err.Error(), constants.AlicloudAccessKeyIDEnv)
}

func TestMeteringLoader_CreateStorageConfig_Azure(t *testing.T) {
	t.Run("with account key", func(t *testing.T) {
		t.Setenv(constants.AzureStorageAccountNameEnv, "teststorageaccount")
		t.Setenv(constants.AzureStorageAccountKeyEnv, "dGVzdC1hY2NvdW50LWtleQ==")

		conf := &config.ServiceConfig{
			Vendor: constants.VendorAzure,
			Bucket: "metering-container",
			NextGenMeteringConfig: &config.NextGenMeteringConfig{
				MeteringPrefix: "metering-data/",
			},
		}

		loader := NewMeteringLoader(conf, zap.NewNop())
		storageConfig, err := loader.createStorageConfig()
		require.NoError(t, err)
		require.NotNil(t, storageConfig)

		assert.Equal(t, storage.ProviderTypeAzure, storageConfig.Type)
		assert.Equal(t, "metering-container", storageConfig.Bucket)
		assert.Equal(t, "metering-data/", storageConfig.Prefix)
		require.NotNil(t, storageConfig.Azure)
		assert.Equal(t, "teststorageaccount", storageConfig.Azure.AccountName)
		assert.Equal(t, "dGVzdC1hY2NvdW50LWtleQ==", storageConfig.Azure.AccountKey)
	})

	t.Run("with SAS token", func(t *testing.T) {
		t.Setenv(constants.AzureStorageAccountNameEnv, "testsasaccount")
		t.Setenv(constants.AzureStorageSASTokenEnv, "sv=2023-01-01&ss=b&srt=sco&sp=rwdlac")

		conf := &config.ServiceConfig{
			Vendor: constants.VendorAzure,
			Bucket: "metering-container",
			NextGenMeteringConfig: &config.NextGenMeteringConfig{
				MeteringPrefix: "metering-data/",
			},
		}

		loader := NewMeteringLoader(conf, zap.NewNop())
		storageConfig, err := loader.createStorageConfig()
		require.NoError(t, err)
		require.NotNil(t, storageConfig)

		assert.Equal(t, storage.ProviderTypeAzure, storageConfig.Type)
		require.NotNil(t, storageConfig.Azure)
		assert.Equal(t, "testsasaccount", storageConfig.Azure.AccountName)
		assert.Empty(t, storageConfig.Azure.AccountKey)
		assert.Equal(t, "sv=2023-01-01&ss=b&srt=sco&sp=rwdlac", storageConfig.Azure.SASToken)
	})

	t.Run("with managed identity (no key or SAS)", func(t *testing.T) {
		t.Setenv(constants.AzureStorageAccountNameEnv, "testmsiaccount")

		conf := &config.ServiceConfig{
			Vendor: constants.VendorAzure,
			Bucket: "metering-container",
			NextGenMeteringConfig: &config.NextGenMeteringConfig{
				MeteringPrefix: "metering-data/",
			},
		}

		loader := NewMeteringLoader(conf, zap.NewNop())
		storageConfig, err := loader.createStorageConfig()
		require.NoError(t, err)
		require.NotNil(t, storageConfig)

		require.NotNil(t, storageConfig.Azure)
		assert.Equal(t, "testmsiaccount", storageConfig.Azure.AccountName)
		assert.Empty(t, storageConfig.Azure.AccountKey)
		assert.Empty(t, storageConfig.Azure.SASToken)
	})

	t.Run("missing account name", func(t *testing.T) {
		t.Setenv(constants.AzureStorageAccountNameEnv, "")

		conf := &config.ServiceConfig{
			Vendor: constants.VendorAzure,
			Bucket: "metering-container",
			NextGenMeteringConfig: &config.NextGenMeteringConfig{
				MeteringPrefix: "metering-data/",
			},
		}

		loader := NewMeteringLoader(conf, zap.NewNop())
		storageConfig, err := loader.createStorageConfig()
		assert.Error(t, err)
		assert.Nil(t, storageConfig)
		assert.Contains(t, err.Error(), constants.AzureStorageAccountNameEnv)
	})
}

func TestMeteringLoader_CreateMeteringValue(t *testing.T) {
	tests := []struct {
		name     string
		value    interface{}
		unit     string
		expected uint64
	}{
		{
			name:     "uint64 value",
			value:    uint64(1000),
			unit:     "bytes",
			expected: 1000,
		},
		{
			name:     "int64 positive value",
			value:    int64(2000),
			unit:     "seconds",
			expected: 2000,
		},
		{
			name:     "int64 negative value",
			value:    int64(-500),
			unit:     "bytes",
			expected: 0,
		},
		{
			name:     "int positive value",
			value:    3000,
			unit:     "MB",
			expected: 3000,
		},
		{
			name:     "int negative value",
			value:    -100,
			unit:     "MB",
			expected: 0,
		},
		{
			name:     "float64 positive value",
			value:    1500.5,
			unit:     "cores",
			expected: 1500,
		},
		{
			name:     "float64 negative value",
			value:    -250.8,
			unit:     "cores",
			expected: 0,
		},
		{
			name:     "string value (unsupported)",
			value:    "invalid",
			unit:     "units",
			expected: 0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := createMeteringValue(tt.value, tt.unit)
			assert.Equal(t, tt.expected, result.Value)
			assert.Equal(t, tt.unit, result.Unit)
		})
	}
}

func TestMeteringLoader_CreateMeteringData(t *testing.T) {
	loader := setupTestLoader(t)
	defer loader.Close()

	selfID := "testserver001"
	category := "test-category"
	timestamp := int64(1609459200) // 2021-01-01 00:00:00 UTC
	data := []map[string]interface{}{
		{
			"cluster_id": "test-cluster",
			"value":      createMeteringValue(1000, "bytes"),
		},
	}

	result := loader.CreateMeteringData(selfID, category, timestamp, data)

	assert.Equal(t, selfID, result.SelfID)
	assert.Equal(t, category, result.Category)
	assert.Equal(t, timestamp, result.Timestamp) // Should be minute-aligned
	assert.Equal(t, data, result.Data)
}

func TestMeteringLoader_GetCurrentTimestamp(t *testing.T) {
	loader := setupTestLoader(t)
	defer loader.Close()

	timestamp := loader.GetCurrentTimestamp()

	// Check that timestamp is minute-aligned
	assert.Equal(t, int64(0), timestamp%60)

	// Check that timestamp is close to current time
	now := time.Now().Unix()
	assert.True(t, timestamp <= now)
	assert.True(t, timestamp >= now-60) // Within last minute
}

func TestMeteringLoader_WriteMeteringData(t *testing.T) {
	tlw := setupTestLoaderWithWriter(t)
	defer tlw.Close()

	ctx := context.Background()
	timestamp := tlw.loader.GetCurrentTimestamp()

	// Create test metering data
	meteringData := tlw.loader.CreateMeteringData(
		"testserver",
		"test-category",
		timestamp,
		[]map[string]interface{}{
			{
				"test_metric": createMeteringValue(500, "units"),
			},
		},
	)

	// Write the data using test writer
	err := tlw.writer.Write(ctx, meteringData)
	assert.NoError(t, err)
}

func TestMeteringLoader_LoadMeteringData(t *testing.T) {
	tlw := setupTestLoaderWithWriter(t)
	defer tlw.Close()

	ctx := context.Background()
	timestamp := tlw.loader.GetCurrentTimestamp()

	// First write some test data
	meteringData := tlw.loader.CreateMeteringData(
		"testserver",
		"test-load-category",
		timestamp,
		[]map[string]interface{}{
			{
				"load_test_metric": createMeteringValue(750, "bytes"),
			},
		},
	)

	err := tlw.writer.Write(ctx, meteringData)
	require.NoError(t, err)

	// Allow some time for the write to complete
	time.Sleep(100 * time.Millisecond)

	// Try to load the data
	timestampFiles, err := tlw.loader.LoadMeteringData(ctx, timestamp)
	if err != nil {
		// This might fail in test environment, which is acceptable
		t.Logf("LoadMeteringData failed (acceptable in test): %v", err)
		return
	}

	// If successful, verify the structure
	assert.NotNil(t, timestampFiles)
	assert.IsType(t, map[string][]string{}, timestampFiles.Files)
}

func TestMeteringLoader_LoadMeteringDataByCategory(t *testing.T) {
	tlw := setupTestLoaderWithWriter(t)
	defer tlw.Close()

	ctx := context.Background()
	timestamp := tlw.loader.GetCurrentTimestamp()
	category := "test-category-load"

	// First write some test data
	meteringData := tlw.loader.CreateMeteringData(
		"testserver",
		category,
		timestamp,
		[]map[string]interface{}{
			{
				"category_test_metric": createMeteringValue(1000, "requests"),
			},
		},
	)

	err := tlw.writer.Write(ctx, meteringData)
	require.NoError(t, err)

	// Allow some time for the write to complete
	time.Sleep(100 * time.Millisecond)

	// Try to load the data by category
	data, err := tlw.loader.LoadMeteringDataByCategory(ctx, timestamp, category)
	if err != nil {
		// This might fail in test environment due to timing or storage issues
		t.Logf("LoadMeteringDataByCategory failed (acceptable in test): %v", err)
		return
	}

	// If successful, verify the data
	assert.NotNil(t, data)
	assert.IsType(t, []*common.MeteringData{}, data)
}

func TestMeteringLoader_LoadMeteringDataByTimestamp(t *testing.T) {
	tlw := setupTestLoaderWithWriter(t)
	defer tlw.Close()

	ctx := context.Background()
	timestamp := tlw.loader.GetCurrentTimestamp()
	category := "test-timestamp"

	// Write test data for a specific timestamp
	meteringData := tlw.loader.CreateMeteringData(
		"testserver",
		category,
		timestamp,
		[]map[string]interface{}{
			{
				"timestamp_metric": createMeteringValue(uint64(100), "ops"),
			},
		},
	)

	err := tlw.writer.Write(ctx, meteringData)
	require.NoError(t, err)

	// Allow some time for writes to complete
	time.Sleep(200 * time.Millisecond)

	// Load data by timestamp
	data, err := tlw.loader.LoadMeteringDataByTimestamp(ctx, timestamp, category)
	if err != nil {
		// This might fail in test environment, which is acceptable
		t.Logf("LoadMeteringDataByTimestamp failed (acceptable in test): %v", err)
		return
	}

	// If successful, verify the data
	assert.NotNil(t, data)
	assert.IsType(t, []*common.MeteringData{}, data)
}

func TestMeteringLoader_Close(t *testing.T) {
	loader := setupTestLoader(t)

	// Close should not return error
	err := loader.Close()
	assert.NoError(t, err)

	// Calling close again should still not error
	err = loader.Close()
	assert.NoError(t, err)
}

// Integration test demonstrating a complete workflow
func TestMeteringLoader_IntegrationWorkflow(t *testing.T) {
	tlw := setupTestLoaderWithWriter(t)
	defer tlw.Close()

	ctx := context.Background()
	timestamp := tlw.loader.GetCurrentTimestamp()

	// Step 1: Create and write multiple types of metering data
	categories := []string{"compute", "storage", "network"}

	for i, category := range categories {
		meteringData := tlw.loader.CreateMeteringData(
			"integrationtestserver",
			category,
			timestamp,
			[]map[string]interface{}{
				{
					"cluster_id":   "integration-cluster",
					"metric_value": createMeteringValue(uint64(1000*(i+1)), "units"),
					"service_type": category,
				},
			},
		)

		err := tlw.writer.Write(ctx, meteringData)
		require.NoError(t, err)
	}

	// Step 2: Allow time for data to be written
	time.Sleep(200 * time.Millisecond)

	// Step 3: Verify we can list files for the timestamp
	timestampFiles, err := tlw.loader.LoadMeteringData(ctx, timestamp)
	if err != nil {
		t.Logf("Integration test - listing files failed (acceptable): %v", err)
		return
	}

	t.Logf("Integration test found %d categories", len(timestampFiles.Files))

	// Step 4: Try to read back each category
	for _, category := range categories {
		data, err := tlw.loader.LoadMeteringDataByCategory(ctx, timestamp, category)
		if err != nil {
			t.Logf("Integration test - reading category %s failed (acceptable): %v", category, err)
			continue
		}

		t.Logf("Integration test - category %s: %d records", category, len(data))
	}
}

// TestMeteringLoader_NewMeteringLoader tests the constructor
func TestMeteringLoader_NewMeteringLoader(t *testing.T) {
	logger := zap.NewNop()
	conf := &config.ServiceConfig{
		Vendor:   "local",
		LogLevel: "info",
	}

	loader := NewMeteringLoader(conf, logger)

	assert.NotNil(t, loader)
	assert.Equal(t, conf, loader.conf)
	assert.NotNil(t, loader.log)
	assert.Nil(t, loader.storage)    // Should be nil before Init
	assert.Nil(t, loader.reader)     // Should be nil before Init
	assert.Nil(t, loader.metaReader) // Should be nil before Init
}

// TestMeteringLoader_Init tests initialization with different storage providers
func TestMeteringLoader_Init(t *testing.T) {
	tests := []struct {
		name      string
		vendor    string
		bucket    string
		region    string
		wantErr   bool
		configure func(conf *config.ServiceConfig)
		setup     func(t *testing.T) func()
	}{
		{
			name:    "local filesystem",
			vendor:  "local",
			wantErr: false,
		},
		{
			name:    "AWS S3",
			vendor:  "aws",
			bucket:  "test-bucket",
			region:  "us-west-2",
			wantErr: false,
			setup: func(t *testing.T) func() {
				t.Helper()
				prevAccessKey, accessExists := os.LookupEnv("AWS_ACCESS_KEY_ID")
				prevSecretKey, secretExists := os.LookupEnv("AWS_SECRET_ACCESS_KEY")
				if err := os.Setenv("AWS_ACCESS_KEY_ID", "fake-access-key"); err != nil {
					t.Fatalf("failed to set AWS_ACCESS_KEY_ID: %v", err)
				}
				if err := os.Setenv("AWS_SECRET_ACCESS_KEY", "fake-secret-key"); err != nil {
					t.Fatalf("failed to set AWS_SECRET_ACCESS_KEY: %v", err)
				}

				return func() {
					if accessExists {
						_ = os.Setenv("AWS_ACCESS_KEY_ID", prevAccessKey)
					} else {
						_ = os.Unsetenv("AWS_ACCESS_KEY_ID")
					}
					if secretExists {
						_ = os.Setenv("AWS_SECRET_ACCESS_KEY", prevSecretKey)
					} else {
						_ = os.Unsetenv("AWS_SECRET_ACCESS_KEY")
					}
				}
			},
		},
		{
			name:    "GCP (not implemented)",
			vendor:  "gcp",
			bucket:  "test-bucket",
			wantErr: true, // GCP provider not implemented yet
		},
		{
			name:    "Azure",
			vendor:  "azure",
			bucket:  "test-bucket",
			wantErr: false,
			setup: func(t *testing.T) func() {
				t.Helper()
				prevName, nameExists := os.LookupEnv(constants.AzureStorageAccountNameEnv)
				prevKey, keyExists := os.LookupEnv(constants.AzureStorageAccountKeyEnv)
				if err := os.Setenv(constants.AzureStorageAccountNameEnv, "teststorageaccount"); err != nil {
					t.Fatalf("failed to set %s: %v", constants.AzureStorageAccountNameEnv, err)
				}
				if err := os.Setenv(constants.AzureStorageAccountKeyEnv, "dGVzdC1hY2NvdW50LWtleQ=="); err != nil {
					t.Fatalf("failed to set %s: %v", constants.AzureStorageAccountKeyEnv, err)
				}

				return func() {
					if nameExists {
						_ = os.Setenv(constants.AzureStorageAccountNameEnv, prevName)
					} else {
						_ = os.Unsetenv(constants.AzureStorageAccountNameEnv)
					}
					if keyExists {
						_ = os.Setenv(constants.AzureStorageAccountKeyEnv, prevKey)
					} else {
						_ = os.Unsetenv(constants.AzureStorageAccountKeyEnv)
					}
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var cleanup func()
			if tt.setup != nil {
				cleanup = tt.setup(t)
				if cleanup != nil {
					defer cleanup()
				}
			}

			logger := zap.NewNop()
			conf := &config.ServiceConfig{
				Vendor:   tt.vendor,
				Bucket:   tt.bucket,
				Region:   tt.region,
				LogLevel: "info",
				NextGenMeteringConfig: &config.NextGenMeteringConfig{
					MeteringPrefix: "test-prefix/",
				},
			}

			if tt.configure != nil {
				tt.configure(conf)
			}

			loader := NewMeteringLoader(conf, logger)
			err := loader.Init()

			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.NotNil(t, loader.storage)
				assert.NotNil(t, loader.reader)
				assert.NotNil(t, loader.metaReader)
				assert.NotNil(t, loader.sdkCfg)

				// Clean up
				loader.Close()
			}
		})
	}
}

// TestMeteringLoader_InitWithDebugLogger tests initialization with debug logging
func TestMeteringLoader_InitWithDebugLogger(t *testing.T) {
	logger := zap.NewNop()
	conf := &config.ServiceConfig{
		Vendor:   "local",
		LogLevel: "debug", // Debug mode
		NextGenMeteringConfig: &config.NextGenMeteringConfig{
			MeteringPrefix: "debug-prefix/",
		},
	}

	loader := NewMeteringLoader(conf, logger)
	err := loader.Init()

	assert.NoError(t, err)
	assert.NotNil(t, loader.sdkCfg)

	// Clean up
	loader.Close()
}

// TestMeteringLoader_UninitializedOperations tests operations on uninitialized loader
func TestMeteringLoader_UninitializedOperations(t *testing.T) {
	logger := zap.NewNop()
	conf := &config.ServiceConfig{
		Vendor: "local",
	}

	loader := NewMeteringLoader(conf, logger)
	ctx := context.Background()

	// Test operations without initialization
	t.Run("LoadMeteringData should fail", func(t *testing.T) {
		_, err := loader.LoadMeteringData(ctx, 1234567890)
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "not initialized")
	})

	t.Run("LoadMeteringDataByCategory should fail", func(t *testing.T) {
		_, err := loader.LoadMeteringDataByCategory(ctx, 1234567890, "test")
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "not initialized")
	})

	t.Run("LoadMeteringDataByTimestamp should fail", func(t *testing.T) {
		_, err := loader.LoadMeteringDataByTimestamp(ctx, 1234567890, "test")
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "not initialized")
	})

	t.Run("LoadMetaDataByTypeWithCategory should fail", func(t *testing.T) {
		_, err := loader.LoadMetaDataByTypeWithCategory(ctx, "cluster", common.MetaTypeLogic, "", 1234567890)
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "not initialized")
	})

	t.Run("LoadMetaDataFile should fail", func(t *testing.T) {
		_, err := loader.LoadMetaDataFile(ctx, "path")
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "not initialized")
	})

	t.Run("ListMetaDataFiles should fail", func(t *testing.T) {
		_, err := loader.ListMetaDataFiles(ctx, "prefix")
		assert.Error(t, err)
		assert.Contains(t, err.Error(), "not initialized")
	})
}

// TestMeteringLoader_CreateMetaData tests metadata creation
func TestMeteringLoader_CreateMetaData(t *testing.T) {
	loader := setupTestLoader(t)
	defer loader.Close()

	clusterID := "test-cluster-123"
	modifyTS := int64(1640995200)
	metadata := map[string]interface{}{
		"version": "1.0",
		"config": map[string]interface{}{
			"cpu_cores": 4,
			"memory_gb": 16,
		},
	}

	result := loader.CreateMetaData(clusterID, modifyTS, metadata)

	assert.Equal(t, clusterID, result.ClusterID)
	assert.Equal(t, modifyTS, result.ModifyTS)
	assert.Equal(t, metadata, result.Metadata)
}

// TestMeteringLoader_LoadMeteringDataByTimestamp_EmptyCategory tests loading all categories
func TestMeteringLoader_LoadMeteringDataByTimestamp_EmptyCategory(t *testing.T) {
	tlw := setupTestLoaderWithWriter(t)
	defer tlw.Close()

	ctx := context.Background()
	timestamp := tlw.loader.GetCurrentTimestamp()

	// Write test data for multiple categories
	categories := []string{"cat1", "cat2", "cat3"}
	for _, category := range categories {
		meteringData := tlw.loader.CreateMeteringData(
			"testserver",
			category,
			timestamp,
			[]map[string]interface{}{
				{
					"test_metric": createMeteringValue(100, "units"),
				},
			},
		)

		err := tlw.writer.Write(ctx, meteringData)
		require.NoError(t, err)
	}

	// Allow some time for writes to complete
	time.Sleep(200 * time.Millisecond)

	// Load data for all categories (empty category string)
	data, err := tlw.loader.LoadMeteringDataByTimestamp(ctx, timestamp, "")
	if err != nil {
		t.Logf("LoadMeteringDataByTimestamp with empty category failed (acceptable in test): %v", err)
		return
	}

	assert.NotNil(t, data)
	t.Logf("Loaded %d records for all categories", len(data))
}

// TestMeteringLoader_CreateMeteringValue_EdgeCases tests edge cases for value creation
func TestMeteringLoader_CreateMeteringValue_EdgeCases(t *testing.T) {
	tests := []struct {
		name     string
		value    interface{}
		unit     string
		expected uint64
	}{
		{
			name:     "zero uint64",
			value:    uint64(0),
			unit:     "bytes",
			expected: 0,
		},
		{
			name:     "zero int64",
			value:    int64(0),
			unit:     "seconds",
			expected: 0,
		},
		{
			name:     "zero int",
			value:    0,
			unit:     "MB",
			expected: 0,
		},
		{
			name:     "zero float64",
			value:    float64(0),
			unit:     "cores",
			expected: 0,
		},
		{
			name:     "large float64",
			value:    1.7976931348623157e+308,
			unit:     "cores",
			expected: ^uint64(0), // Clamped to max uint64
		},
		{
			name:     "bool value (unsupported)",
			value:    true,
			unit:     "units",
			expected: 0,
		},
		{
			name:     "nil value",
			value:    nil,
			unit:     "units",
			expected: 0,
		},
		{
			name:     "slice value (unsupported)",
			value:    []int{1, 2, 3},
			unit:     "units",
			expected: 0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := createMeteringValue(tt.value, tt.unit)
			assert.Equal(t, tt.expected, result.Value)
			assert.Equal(t, tt.unit, result.Unit)
		})
	}
}

// TestMeteringLoader_Close_Errors tests Close method with potential errors
func TestMeteringLoader_Close_Errors(t *testing.T) {
	// Test closing multiple times should not error
	loader := setupTestLoader(t)

	err1 := loader.Close()
	assert.NoError(t, err1)

	err2 := loader.Close()
	assert.NoError(t, err2)
}
