package auth

import (
	"context"
	"errors"
	"log/slog"
	"sync/atomic"
	"time"
)

var ErrVerifierUnavailable = errors.New("verifier unavailable")

// Holder keeps an OIDC verifier available, retrying in the background when Authentik
// is not ready at ultid startup (common in local Docker compose).
type Holder struct {
	v atomic.Pointer[Verifier]

	issuerURL     string
	clientID      string
	discoveryHost string
}

func NewHolder(initial *Verifier) *Holder {
	h := &Holder{}
	if initial != nil {
		h.v.Store(initial)
	}
	return h
}

func NewHolderPending(issuerURL, clientID, discoveryHost string) *Holder {
	return &Holder{
		issuerURL:     issuerURL,
		clientID:      clientID,
		discoveryHost: discoveryHost,
	}
}

func (h *Holder) Ready() bool {
	return h != nil && h.v.Load() != nil
}

func (h *Holder) Get() *Verifier {
	if h == nil {
		return nil
	}
	return h.v.Load()
}

func (h *Holder) Verify(ctx context.Context, rawToken string) (*Claims, error) {
	v := h.Get()
	if v == nil {
		return nil, ErrVerifierUnavailable
	}
	return v.Verify(ctx, rawToken)
}

// StartBackgroundRetry keeps trying until the verifier is ready or ctx is cancelled.
func (h *Holder) StartBackgroundRetry(ctx context.Context, interval time.Duration) {
	if h == nil || h.Ready() || h.issuerURL == "" || h.clientID == "" {
		return
	}
	if interval <= 0 {
		interval = 5 * time.Second
	}
	go func() {
		ticker := time.NewTicker(interval)
		defer ticker.Stop()
		for {
			v, err := NewVerifier(ctx, h.issuerURL, h.clientID, h.discoveryHost)
			if err == nil {
				h.v.Store(v)
				slog.Info("OIDC verifier ready (background retry)")
				return
			}
			slog.Warn("OIDC verifier background retry", "error", err)
			select {
			case <-ctx.Done():
				return
			case <-ticker.C:
			}
		}
	}()
}
