package auth

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

type Pool struct {
	creds  []*Credential
	cursor int
	mu     sync.Mutex
}

func NewPool(creds []*Credential) *Pool {
	return &Pool{creds: creds}
}

func (p *Pool) Pick() (*Credential, error) {
	p.mu.Lock()
	defer p.mu.Unlock()

	n := len(p.creds)
	if n == 0 {
		return nil, fmt.Errorf("no credentials available")
	}

	for i := 0; i < n; i++ {
		idx := (p.cursor + i) % n
		cred := p.creds[idx]
		if !cred.IsOnCooldown() {
			p.cursor = (idx + 1) % n
			return cred, nil
		}
	}

	return nil, fmt.Errorf("all %d credentials are on cooldown", n)
}

func (p *Pool) MarkFailure(cred *Credential, statusCode int) {
	switch {
	case statusCode == 429:
		cred.SetCooldown(30 * time.Second)
	case statusCode >= 500:
		cred.SetCooldown(5 * time.Second)
	}
}

func (p *Pool) MarkSuccess(cred *Credential) {
	cred.ClearCooldown()
}

func (p *Pool) RefreshExpiring(ctx context.Context) {
	refreshExpiring(p)
}

func (p *Pool) RefreshAll(ctx context.Context) []map[string]string {
	p.mu.Lock()
	creds := make([]*Credential, len(p.creds))
	copy(creds, p.creds)
	p.mu.Unlock()

	var results []map[string]string
	for _, cred := range creds {
		cred.mu.Lock()
		id := cred.ID
		email := cred.Email
		oldExpiry := cred.ExpiresAt
		hasRefresh := cred.RefreshToken != ""
		cred.mu.Unlock()

		r := map[string]string{
			"id":         id,
			"email":      email,
			"old_expiry": oldExpiry.Format(time.RFC3339),
		}

		if !hasRefresh {
			r["status"] = "skipped"
			r["reason"] = "no refresh token"
			results = append(results, r)
			continue
		}

		err := RefreshToken(ctx, cred)
		if err != nil {
			r["status"] = "error"
			r["error"] = err.Error()
		} else {
			cred.mu.Lock()
			r["status"] = "ok"
			r["new_expiry"] = cred.ExpiresAt.Format(time.RFC3339)
			r["new_token_prefix"] = cred.AccessToken[:20] + "..."
			cred.mu.Unlock()
		}
		results = append(results, r)
	}
	return results
}
