package logger

import (
	"log/slog"
	"regexp"
	"strings"
)

var sensitiveClientLogKeys = map[string]struct{}{
	"password":         {},
	"currentpassword":  {},
	"newpassword":      {},
	"oldpassword":      {},
	"token":            {},
	"accesstoken":      {},
	"refreshtoken":     {},
	"secret":           {},
	"clientsecret":     {},
	"authorization":    {},
	"cookie":           {},
	"setcookie":        {},
	"verificationcode": {},
	"code":             {},
	"loginchallenge":   {},
	"loginverifier":    {},
	"sessionjwt":       {},
	"accessjwt":        {},
	"refreshjwt":       {},
}

var (
	logJSONSensitivePattern = regexp.MustCompile(`(?i)"(password|currentpassword|newpassword|oldpassword|token|accesstoken|refreshtoken|secret|clientsecret|authorization|cookie|setcookie|verificationcode|code|loginchallenge|loginverifier|sessionjwt|accessjwt|refreshjwt)"\s*:\s*"[^"]*"`)
	logKVPattern            = regexp.MustCompile(`(?i)\b(password|current_password|currentpassword|new_password|newpassword|old_password|oldpassword|token|access_token|accesstoken|refresh_token|refreshtoken|authorization|cookie|session_jwt|sessionjwt|access_jwt|accessjwt|refresh_jwt|refreshjwt)\b\s*[:=]\s*([^\s,;]+)`)
)

func IsProductionEnv(appEnv string) bool {
	return IsProductionLikeEnv(appEnv)
}

func parseOptionalBoolFlag(raw string) (bool, bool) {
	switch strings.ToLower(strings.TrimSpace(raw)) {
	case "1", "true", "yes", "y", "on":
		return true, true
	case "0", "false", "no", "n", "off":
		return false, true
	default:
		return false, false
	}
}

func ClientDebugEnabled(appEnv, debugFlag string) bool {
	if enabled, ok := parseOptionalBoolFlag(debugFlag); ok {
		return enabled
	}
	if !IsProductionEnv(appEnv) {
		return true
	}
	return false
}

func NormalizeClientLogLevel(level string) slog.Level {
	switch strings.ToUpper(strings.TrimSpace(level)) {
	case "SEVERE", "ERROR":
		return slog.LevelError
	case "WARNING", "WARN":
		return slog.LevelWarn
	case "DEBUG", "FINE", "TRACE":
		return slog.LevelDebug
	default:
		return slog.LevelInfo
	}
}

func ShouldAcceptClientLog(appEnv, productionDebugFlag, level string) bool {
	if ClientDebugEnabled(appEnv, productionDebugFlag) {
		return true
	}
	return NormalizeClientLogLevel(level) >= slog.LevelWarn
}

func ShouldFilterNoisyClientInfo(appEnv, productionDebugFlag, message string) bool {
	if ClientDebugEnabled(appEnv, productionDebugFlag) {
		return false
	}
	msg := strings.ToLower(message)
	return strings.Contains(msg, "navigating to") ||
		strings.Contains(msg, "going to") ||
		strings.Contains(msg, "redirecting to") ||
		strings.Contains(msg, "full paths for routes")
}

func SanitizeClientLogMessage(message string) string {
	if strings.TrimSpace(message) == "" {
		return message
	}
	sanitized := logJSONSensitivePattern.ReplaceAllStringFunc(message, func(segment string) string {
		parts := strings.SplitN(segment, ":", 2)
		if len(parts) != 2 {
			return segment
		}
		return parts[0] + `:"*****"`
	})
	sanitized = logKVPattern.ReplaceAllString(sanitized, `$1=*****`)
	return sanitized
}

func SanitizeClientLogData(data map[string]interface{}) map[string]interface{} {
	if len(data) == 0 {
		return data
	}
	out := make(map[string]interface{}, len(data))
	for k, v := range data {
		if isSensitiveClientLogKey(k) {
			out[k] = "*****"
			continue
		}
		out[k] = sanitizeClientLogValue(v)
	}
	return out
}

func sanitizeClientLogValue(v interface{}) interface{} {
	switch val := v.(type) {
	case map[string]interface{}:
		return SanitizeClientLogData(val)
	case []interface{}:
		next := make([]interface{}, len(val))
		for i := range val {
			next[i] = sanitizeClientLogValue(val[i])
		}
		return next
	case string:
		return SanitizeClientLogMessage(val)
	default:
		return val
	}
}

func isSensitiveClientLogKey(key string) bool {
	normalized := strings.ToLower(strings.TrimSpace(key))
	normalized = strings.ReplaceAll(normalized, "-", "")
	normalized = strings.ReplaceAll(normalized, "_", "")
	normalized = strings.ReplaceAll(normalized, ".", "")
	normalized = strings.ReplaceAll(normalized, " ", "")
	_, ok := sensitiveClientLogKeys[normalized]
	return ok
}
