package report

import (
	"log"
	"sync"
	"time"
)

// Controller self-log tail (v0.116.0 observability) — the CONTROLLER's own debug
// ring riding the report channel, the exact consume-once ACK-flag shape of
// logtail.go (which stays byte-compatible; these are ADDITIVE fields): the hub
// stores a pending per-customer request; the report ACK advertises it as
// controller_log_requested; the NEXT report carries controller_log_tail; the hub
// clears the pending request on arrival. A failed push leaves the hub request
// pending — the next ACK re-arms it (fail-safe retry, no duplicate shipping).

// controllerLogMaxBytes caps the shipped tail (newest lines kept — the ring's
// Lines budget, mirrored agent-side at the same 128 KB).
const controllerLogMaxBytes = 128 * 1024

// ControllerLogTail is the report's on-demand controller ring tail.
type ControllerLogTail struct {
	CollectedAt time.Time `json:"collected_at"`
	Lines       []string  `json:"lines"`
}

var (
	selfTailMu      sync.Mutex
	selfTailPending bool
	selfTailSource  func(maxBytes int) []string
)

// SetPendingControllerLog records the ACK's controller_log_requested flag. The
// hub is the source of truth: an ACK without the flag clears any stale pending.
func SetPendingControllerLog(requested bool) {
	selfTailMu.Lock()
	defer selfTailMu.Unlock()
	selfTailPending = requested
}

// SetControllerLogSource wires the debug ring (web.LogBuffer.Lines) — set once by
// main.go at startup. Unset means a pending request is silently unfulfillable
// (drained but nothing shipped; the hub re-arms on the next ACK).
func SetControllerLogSource(src func(maxBytes int) []string) {
	selfTailMu.Lock()
	defer selfTailMu.Unlock()
	selfTailSource = src
}

// drainPendingControllerLog takes + CLEARS the pending flag and returns the
// source (consume-once controller-side, the drainPendingLogTails shape).
func drainPendingControllerLog() (func(maxBytes int) []string, bool) {
	selfTailMu.Lock()
	defer selfTailMu.Unlock()
	pending := selfTailPending
	selfTailPending = false
	return selfTailSource, pending
}

// buildControllerLogTail attaches the ring tail when a pull is pending. The INFO
// line is the CUSTOMER-VISIBLE transparency record (it lands in the ring/viewer):
// an operator pull of this box's controller log is never silent.
func buildControllerLogTail(logger *log.Logger) *ControllerLogTail {
	src, pending := drainPendingControllerLog()
	if !pending || src == nil {
		return nil
	}
	// Logged BEFORE collecting, so the transparency line rides in the tail it announces.
	if logger != nil {
		logger.Printf("[INFO] [report] operator log pull served (controller ring)")
	}
	lines := src(controllerLogMaxBytes)
	return &ControllerLogTail{CollectedAt: time.Now().UTC(), Lines: lines}
}
