package usecase import ( "context" "strings" "apps/backend/internal/module/ai" memberDomain "apps/backend/internal/module/member/domain" "apps/backend/internal/module/usage/domain" ) // SettingsResolver uses member settings + platform config. type SettingsResolver struct { Members memberDomain.Repository PlatformAI string // legacy / xAI fallback PlatformXAI string PlatformOpenCode string PlatformExa string } func (r *SettingsResolver) platformAIKey(provider string) string { switch ai.NormalizeProvider(provider) { case ai.ProviderOpenCodeGo: if k := strings.TrimSpace(r.PlatformOpenCode); k != "" { return k } default: if k := strings.TrimSpace(r.PlatformXAI); k != "" { return k } if k := strings.TrimSpace(r.PlatformAI); k != "" { return k } } return "" } func (r *SettingsResolver) Resolve(ctx context.Context, uid int64, meter string) (string, bool, error) { st, err := r.Members.GetSettings(ctx, uid) if err != nil { return "", false, err } if st == nil { st = memberDomain.DefaultSettings(uid) } provider := ai.NormalizeProvider(st.Provider) switch meter { case domain.MeterAICopy, domain.MeterAIResearch, domain.MeterAIImage: if st.KeyForProvider(provider) != "" { return domain.KeyModeByok, true, nil } if r.platformAIKey(provider) != "" { return domain.KeyModePlatform, true, nil } return "", false, nil case domain.MeterWebSearch: if st.ExaAPIKeyConfigured && st.ExaAPIKey != "" { return domain.KeyModeByok, true, nil } if r.PlatformExa != "" { return domain.KeyModePlatform, true, nil } return "", false, nil default: return "", false, nil } } // ResolveKey returns actual key material for call (never log). func (r *SettingsResolver) ResolveKey(ctx context.Context, uid int64, meter string) (keyMode, apiKey string, err error) { mode, has, err := r.Resolve(ctx, uid, meter) if err != nil { return "", "", err } if !has { return "", "", domain.ErrNoKey } st, err := r.Members.GetSettings(ctx, uid) if err != nil { return "", "", err } if st == nil { st = memberDomain.DefaultSettings(uid) } provider := ai.NormalizeProvider(st.Provider) switch mode { case domain.KeyModeByok: if meter == domain.MeterWebSearch { return mode, st.ExaAPIKey, nil } return mode, st.KeyForProvider(provider), nil default: if meter == domain.MeterWebSearch { return mode, r.PlatformExa, nil } return mode, r.platformAIKey(provider), nil } } var _ KeyResolver = (*SettingsResolver)(nil)