package service

import (
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/inference-service/internal/config"
)

func TestNewUsageData(t *testing.T) {
	data := NewUsageData()

	require.NotNil(t, data.Tenants)
	require.NotNil(t, data.Models)
}

func TestGetTenantUsage(t *testing.T) {
	// Load config to set default quota
	err := config.LoadConfig("")
	require.NoError(t, err)

	data := NewUsageData()
	tenantID := "test-tenant"
	expectedQuota := config.Global().Quotas.DefaultMonthlyLimit

	// First call should create new tenant usage
	usage := data.GetTenantUsage(tenantID)

	require.Equal(t, tenantID, usage.TenantID)
	require.Equal(t, int64(0), usage.CurrentUsage)
	require.Equal(t, expectedQuota, usage.QuotaLimit)

	// Second call should return existing tenant usage
	usage2 := data.GetTenantUsage(tenantID)

	require.Same(t, usage, usage2)
}

func TestAddUsage(t *testing.T) {
	// Load config to set default quota
	err := config.LoadConfig("")
	require.NoError(t, err)

	data := NewUsageData()
	tenantID := "test-tenant"
	modelName := "test-model"
	costMicroDollars := config.DollarsToMicroDollarsForTest(1.5)
	tokens := 100

	// Add usage
	data.AddUsage(tenantID, modelName, costMicroDollars, tokens)

	// Check tenant usage
	tenantUsage := data.GetTenantUsage(tenantID)
	require.Equal(t, costMicroDollars, tenantUsage.CurrentUsage)

	// Check model usage
	modelUsage := data.GetModelUsage(modelName)
	require.Equal(t, costMicroDollars, modelUsage.TotalUsage)
	require.Equal(t, int64(tokens), modelUsage.TotalTokens)
	require.Equal(t, int64(1), modelUsage.RequestCount)

	// Add more usage
	data.AddUsage(tenantID, modelName, costMicroDollars, tokens)

	// Check updated values
	require.Equal(t, costMicroDollars*2, tenantUsage.CurrentUsage)
	require.Equal(t, costMicroDollars*2, modelUsage.TotalUsage)
	require.Equal(t, int64(2), modelUsage.RequestCount)
}

func TestCanUseQuota(t *testing.T) {
	// Load config to set default quota
	err := config.LoadConfig("")
	require.NoError(t, err)

	data := NewUsageData()
	tenantID := "test-tenant"

	// Should be able to use quota initially
	require.True(t, data.CanUseQuota(tenantID, config.DollarsToMicroDollarsForTest(10.0)))

	// Add usage close to limit
	data.AddUsage(tenantID, "test-model", config.DollarsToMicroDollarsForTest(20.0), 1000)

	// Should still be able to use small amount
	require.True(t, data.CanUseQuota(tenantID, config.DollarsToMicroDollarsForTest(4.0)))

	// Should not be able to exceed quota
	require.False(t, data.CanUseQuota(tenantID, config.DollarsToMicroDollarsForTest(10.0)))
}

func TestSetQuotaLimit(t *testing.T) {
	// Load config to set default quota
	err := config.LoadConfig("")
	require.NoError(t, err)

	data := NewUsageData()
	tenantID := "test-tenant"
	newLimitMicroDollars := config.DollarsToMicroDollarsForTest(100.0)

	// Set quota limit
	data.SetQuotaLimit(tenantID, newLimitMicroDollars)

	// Check that limit was set
	tenantUsage := data.GetTenantUsage(tenantID)
	require.Equal(t, newLimitMicroDollars, tenantUsage.QuotaLimit)
}

func TestQuotaReset(t *testing.T) {
	// Load config to set default quota
	err := config.LoadConfig("")
	require.NoError(t, err)

	data := NewUsageData()
	tenantID := "test-tenant"

	// Get tenant usage and manually set reset date to past
	tenantUsage := data.GetTenantUsage(tenantID)
	tenantUsage.ResetDate = time.Now().Add(-24 * time.Hour)              // Yesterday
	tenantUsage.CurrentUsage = config.DollarsToMicroDollarsForTest(20.0) // Set some usage

	// Getting tenant usage again should reset the quota
	updatedUsage := data.GetTenantUsage(tenantID)

	require.Equal(t, int64(0), updatedUsage.CurrentUsage)
	require.True(t, updatedUsage.ResetDate.After(time.Now()))
}

func TestDollarsToMicroDollars(t *testing.T) {
	testCases := []struct {
		dollars  float64
		expected int64
	}{
		{1.0, 1_000_000},
		{0.5, 500_000},
		{25.0, 25_000_000},
		{0.00002, 20},
	}

	for _, tc := range testCases {
		result := config.DollarsToMicroDollarsForTest(tc.dollars)
		require.Equal(t, tc.expected, result)
	}
}

func TestMicroDollarsToDollars(t *testing.T) {
	testCases := []struct {
		microDollars int64
		expected     float64
	}{
		{1_000_000, 1.0},
		{500_000, 0.5},
		{25_000_000, 25.0},
		{20, 0.00002},
	}

	for _, tc := range testCases {
		result := config.MicroDollarsToDollarsForDisplay(tc.microDollars)
		require.Equal(t, tc.expected, result)
	}
}

func TestBackupFileGeneration(t *testing.T) {
	// Create a temporary directory for testing
	tempDir := t.TempDir()

	// Create a mock usage service with local storage
	storage := NewLocalStorageBackend(tempDir)
	service := &UsageService{
		storage: storage,
		dataKey: "usage_data.json",
	}

	// Test daily backup file generation
	testDate := time.Date(2024, 1, 15, 12, 0, 0, 0, time.UTC)
	expectedKey := "usage_202401/usage_data_backup_2024-01-15.json"
	actualKey := service.getDailyBackupFile(testDate)

	require.Equal(t, expectedKey, actualKey)
}
