package agentd

import (
	"context"
	"time"

	agentv1 "github.com/wangjia/pangolin/server/internal/pb/agentv1"
)

// LoadSource provides the runtime metrics reported in each heartbeat. The
// production implementation can read sing-box stats + host CPU; the default only
// reports the online peer count (number of provisioned credentials), which never
// reveals identities.
type LoadSource interface {
	Load() (onlinePeers int32, upBps, downBps int64, cpuPercent float64)
}

// defaultLoadSource reports peer count from the SingBox table and zeros for the
// rest. Bandwidth/CPU collection is a documented extension point.
type defaultLoadSource struct{ sb *SingBox }

func (d defaultLoadSource) Load() (int32, int64, int64, float64) {
	return int32(d.sb.OnlinePeers()), 0, 0, 0
}

// runHeartbeat sends a heartbeat every cfg.HeartbeatInterval until ctx is
// cancelled or an RPC error occurs. A need_full_resync response returns
// errResync so the session re-Registers and overwrites local state.
func (a *Agent) runHeartbeat(ctx context.Context, client agentv1.AgentServiceClient) error {
	ticker := time.NewTicker(a.cfg.HeartbeatInterval)
	defer ticker.Stop()
	for {
		if err := a.sendHeartbeat(ctx, client); err != nil {
			return err
		}
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-ticker.C:
		}
	}
}

func (a *Agent) sendHeartbeat(ctx context.Context, client agentv1.AgentServiceClient) error {
	peers, up, down, cpu := a.load.Load()
	resp, err := client.Heartbeat(ctx, &agentv1.HeartbeatRequest{
		NodeUUID:         a.NodeUUID(),
		ConfigVersion:    a.sb.ConfigVersion(),
		OnlinePeers:      peers,
		BandwidthUpBps:   up,
		BandwidthDownBps: down,
		CPUPercent:       cpu,
		TimestampUnix:    a.clock().Unix(),
	})
	if err != nil {
		return err
	}
	if resp.NeedFullResync {
		logf("control plane requested full resync")
		return errResync
	}
	return nil
}
