package library

import (
	"sync"

	"github.com/jackc/pgx/v5/pgtype"

	"git.fabledsword.com/bvandeusen/minstrel/internal/eventbus"
)

// Package-level bus pointer for scan lifecycle events (#392). Set once
// at startup via SetEventBus; reads are nil-safe so test contexts that
// never call SetEventBus simply skip publishing.
//
// A package-global is the right shape here because:
//
//   - There is exactly one bus per process (the same instance powers
//     the SSE subscriber endpoint and every publisher across the codebase).
//   - Threading it through RunScan + TryStartScan + the Scheduler +
//     every test caller would touch ~10 call sites without changing
//     behavior at the boundaries that don't care.
//   - Other Go stdlib packages use the same pattern (log.SetDefault,
//     etc.) for process-wide singletons.
var (
	busMu sync.RWMutex
	bus   *eventbus.Bus
)

// SetEventBus wires the live-event bus into the library package so scan
// runs emit scan.run_started / scan.run_finished events. Call once during
// process startup; subsequent calls overwrite the previous bus.
func SetEventBus(b *eventbus.Bus) {
	busMu.Lock()
	defer busMu.Unlock()
	bus = b
}

// publishScanEvent broadcasts a scan lifecycle event. No-op when no bus
// has been set. Scan events are always broadcast (empty UserID) because
// only admins see the scan status card; non-admin clients' invalidation
// dispatcher will receive the event but won't have any provider keyed on
// scan status, so the receive is harmless.
func publishScanEvent(kind string, runID pgtype.UUID, data map[string]any) {
	busMu.RLock()
	b := bus
	busMu.RUnlock()
	if b == nil {
		return
	}
	payload := map[string]any{"run_id": uuidToBusString(runID)}
	for k, v := range data {
		payload[k] = v
	}
	b.Publish(eventbus.Event{
		Kind:   kind,
		UserID: "", // broadcast
		Data:   payload,
	})
}

// uuidToBusString renders a pgtype.UUID in canonical 8-4-4-4-12 form.
// Inlined to avoid a back-edge dependency on internal/api or another
// helper-bearing package.
func uuidToBusString(u pgtype.UUID) string {
	if !u.Valid {
		return ""
	}
	const hex = "0123456789abcdef"
	out := make([]byte, 36)
	pos := 0
	for i, b := range u.Bytes {
		if i == 4 || i == 6 || i == 8 || i == 10 {
			out[pos] = '-'
			pos++
		}
		out[pos] = hex[b>>4]
		out[pos+1] = hex[b&0xf]
		pos += 2
	}
	return string(out)
}
