// Package securelog centralizes log hygiene for secrets.
//
// Policy:
//   - Never log password / new_password / current_password / temporary_password
//   - Never log password_hash, refresh_token, access_token, api keys
//   - go-zero mon dumps full Mongo docs at info — call SilenceMongo() at process start
package securelog

import (
	"encoding/json"
	"regexp"
	"strings"

	"github.com/zeromicro/go-zero/core/stores/mon"
)

// sensitiveJSONKeys matched case-insensitively as JSON object keys.
var sensitiveKeyPattern = regexp.MustCompile(
	`(?i)("(?:password|new_password|current_password|temporary_password|password_hash|` +
		`access_token|refresh_token|api_key|secret|authorization|cookie)"\s*:\s*)` +
		`(?:"(?:\\.|[^"\\])*"|null|true|false|-?\d+(?:\.\d+)?)`,
)

// SilenceMongo disables go-zero mon command logging (including document dumps).
// monc uses mon under the hood; without this, ReplaceOne logs password_hash.
func SilenceMongo() {
	mon.DisableLog()
}

// RedactJSON replaces known secret fields in a JSON blob with "[REDACTED]".
func RedactJSON(raw string) string {
	if raw == "" {
		return raw
	}
	return sensitiveKeyPattern.ReplaceAllString(raw, `${1}"[REDACTED]"`)
}

// RedactAny marshals v then redacts; for ad-hoc logx of structs.
func RedactAny(v any) string {
	if v == nil {
		return "null"
	}
	b, err := json.Marshal(v)
	if err != nil {
		return "[unserializable]"
	}
	return RedactJSON(string(b))
}

// MaskPassword returns a fixed placeholder (never the input).
func MaskPassword(_ string) string { return "[REDACTED]" }

// IsSensitiveKey reports if a field name must never be logged.
func IsSensitiveKey(name string) bool {
	n := strings.ToLower(strings.TrimSpace(name))
	switch n {
	case "password", "new_password", "current_password", "temporary_password",
		"password_hash", "access_token", "refresh_token", "api_key", "secret",
		"authorization", "cookie", "content": // media content is base64 blob
		return true
	default:
		return false
	}
}
