package codex

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"time"
)

// AuthSnapshot describes one configured Codex auth backend.
type AuthSnapshot struct {
	Source          string   `json:"source"`
	Name            string   `json:"name,omitempty"`
	ID              int      `json:"id,omitempty"`
	BaseURL         string   `json:"base_url"`
	Mode            string   `json:"mode"`
	Active          bool     `json:"active"`
	Weight          float64  `json:"weight"`
	AllowedAPIKeys  []string `json:"allowed_api_keys,omitempty"`
	HasAccessToken  bool     `json:"has_access_token"`
	HasRefreshToken bool     `json:"has_refresh_token"`
	AccessToken     string   `json:"access_token,omitempty"`
	APIKey          string   `json:"api_key,omitempty"`
	AccountID       string   `json:"account_id,omitempty"`
	LastRefreshAt   string   `json:"last_refresh_at,omitempty"`
	LastStatus      int      `json:"last_status,omitempty"`
	LastError       string   `json:"last_error,omitempty"`
}

// AuthRefreshResult is returned after a manual refresh request.
type AuthRefreshResult struct {
	Source    string `json:"source"`
	Refreshed bool   `json:"refreshed"`
	Error     string `json:"error,omitempty"`
}

// AuthSnapshots returns the primary auth backend, same-base auth backends, and fallbacks.
func (c *Codex) AuthSnapshots() []AuthSnapshot {
	var out []AuthSnapshot
	c.collectAuthSnapshots("primary", &out)
	return out
}

func (c *Codex) collectAuthSnapshots(prefix string, out *[]AuthSnapshot) {
	if c == nil {
		return
	}
	*out = append(*out, c.authSnapshot(prefix))
	for i, backend := range c.authBackends {
		backend.collectAuthSnapshots(fmt.Sprintf("%s.auth_files.%d", prefix, i), out)
	}
	for i, fallback := range c.fallbacks {
		fallback.collectAuthSnapshots(fmt.Sprintf("%s.fallbacks.%d", prefix, i), out)
	}
}

func (c *Codex) authSnapshot(source string) AuthSnapshot {
	snap := AuthSnapshot{
		Source:         source,
		Name:           c.backendName,
		ID:             c.backendID,
		BaseURL:        c.baseURL,
		Mode:           c.mode,
		Active:         c.isEligibleWithoutKey(),
		Weight:         c.weight,
		AllowedAPIKeys: c.allowedAPIKeyList(),
	}
	if status := c.lastStatus.Load(); status > 0 {
		snap.LastStatus = int(status)
	}
	if v := c.lastError.Load(); v != nil {
		if errText, ok := v.(string); ok {
			snap.LastError = errText
		}
	}
	if c.apiKey != "" {
		snap.APIKey = maskSecret(c.apiKey)
	}
	if c.tokenManager != nil {
		accessToken, refreshToken, accountID, lastRefresh, lastError := c.tokenManager.tokenSnapshot()
		snap.HasAccessToken = accessToken != ""
		snap.HasRefreshToken = refreshToken != ""
		snap.AccessToken = maskSecret(accessToken)
		snap.AccountID = accountID
		if !lastRefresh.IsZero() {
			snap.LastRefreshAt = lastRefresh.Format(time.RFC3339)
		}
		if lastError != "" {
			snap.LastError = lastError
		}
	}
	return snap
}

func (c *Codex) isEligibleWithoutKey() bool {
	return !c.activeSet || c.active
}

func (c *Codex) allowedAPIKeyList() []string {
	if len(c.allowedAPIKeys) == 0 {
		return nil
	}
	keys := make([]string, 0, len(c.allowedAPIKeys))
	for key := range c.allowedAPIKeys {
		keys = append(keys, maskSecret(key))
	}
	sort.Strings(keys)
	return keys
}

// ForceRefreshAuth refreshes a specific auth source. Empty source refreshes all
// refreshable ChatGPT auth backends.
func (c *Codex) ForceRefreshAuth(ctx context.Context, source string) []AuthRefreshResult {
	source = strings.TrimSpace(source)
	var results []AuthRefreshResult
	c.forceRefreshAuth(ctx, "primary", source, &results)
	return results
}

func (c *Codex) forceRefreshAuth(ctx context.Context, current, requested string, results *[]AuthRefreshResult) {
	if c == nil {
		return
	}
	matches := requested == "" ||
		requested == current ||
		requested == c.backendName ||
		(c.backendID != 0 && requested == fmt.Sprintf("%d", c.backendID))

	if matches && c.tokenManager != nil && c.tokenManager.hasRefreshToken() {
		result := AuthRefreshResult{Source: current}
		if err := c.tokenManager.ForceRefresh(ctx); err != nil {
			result.Error = err.Error()
		} else {
			result.Refreshed = true
		}
		*results = append(*results, result)
	}

	for i, backend := range c.authBackends {
		backend.forceRefreshAuth(ctx, fmt.Sprintf("%s.auth_files.%d", current, i), requested, results)
	}
	for i, fallback := range c.fallbacks {
		fallback.forceRefreshAuth(ctx, fmt.Sprintf("%s.fallbacks.%d", current, i), requested, results)
	}
}

func maskSecret(value string) string {
	if value == "" {
		return ""
	}
	if len(value) <= 10 {
		return "****"
	}
	return value[:8] + "****" + value[len(value)-4:]
}
