package k8sresource

import (
	"encoding/json"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

const sensitiveDataMask = "****"

// MaskFunc mutates obj in place for detail output.
type MaskFunc func(obj *unstructured.Unstructured)

// ApplyGKMask runs the registered mask for resolved G+K, if any.
// Unlisted GK is a no-op. Mask failures are best-effort and never abort the request.
func ApplyGKMask(gvk schema.GroupVersionKind, obj *unstructured.Unstructured) {
	if obj == nil {
		return
	}
	fns, ok := gkMaskFuncs[gvk.Group]
	if !ok {
		return
	}
	fn, ok := fns[gvk.Kind]
	if !ok {
		return
	}
	fn(obj)
}

func maskCluster(obj *unstructured.Unstructured) {
	if obj == nil {
		return
	}
	m := obj.Object
	maskKubernetesCaData(m)
	maskScalarIfNonEmpty(m, "spec", "cluster", "tidb", "audit", "azure", "sasToken")
	maskScalarIfNonEmpty(m, "spec", "cluster", "tidb", "precheckAudit", "azure", "sasToken")
	maskScalarIfNonEmpty(m, "status", "sqlEndpoints", "publicEndpoint", "token")
	maskScalarIfNonEmpty(m, "status", "sqlEndpoints", "privateEndpoint", "token")
	maskScalarIfNonEmpty(m, "status", "metrics", "credentialConfig")
	maskScalarIfNonEmpty(m, "status", "proxy", "credentialConfig")
	maskClusterExtraEndpointTokens(m)
}

func maskReplay(obj *unstructured.Unstructured) {
	if obj == nil {
		return
	}
	maskKubernetesCaData(obj.Object)
}

func maskKubernetesCaData(obj map[string]any) {
	maskScalarIfNonEmpty(obj, "spec", "kubernetes", "caData")
}

func maskAPPRoute(obj *unstructured.Unstructured) {
	if obj == nil {
		return
	}
	forCluster, found, err := unstructured.NestedString(obj.Object, "spec", "forCluster")
	if err != nil || !found || forCluster == "" {
		return
	}
	var cluster map[string]any
	if err := json.Unmarshal([]byte(forCluster), &cluster); err != nil {
		return
	}
	maskScalarIfNonEmpty(cluster, "securityConfigurations", "tidbAuthTokenJwks")
	data, err := json.Marshal(cluster)
	if err != nil {
		return
	}
	_ = unstructured.SetNestedField(obj.Object, string(data), "spec", "forCluster")
}

func maskScalarIfNonEmpty(obj map[string]any, path ...string) {
	if len(path) == 0 {
		return
	}
	val, found, err := unstructured.NestedFieldNoCopy(obj, path...)
	if err != nil || !found || val == nil {
		return
	}
	s, ok := val.(string)
	if !ok || s == "" {
		return
	}
	_ = unstructured.SetNestedField(obj, sensitiveDataMask, path...)
}

func maskClusterExtraEndpointTokens(obj map[string]any) {
	items, found, err := unstructured.NestedSlice(obj, "status", "extraEndpoints")
	if err != nil || !found {
		return
	}
	for i, item := range items {
		ep, ok := item.(map[string]any)
		if !ok {
			continue
		}
		maskScalarIfNonEmpty(ep, "sqlEndpoints", "publicEndpoint", "token")
		maskScalarIfNonEmpty(ep, "sqlEndpoints", "privateEndpoint", "token")
		items[i] = ep
	}
	_ = unstructured.SetNestedSlice(obj, items, "status", "extraEndpoints")
}

// gkMaskFuncs holds per-GK detail output masks. Unlisted GK is unmasked beyond common sanitize.
var gkMaskFuncs = map[string]map[string]MaskFunc{
	"cloud.pingcap.com": {
		"Cluster": maskCluster,
		"Replay":  maskReplay,
	},
	"dedicated.regionalserver.tidbcloud.com": {
		"APPRoute": maskAPPRoute,
	},
}
