//go:build unit

package admin

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/Wei-Shaw/sub2api/internal/config"
	"github.com/Wei-Shaw/sub2api/internal/pkg/response"
	"github.com/Wei-Shaw/sub2api/internal/service"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"
)

func TestDiffSettings_DetectsGlobalPlatformQuotaChange(t *testing.T) {
	five := 5.0
	ten := 10.0
	before := &service.SystemSettings{
		DefaultPlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
			"anthropic": {DailyLimitUSD: &five},
		},
	}
	after := &service.SystemSettings{
		DefaultPlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
			"anthropic": {DailyLimitUSD: &ten},
		},
	}

	changed := diffSettings(before, after, nil, nil, UpdateSettingsRequest{})
	found := false
	for _, key := range changed {
		if key == service.SettingKeyDefaultPlatformQuotas {
			found = true
			break
		}
	}
	if !found {
		t.Errorf("expected change detection for default platform quotas, got %v", changed)
	}
}

func TestDiffSettings_NoChangeWhenEqual(t *testing.T) {
	five := 5.0
	before := &service.SystemSettings{
		DefaultPlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
			"anthropic": {DailyLimitUSD: &five},
		},
	}
	after := &service.SystemSettings{
		DefaultPlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
			"anthropic": {DailyLimitUSD: &five},
		},
	}

	changed := diffSettings(before, after, nil, nil, UpdateSettingsRequest{})
	for _, key := range changed {
		if key == service.SettingKeyDefaultPlatformQuotas {
			t.Error("equal values should not be detected as changed")
		}
	}
}

func TestSettingsAuditRequestDoesNotInheritStoredTencentSecrets(t *testing.T) {
	req := UpdateSettingsRequest{
		TencentCaptchaAppSecretKey:   "  ",
		TencentCaptchaCloudSecretID:  "\t",
		TencentCaptchaCloudSecretKey: "\n",
	}

	auditReq := settingsAuditRequest(req)
	req.TencentCaptchaAppSecretKey = "stored-app-secret"
	req.TencentCaptchaCloudSecretID = "stored-secret-id"
	req.TencentCaptchaCloudSecretKey = "stored-secret-key"

	require.Empty(t, auditReq.TencentCaptchaAppSecretKey)
	require.Empty(t, auditReq.TencentCaptchaCloudSecretID)
	require.Empty(t, auditReq.TencentCaptchaCloudSecretKey)
}

func TestDiffSettings_DetectsCompactHomeChange(t *testing.T) {
	changed := diffSettings(
		&service.SystemSettings{},
		&service.SystemSettings{CompactHomeEnabled: true},
		nil,
		nil,
		UpdateSettingsRequest{},
	)

	require.Contains(t, changed, service.SettingKeyCompactHomeEnabled)
}

func TestEqualNullableFloat(t *testing.T) {
	five := 5.0
	five2 := 5.0
	ten := 10.0
	cases := []struct {
		a, b *float64
		want bool
	}{
		{nil, nil, true},
		{&five, nil, false},
		{nil, &five, false},
		{&five, &five2, true},
		{&five, &ten, false},
	}
	for _, c := range cases {
		if got := equalNullableFloat(c.a, c.b); got != c.want {
			t.Errorf("equalNullableFloat(%v, %v) = %v, want %v", c.a, c.b, got, c.want)
		}
	}
}

func TestEqualPlatformQuotaSettings_DetectsPerWindowChange(t *testing.T) {
	five := 5.0
	ten := 10.0
	before := map[string]*service.DefaultPlatformQuotaSetting{
		"anthropic": {DailyLimitUSD: &five},
	}
	after := map[string]*service.DefaultPlatformQuotaSetting{
		"anthropic": {DailyLimitUSD: &ten},
	}
	if equalPlatformQuotaSettings(before, after) {
		t.Error("expected unequal")
	}
}

func TestAppendAuthSourceDefaultChanges_DetectsPerWindow(t *testing.T) {
	five := 5.0
	ten := 10.0
	before := &service.AuthSourceDefaultSettings{
		LinuxDo: service.ProviderDefaultGrantSettings{
			PlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
				"anthropic": {DailyLimitUSD: &five},
			},
		},
	}
	after := &service.AuthSourceDefaultSettings{
		LinuxDo: service.ProviderDefaultGrantSettings{
			PlatformQuotas: map[string]*service.DefaultPlatformQuotaSetting{
				"anthropic": {DailyLimitUSD: &ten},
			},
		},
	}

	changed := appendAuthSourceDefaultChanges([]string{}, before, after)
	// 改动 B5：整体替换语义，审计 log 发单个 JSON key，而非展开 84 个扁平 key。
	key := service.SettingKeyAuthSourcePlatformQuotas("linuxdo")
	found := false
	for _, k := range changed {
		if k == key {
			found = true
			break
		}
	}
	if !found {
		t.Errorf("expected %q in changed, got %v", key, changed)
	}
}

// TestSettingHandler_AuthSourcePlatformQuotas_PutGetRoundTrip 验证 Bug A 修复：
// PUT 发 auth_source_default_email_platform_quotas，GET 能读回相同值（端到端往返）。
func TestSettingHandler_AuthSourcePlatformQuotas_PutGetRoundTrip(t *testing.T) {
	gin.SetMode(gin.TestMode)
	repo := &settingHandlerRepoStub{
		values: map[string]string{
			service.SettingKeyPromoCodeEnabled: "true",
		},
	}
	svc := service.NewSettingService(repo, &config.Config{Default: config.DefaultConfig{UserConcurrency: 5}})
	handler := NewSettingHandler(svc, nil, nil, nil, nil, nil, nil)

	// PUT：发 email platform quota（openai monthly=20）
	putBody := map[string]any{
		"auth_source_default_email_platform_quotas": map[string]any{
			"openai": map[string]any{
				"monthly": 20,
			},
		},
	}
	rawBody, err := json.Marshal(putBody)
	require.NoError(t, err)

	rec := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(rec)
	c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/admin/settings", bytes.NewReader(rawBody))
	c.Request.Header.Set("Content-Type", "application/json")
	handler.UpdateSettings(c)
	require.Equal(t, http.StatusOK, rec.Code)

	// 验证 DB 中写入了 JSON key
	jsonKey := service.SettingKeyAuthSourcePlatformQuotas("email")
	require.NotEmpty(t, repo.values[jsonKey], "expected JSON key to be written to DB")

	// GET：验证响应中 auth_source_default_email_platform_quotas.openai.monthly = 20
	rec2 := httptest.NewRecorder()
	c2, _ := gin.CreateTestContext(rec2)
	c2.Request = httptest.NewRequest(http.MethodGet, "/api/v1/admin/settings", nil)
	handler.GetSettings(c2)
	require.Equal(t, http.StatusOK, rec2.Code)

	var resp response.Response
	require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
	data, ok := resp.Data.(map[string]any)
	require.True(t, ok)

	emailPQ, ok := data["auth_source_default_email_platform_quotas"].(map[string]any)
	require.True(t, ok, "expected auth_source_default_email_platform_quotas to be a map")
	openaiPQ, ok := emailPQ["openai"].(map[string]any)
	require.True(t, ok, "expected openai entry in email platform quotas")
	monthly, ok := openaiPQ["monthly"].(float64)
	require.True(t, ok, "expected monthly to be float64")
	require.Equal(t, float64(20), monthly, "expected openai monthly=20")
}
