package commerce

import (
	"context"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"qycw/go-server/internal/plugins"
)

func TestSafePluginID(t *testing.T) {
	for _, value := range []string{"mengnai_bt", "generic-blank", "Plugin123"} {
		if !safePluginID(value) {
			t.Fatalf("expected safe plugin id: %s", value)
		}
	}
}

func TestSafePluginIDRejectsPathTraversal(t *testing.T) {
	for _, value := range []string{"../x", "x/y", "x y", ""} {
		if safePluginID(value) {
			t.Fatalf("expected unsafe plugin id: %q", value)
		}
	}
}

func TestAdminPluginPreviewMapMatchesFrontendContract(t *testing.T) {
	manifest := plugins.Manifest{
		SchemaVersion: 1,
		ID:            "aliyunsms",
		Type:          plugins.CapabilitySMS,
		Name:          "Aliyun SMS",
		Version:       "1.0.0-go",
		Runtime:       plugins.RuntimeJSONRPC,
		Capabilities:  []string{plugins.CapabilitySMS},
		ConfigSchema: map[string]any{
			"fields": []any{map[string]any{"key": "access_key_id", "label": "AccessKey ID"}},
		},
		Requirements: map[string]any{
			"python_packages": []any{},
			"network":         []any{"Aliyun DYPNS SMS API"},
		},
	}

	installPath := filepath.Join("plugins", "aliyunsms", "1.0.0-go")
	preview := adminPluginPreviewMap(manifest, "aliyunsms.zip", installPath, false)
	if _, nested := preview["plugin"]; nested {
		t.Fatalf("preview must expose the manifest directly: %#v", preview)
	}
	if preview["runtime"] != plugins.RuntimeJSONRPC {
		t.Fatalf("runtime = %#v", preview["runtime"])
	}
	if fields, ok := preview["config_fields"].([]any); !ok || len(fields) != 1 {
		t.Fatalf("config fields = %#v", preview["config_fields"])
	}
	if preview["requirements"] == nil {
		t.Fatal("requirements missing from preview")
	}
	if preview["install_path"] != filepath.ToSlash(installPath) {
		t.Fatalf("install path = %#v", preview["install_path"])
	}
	if preview["exists"] != false {
		t.Fatalf("exists = %#v", preview["exists"])
	}
}

func TestAdminPluginManifestSelectionIsSingleProvider(t *testing.T) {
	selected := plugins.Manifest{
		ID: "aliyunemail", Type: plugins.CapabilityEmail, Name: "阿里云邮件",
		Version: "1.0.0", Runtime: plugins.RuntimeJSONRPC,
		Capabilities: []string{plugins.CapabilityEmail}, Enabled: true,
	}
	available := selected
	available.ID = "e"
	available.Name = "通用 SMTP 邮件"
	selection := adminPluginSelection{
		featureProviders: map[string]string{plugins.CapabilityEmail: "aliyunemail"},
		featureEnabled:   map[string]bool{plugins.CapabilityEmail: true},
		paymentEnabled:   map[string]bool{},
	}
	selectedMap := adminPluginManifestWithSelection(selected, selection)
	availableMap := adminPluginManifestWithSelection(available, selection)
	if selectedMap["enabled"] != true || selectedMap["current"] != true {
		t.Fatalf("selected plugin state = %#v", selectedMap)
	}
	if availableMap["enabled"] != false || availableMap["current"] != false || availableMap["available"] != true {
		t.Fatalf("available plugin state = %#v", availableMap)
	}
}

func TestConfiguredPluginIDSupportsGoAndLegacyProviders(t *testing.T) {
	for _, provider := range []string{"plugin:aliyunsms", "mode.sms.aliyunsms.provider:create_provider"} {
		if got := configuredPluginID(provider, plugins.CapabilitySMS); got != "aliyunsms" {
			t.Fatalf("configuredPluginID(%q) = %q", provider, got)
		}
	}
	if got := configuredPluginID("mode.email.aliyunemail.provider:create_provider", plugins.CapabilitySMS); got != "" {
		t.Fatalf("mismatched capability parsed as %q", got)
	}
}

func TestMergeAdminPluginConfigPreservesRedactedSecrets(t *testing.T) {
	base := map[string]any{"access_key_id": "old-id", "access_key_secret": "real-secret"}
	merged := mergeAdminPluginConfig(base, map[string]any{
		"access_key_id":     "new-id",
		"access_key_secret": "***",
	})
	if merged["access_key_id"] != "new-id" {
		t.Fatalf("access_key_id = %#v", merged["access_key_id"])
	}
	if merged["access_key_secret"] != "real-secret" {
		t.Fatalf("redacted secret replaced the stored value: %#v", merged["access_key_secret"])
	}
}

func TestPublicGoProvisionManifestPreservesWorkflowMetadata(t *testing.T) {
	manifest := plugins.Manifest{
		SchemaVersion: 1,
		ID:            "mengnai_bt",
		Type:          plugins.CapabilityProvision,
		Name:          "梦奈宝塔自动开通",
		Version:       "1.1.0-go",
		Runtime:       plugins.RuntimeJSONRPC,
		Provider:      "plugin:mengnai_bt",
		Capabilities:  []string{plugins.CapabilityProvision, "manage", "test"},
		ConfigSchema: map[string]any{
			"fields": []any{map[string]any{"key": "mnbt_url", "label": "MNBT 接口地址"}},
		},
		ManageActions: []map[string]any{
			{"key": "sync", "audience": "both"},
			{"key": "terminate", "audience": "admin"},
		},
		TestLinks: []map[string]any{{"label": "连接验证", "url": "/test"}},
		ProductPage: map[string]any{
			"purchase_fields": []any{map[string]any{"key": "contact", "label": "联系信息"}},
		},
	}

	got := publicGoPluginManifest(manifest)
	if got["provider"] != "plugin:mengnai_bt" {
		t.Fatalf("provider = %#v", got["provider"])
	}
	if len(asSlice(got["manage_actions"])) != 2 || len(asSlice(got["config_fields"])) != 1 {
		t.Fatalf("manifest metadata missing: %#v", got)
	}
	page := got["product_page"].(map[string]any)
	if len(asSlice(page["purchase_fields"])) != 1 {
		t.Fatalf("purchase fields = %#v", page["purchase_fields"])
	}
	visible := userVisiblePluginManageActions(got)
	if len(visible) != 1 || visible[0].(map[string]any)["key"] != "sync" {
		t.Fatalf("user-visible actions = %#v", visible)
	}
}

func TestProvisionInterfaceSecretsAreEncryptedAndRedacted(t *testing.T) {
	pluginRoot := filepath.Join("..", "..", "..", "..", "mode", "provision", "mengnai_bt")
	pluginService := plugins.NewService([]string{pluginRoot}, time.Second, nil)
	service := NewService(nil, nil, nil, pluginService)
	payload, err := service.prepareProvisionInterfacePayload(context.Background(), 0, map[string]any{
		"plugin_id": "mengnai_bt",
		"config": map[string]any{
			"mnbt_url":  "https://panel.example.com",
			"api_key":   "api-secret",
			"bt_secret": "call-secret",
		},
	})
	if err != nil {
		t.Fatal(err)
	}
	raw := string(payload["config"].(JSONRaw))
	if strings.Contains(raw, "api-secret") || strings.Contains(raw, "call-secret") {
		t.Fatalf("stored config contains plaintext secrets: %s", raw)
	}
	runtimeConfig := provisionInterfaceRuntimeConfig(payload["config"])
	if runtimeConfig["api_key"] != "api-secret" || runtimeConfig["bt_secret"] != "call-secret" {
		t.Fatalf("runtime config did not decrypt: %#v", runtimeConfig)
	}
	row := map[string]any{"plugin_id": "mengnai_bt", "config": payload["config"]}
	service.redactProvisionInterfaceRow(context.Background(), row)
	redacted := row["config"].(map[string]any)
	if redacted["api_key"] != "***" || redacted["bt_secret"] != "***" {
		t.Fatalf("admin config was not redacted: %#v", redacted)
	}
}

func TestPluginDiagnosticsKeepsFrontendContractWhenRuntimeIsDisabled(t *testing.T) {
	data, err := NewService(nil, nil, nil).AdminPluginDiagnostics(context.Background())
	if err != nil {
		t.Fatalf("AdminPluginDiagnostics() error = %v", err)
	}
	for _, key := range []string{"summary", "warnings", "channels", "provision", "verification", "security"} {
		if _, ok := data[key]; !ok {
			t.Fatalf("diagnostics missing %q: %#v", key, data)
		}
	}
	summary := data["summary"].(map[string]any)
	if summary["error_count"] != 1 || summary["issue_count"] != 1 {
		t.Fatalf("runtime failure must be visible in summary: %#v", summary)
	}
}

func TestPluginDiagnosticsWorksWithoutDatabase(t *testing.T) {
	pluginService := plugins.NewService([]string{}, time.Second, nil)
	data, err := NewService(nil, nil, nil, pluginService).AdminPluginDiagnostics(context.Background())
	if err != nil {
		t.Fatalf("AdminPluginDiagnostics() error = %v", err)
	}
	channels, ok := data["channels"].([]map[string]any)
	if !ok || len(channels) != 4 {
		t.Fatalf("channels = %#v, want four notification channels", data["channels"])
	}
	provision := data["provision"].(map[string]any)
	if _, ok := provision["summary"].(map[string]any); !ok {
		t.Fatalf("provision summary missing: %#v", provision)
	}
}

func TestPluginDiagnosticsDetectsRequiredConfigAndInvalidProductBinding(t *testing.T) {
	manifest := plugins.Manifest{
		ID:   "provision-demo",
		Name: "Provision Demo",
		ConfigSchema: map[string]any{
			"fields": []any{
				map[string]any{"key": "endpoint", "label": "Endpoint", "required": true},
			},
		},
	}
	missing := diagnosticMissingFields(manifest, map[string]any{})
	if len(missing) != 1 || missing[0]["key"] != "endpoint" {
		t.Fatalf("required config fields = %#v", missing)
	}
	product := Product{ID: 7, Name: "Demo", SKU: "demo", Status: "active", APIType: "plugin"}
	issues := diagnosticProductIssues([]Product{product}, map[int64]map[string]any{}, map[string]plugins.Manifest{})
	if len(issues) != 1 || len(stringSliceFromAny(issues[0]["issues"])) < 3 {
		t.Fatalf("product binding issues = %#v", issues)
	}
}
