package main

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

	"git.d-ma.be/mathias/tapir/internal/runner"
)

// discoveryRunner runs one user's discovery pass.
type discoveryRunner func(ctx context.Context, userID string) (runner.Stats, error)

// serialize wraps run so calls never overlap: every discovery pass — scheduled
// or connect-triggered (#6) — acquires the same lock, preserving the
// one-fetcher-at-a-time invariant the scheduler relies on (ADR-018, the
// single-replica assumption). Locking is per-user, so a connect-triggered pass
// interleaves between the scheduler's users instead of waiting for a whole pass.
func serialize(mu *sync.Mutex, run discoveryRunner) discoveryRunner {
	return func(ctx context.Context, userID string) (runner.Stats, error) {
		mu.Lock()
		defer mu.Unlock()
		return run(ctx, userID)
	}
}

// discoveryTrigger fires an out-of-band discovery pass for one user without
// blocking the caller (the connect HTTP handler). The pass runs on the server's
// long-lived ctx — not the request ctx — so it survives the post-connect
// redirect. run is the serialized runner, so a trigger never overlaps the
// scheduler. Satisfies web.DiscoveryTrigger.
type discoveryTrigger struct {
	ctx context.Context
	run discoveryRunner
	// onboard, when set, runs after the discovery pass to summarize a capped number
	// of the user's newest videos (Feature 1). Optional.
	onboard func(ctx context.Context, userID string)
	log     *slog.Logger
}

func (t *discoveryTrigger) Enqueue(userID string) {
	go func() {
		if _, err := t.run(t.ctx, userID); err != nil {
			t.log.Warn("discovery: connect-triggered pass had errors", "user", userID, "err", err)
		}
		if t.onboard != nil {
			t.onboard(t.ctx, userID)
		}
	}()
}
