package wgtunnel

import (
	"context"
	"log/slog"
	"sync"
	"time"

	"gitea.dooplex.hu/admin/felhom-agent/internal/hub"
)

// Loop drives the Manager on its own cadence (the lanresolver shape) AND consumes fetched
// desired-state via the desired.Syncer raw-consumer seam. It distinguishes "no desired data
// seen yet" (fetched=false — never a teardown signal) from "desired-state present without the
// wireguard block" (revocation).
type Loop struct {
	mgr      *Manager
	interval time.Duration
	logger   *slog.Logger

	mu      sync.Mutex
	fetched bool
	block   *hub.WireWireguard

	nudge chan struct{}
}

// NewLoop builds the loop. interval defaults to 60s.
func NewLoop(mgr *Manager, interval time.Duration, logger *slog.Logger) *Loop {
	if interval <= 0 {
		interval = 60 * time.Second
	}
	if logger == nil {
		logger = slog.Default()
	}
	return &Loop{mgr: mgr, interval: interval, logger: logger, nudge: make(chan struct{}, 1)}
}

// OnDesiredState implements desired.RawConsumer: store the latest wireguard block (or its
// absence) and nudge the loop. Non-blocking and panic-free by construction.
func (l *Loop) OnDesiredState(_ context.Context, resp *hub.DesiredStateResponse) {
	if resp == nil {
		return
	}
	l.mu.Lock()
	l.fetched = true
	l.block = resp.DesiredState.Wireguard
	l.mu.Unlock()
	select {
	case l.nudge <- struct{}{}:
	default:
	}
}

func (l *Loop) snapshot() (bool, *hub.WireWireguard) {
	l.mu.Lock()
	defer l.mu.Unlock()
	return l.fetched, l.block
}

// Run applies immediately, then on every tick or desired-state nudge, until ctx is cancelled.
func (l *Loop) Run(ctx context.Context) error {
	fetched, block := l.snapshot()
	l.mgr.Apply(ctx, fetched, block)
	t := time.NewTicker(l.interval)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-t.C:
		case <-l.nudge:
		}
		fetched, block = l.snapshot()
		l.mgr.Apply(ctx, fetched, block)
		l.mgr.Watchdog(ctx, fetched, block) // re-resolve + recover on endpoint re-IP (doc 06 §4.2)
	}
}

// WireguardStatus implements the hub collector's WireguardReporter seam.
func (l *Loop) WireguardStatus(ctx context.Context) *hub.WireguardStatus {
	return l.mgr.Status(ctx)
}
