package planner

import (
	"fmt"
	"math"
	"time"
)

const tsoLogicalBits = 18

type Clock interface {
	Now() time.Time
}

// EnsureSnapshotTSO computes T only when it has not been persisted. This makes
// retry behavior explicit and prevents a second snapshot boundary allocation.
func EnsureSnapshotTSO(current uint64, clock Clock) (uint64, error) {
	if current != 0 {
		return current, nil
	}
	if clock == nil {
		return 0, fmt.Errorf("%w: clock is nil", ErrInvalidSnapshotTime)
	}

	snapshotTime := clock.Now().UTC().Truncate(time.Millisecond)
	unixMillis := snapshotTime.UnixMilli()
	if unixMillis < 0 || uint64(unixMillis) > math.MaxUint64>>tsoLogicalBits {
		return 0, fmt.Errorf("%w: %s", ErrInvalidSnapshotTime, snapshotTime.Format(time.RFC3339Nano))
	}
	return uint64(unixMillis) << tsoLogicalBits, nil
}
