package discovery

import (
	"log"
	"net"
	"strings"
	"sync/atomic"
)

// verboseLogging toggles the per-packet / per-header diagnostic output
// that was historically emitted unconditionally during UPnP and mDNS
// discovery. The service binary leaves it at its zero value (off) so
// the log stays useful at info-level; the CLI's `discover` command
// flips it on so interactive runs surface full protocol details.
//
// Stored as an int32 so the read path in logVerbose is allocation-
// free (atomic.Bool would work too on Go 1.19+, but a uint8 lookup
// keeps the toggle hot-path even on older toolchains we still build
// against in CI).
var verboseLogging atomic.Bool

// SetVerbose enables (or disables) the package-wide verbose-discovery
// log toggle. Safe to call from any goroutine.
func SetVerbose(v bool) {
	verboseLogging.Store(v)
}

// IsVerbose reports the current value of the verbose toggle. Mainly
// for tests that want to assert the CLI flipped it on.
func IsVerbose() bool {
	return verboseLogging.Load()
}

// logVerbose forwards to log.Printf only when verbose-discovery
// logging is enabled. The fast path (verbose off) is a single
// atomic load + branch, so it's safe to scatter calls liberally
// across the hot path.
func logVerbose(format string, args ...any) {
	if verboseLogging.Load() {
		log.Printf(format, args...)
	}
}

// sanitizeLog strips newline characters from s to prevent log-injection
// (CodeQL go/log-injection). Values from speakers, HTTP requests, and
// external APIs may contain attacker-controlled newlines.
func sanitizeLog(s string) string {
	s = strings.ReplaceAll(s, "\n", `\n`)
	s = strings.ReplaceAll(s, "\r", `\r`)

	return s
}

// remoteAddrString safely converts a net.Addr to a string, returning
// an empty string when addr is nil (dns.ResponseWriter.RemoteAddr may
// return nil in unit-test contexts).
func remoteAddrString(w interface{ RemoteAddr() net.Addr }) string {
	if ra := w.RemoteAddr(); ra != nil {
		return ra.String()
	}

	return ""
}
