package infra

import (
	"context"
	"fmt"
	"time"
)

// JobWorker runs a poll loop in the calling goroutine, dequeuing and processing
// jobs until ctx is cancelled. handler is called for each job; its return value
// drives complete vs fail logic. The worker sleeps pollInterval between empty
// dequeue attempts.
//
// Options: WithPollInterval (default 1s), WithJobTypes (default all types).
//
// This function blocks until ctx is done. Run it in a goroutine:
//
//	go JobWorker(ctx, q, handler, WithPollInterval(2*time.Second))
func JobWorker(ctx context.Context, q *JobQueue, handler JobHandler, opts ...WorkerOption) error {
	if q == nil {
		return fmt.Errorf("job_worker: queue must not be nil")
	}
	if handler == nil {
		return fmt.Errorf("job_worker: handler must not be nil")
	}

	cfg := &workerConfig{
		pollInterval: time.Second,
	}
	for _, o := range opts {
		o(cfg)
	}

	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}

		job, err := JobDequeue(q, cfg.jobTypes)
		if err != nil {
			// Transient DB error — wait and retry.
			select {
			case <-ctx.Done():
				return ctx.Err()
			case <-time.After(cfg.pollInterval):
			}
			continue
		}

		if job == nil {
			// Queue empty — wait before polling again.
			select {
			case <-ctx.Done():
				return ctx.Err()
			case <-time.After(cfg.pollInterval):
			}
			continue
		}

		// Process the job.
		handlerErr := handler(*job)
		if handlerErr != nil {
			_ = JobFail(q, job.ID, handlerErr.Error())
		} else {
			_ = JobComplete(q, job.ID, "")
		}
	}
}
