package audit

import (
	"sync/atomic"
	"time"
)

// hotConfig holds hot-reloadable configuration for the audit logger.
// All fields use atomic types for lock-free concurrent access.
type hotConfig struct {
	flushInterval      atomic.Int64 // stored as nanoseconds
	includeGetRequests atomic.Bool
	maxBodySize        atomic.Int32
	sanitizer          atomic.Pointer[Sanitizer]
}

// newHotConfig creates a hotConfig from the given Config.
func newHotConfig(cfg *Config) *hotConfig {
	h := &hotConfig{}
	h.update(cfg)
	return h
}

// update applies new configuration values.
// The sanitizer is updated first to ensure "better safe than sorry" - during the brief
// inconsistency window, we prefer to over-redact (using new sensitive fields) rather
// than under-redact (using old sensitive fields).
// Note: Individual field updates are atomic, but the overall update is not atomic as a whole.
// During an update, readers may observe a mix of old and new field values. For audit logging,
// this brief inconsistency is acceptable as it doesn't affect correctness, only minor
// differences in which fields get redacted during the transition window.
func (h *hotConfig) update(cfg *Config) {
	// Update sanitizer first - this is the most security-critical field.
	// By updating it first, we ensure that during the transition window,
	// any new sensitive fields are already being redacted.
	sanitizer := NewSanitizer(cfg.SensitiveQueryParams, cfg.SensitiveBodyFields)
	h.sanitizer.Store(sanitizer)

	// Then update other configuration fields
	flushInterval := cfg.FlushInterval
	if flushInterval <= 0 {
		flushInterval = defaultFlushInterval
	}
	h.flushInterval.Store(int64(flushInterval))

	h.includeGetRequests.Store(cfg.IncludeGetRequests)

	maxBodySize := cfg.MaxBodySize
	if maxBodySize <= 0 {
		// MaxBodySize <= 0 means request body logging is disabled.
		maxBodySize = 0
	}
	// Defensive: avoid int32 overflow on extremely large values (treat as disabled).
	if maxBodySize > 1<<31-1 {
		maxBodySize = 0
	}
	h.maxBodySize.Store(int32(maxBodySize))
}

// getFlushInterval returns the current flush interval.
func (h *hotConfig) getFlushInterval() time.Duration {
	return time.Duration(h.flushInterval.Load())
}

// getIncludeGetRequests returns whether GET requests should be logged.
func (h *hotConfig) getIncludeGetRequests() bool {
	return h.includeGetRequests.Load()
}

// getMaxBodySize returns the maximum body size to log.
func (h *hotConfig) getMaxBodySize() int {
	return int(h.maxBodySize.Load())
}

// getSanitizer returns the current sanitizer.
func (h *hotConfig) getSanitizer() *Sanitizer {
	return h.sanitizer.Load()
}
