package provision

import (
	"context"
	"time"
)

// Clock abstracts time for testability (Replace drains on a timer).
type Clock interface {
	Now() time.Time
	// Sleep blocks for d or until ctx is done, returning ctx.Err() on cancel.
	Sleep(ctx context.Context, d time.Duration) error
}

// realClock is the production Clock.
type realClock struct{}

func (realClock) Now() time.Time { return time.Now().UTC() }

func (realClock) Sleep(ctx context.Context, d time.Duration) error {
	t := time.NewTimer(d)
	defer t.Stop()
	select {
	case <-ctx.Done():
		return ctx.Err()
	case <-t.C:
		return nil
	}
}

// Prober verifies a node is reachable before it joins the directory.
//
// The full version (three-way probe cross-check, doc/04 §4.2) depends on the
// probe fleet (task #15). Until then a simplified Prober is injected:
// "heartbeat present + an overseas reachability check". Replace calls WaitReady
// after a node is created; on success the node is promoted to up.
type Prober interface {
	// WaitReady blocks until the node passes simplified probing, or returns an
	// error on timeout / ctx cancellation / probe failure.
	WaitReady(ctx context.Context, node *Node) error
}

// AlertHook receives operational alerts (doc/04 §5.3 — TG bot in production).
// Replace / CreateNode call Fire on boot failure, probe timeout, and capacity
// protection breaches.
type AlertHook interface {
	Fire(ctx context.Context, alert Alert)
}

// AlertKind classifies an alert.
type AlertKind string

const (
	AlertBootFailed   AlertKind = "boot_failed"
	AlertProbeTimeout AlertKind = "probe_timeout"
	AlertReplaceFail  AlertKind = "replace_failed"
	AlertCapacity     AlertKind = "capacity_low"
)

// Alert is a single operational alert payload.
type Alert struct {
	Kind     AlertKind
	NodeUUID string
	Pool     Pool
	Message  string
	// FailCount is the running failure counter for capacity-protection alerts.
	FailCount int
}

// noopAlert discards alerts; used when no hook is configured.
type noopAlert struct{}

func (noopAlert) Fire(context.Context, Alert) {}

// BootstrapIssuer issues one-time enrollment tokens (task #5).
// *mtls.BootstrapTokenManager satisfies this interface.
type BootstrapIssuer interface {
	IssueToken(ctx context.Context, nodeUUID string) (string, error)
}

// CloudInitRenderer renders the node cloud-init document from a template,
// injecting the node UUID and bootstrap token (template lives at
// infra/cloud-init/node.yaml.tmpl, task #6).
type CloudInitRenderer interface {
	Render(data CloudInitData) (string, error)
}

// CloudInitData is the template context for the node cloud-init document.
type CloudInitData struct {
	NodeUUID       string
	BootstrapToken string
	Region         string
	Role           Role
	Tier           Tier
	// ControlPlaneURL is the mTLS enrollment endpoint the agent registers to.
	ControlPlaneURL string
}
