import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { credentialsApi, CreateCredentialRequest, UpdateCredentialRequest, TestConnectionResult, RegisterModelData, } from '@/lib/api/credentials' import { useToast } from '@/lib/hooks/use-toast' import { useTranslation } from '@/lib/hooks/use-translation' import { getApiErrorKey } from '@/lib/utils/error-handler' import { MODEL_QUERY_KEYS } from '@/lib/hooks/use-models' export const CREDENTIAL_QUERY_KEYS = { all: ['credentials'] as const, status: ['credentials', 'status'] as const, envStatus: ['credentials', 'env-status'] as const, byProvider: (provider: string) => ['credentials', 'provider', provider] as const, detail: (id: string) => ['credentials', id] as const, } /** * Hook to get the configuration status of all providers */ export function useCredentialStatus() { return useQuery({ queryKey: CREDENTIAL_QUERY_KEYS.status, queryFn: () => credentialsApi.getStatus(), }) } /** * Hook to get the environment variable status */ export function useEnvStatus() { return useQuery({ queryKey: CREDENTIAL_QUERY_KEYS.envStatus, queryFn: () => credentialsApi.getEnvStatus(), }) } /** * Hook to list all credentials */ export function useCredentials(provider?: string) { return useQuery({ queryKey: provider ? CREDENTIAL_QUERY_KEYS.byProvider(provider) : CREDENTIAL_QUERY_KEYS.all, queryFn: () => credentialsApi.list(provider), }) } /** * Hook to list credentials for a specific provider. * Uses the same list endpoint with provider filter for cache consistency. */ export function useCredentialsByProvider(provider: string) { return useQuery({ queryKey: CREDENTIAL_QUERY_KEYS.byProvider(provider), queryFn: () => credentialsApi.list(provider), enabled: !!provider, }) } /** * Hook to get a specific credential */ export function useCredential(credentialId: string) { return useQuery({ queryKey: CREDENTIAL_QUERY_KEYS.detail(credentialId), queryFn: () => credentialsApi.get(credentialId), enabled: !!credentialId, }) } /** * Hook to create a new credential */ export function useCreateCredential() { const queryClient = useQueryClient() const { toast } = useToast() const { t } = useTranslation() return useMutation({ mutationFn: (data: CreateCredentialRequest) => credentialsApi.create(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: CREDENTIAL_QUERY_KEYS.all }) queryClient.invalidateQueries({ queryKey: MODEL_QUERY_KEYS.providers }) toast({ title: t('common.success'), description: t('apiKeys.configSaveSuccess'), }) }, onError: (error: unknown) => { toast({ title: t('common.error'), description: getApiErrorKey(error, t('common.error')), variant: 'destructive', }) }, }) } /** * Hook to update a credential */ export function useUpdateCredential() { const queryClient = useQueryClient() const { toast } = useToast() const { t } = useTranslation() return useMutation({ mutationFn: ({ credentialId, data, }: { credentialId: string data: UpdateCredentialRequest }) => credentialsApi.update(credentialId, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: CREDENTIAL_QUERY_KEYS.all }) queryClient.invalidateQueries({ queryKey: MODEL_QUERY_KEYS.providers }) toast({ title: t('common.success'), description: t('apiKeys.configUpdateSuccess'), }) }, onError: (error: unknown) => { toast({ title: t('common.error'), description: getApiErrorKey(error, t('common.error')), variant: 'destructive', }) }, }) } /** * Hook to delete a credential */ export function useDeleteCredential() { const queryClient = useQueryClient() const { toast } = useToast() const { t } = useTranslation() return useMutation({ mutationFn: ({ credentialId, options, }: { credentialId: string options?: { delete_models?: boolean; migrate_to?: string } }) => credentialsApi.delete(credentialId, options), onSuccess: () => { queryClient.invalidateQueries({ queryKey: CREDENTIAL_QUERY_KEYS.all }) queryClient.invalidateQueries({ queryKey: MODEL_QUERY_KEYS.models }) queryClient.invalidateQueries({ queryKey: MODEL_QUERY_KEYS.providers }) toast({ title: t('common.success'), description: t('apiKeys.configDeleteSuccess'), }) }, onError: (error: unknown) => { toast({ title: t('common.error'), description: getApiErrorKey(error, t('common.error')), variant: 'destructive', }) }, }) } /** * Hook to test a credential's connection */ export function useTestCredential() { const { toast } = useToast() const { t } = useTranslation() const [testResults, setTestResults] = useState>({}) const mutation = useMutation({ mutationFn: (credentialId: string) => credentialsApi.test(credentialId), onSuccess: (result, credentialId) => { setTestResults(prev => ({ ...prev, [credentialId]: result })) if (result.success) { toast({ title: t('common.success'), description: t('apiKeys.testSuccess'), }) } else { toast({ title: t('common.error'), description: result.message || t('apiKeys.testFailed'), variant: 'destructive', }) } }, onError: (error: unknown) => { toast({ title: t('common.error'), description: getApiErrorKey(error, t('apiKeys.testFailed')), variant: 'destructive', }) }, }) return { testCredential: mutation.mutate, testCredentialAsync: mutation.mutateAsync, isPending: mutation.isPending, testResults, clearResult: (credentialId: string) => { setTestResults(prev => { const { [credentialId]: _removed, ...rest } = prev return rest }) }, } } /** * Hook to discover models for a credential */ export function useDiscoverModels() { const { toast } = useToast() const { t } = useTranslation() return useMutation({ mutationFn: (credentialId: string) => credentialsApi.discover(credentialId), onError: (error: unknown) => { toast({ title: t('common.error'), description: getApiErrorKey(error, t('apiKeys.syncFailed')), variant: 'destructive', }) }, }) } /** * Hook to register discovered models */ export function useRegisterModels() { const queryClient = useQueryClient() const { toast } = useToast() const { t } = useTranslation() return useMutation({ mutationFn: ({ credentialId, models, }: { credentialId: string models: RegisterModelData[] }) => credentialsApi.registerModels(credentialId, { models }), onSuccess: (result) => { queryClient.invalidateQueries({ queryKey: MODEL_QUERY_KEYS.models }) queryClient.invalidateQueries({ queryKey: CREDENTIAL_QUERY_KEYS.all }) if (result.created > 0) { toast({ title: t('common.success'), description: t('apiKeys.syncSuccess') .replace('{discovered}', (result.created + result.existing).toString()) .replace('{new}', result.created.toString()), }) } else { toast({ title: t('common.success'), description: t('apiKeys.syncNoNew').replace('{count}', result.existing.toString()), }) } }, onError: (error: unknown) => { toast({ title: t('common.error'), description: getApiErrorKey(error, t('apiKeys.syncFailed')), variant: 'destructive', }) }, }) } /** * Hook to migrate from environment variables */ export function useMigrateFromEnv() { const queryClient = useQueryClient() const { toast } = useToast() const { t } = useTranslation() return useMutation({ mutationFn: () => credentialsApi.migrateFromEnv(), onSuccess: (result) => { queryClient.invalidateQueries({ queryKey: CREDENTIAL_QUERY_KEYS.all }) queryClient.invalidateQueries({ queryKey: CREDENTIAL_QUERY_KEYS.status }) queryClient.invalidateQueries({ queryKey: CREDENTIAL_QUERY_KEYS.envStatus }) queryClient.invalidateQueries({ queryKey: MODEL_QUERY_KEYS.models }) queryClient.invalidateQueries({ queryKey: MODEL_QUERY_KEYS.providers }) const migratedCount = result.migrated.length const errorCount = result.errors?.length ?? 0 if (errorCount > 0 && migratedCount === 0) { toast({ title: t('common.error'), description: t('apiKeys.migrationErrors').replace('{count}', errorCount.toString()), variant: 'destructive', }) } else if (migratedCount > 0 && errorCount > 0) { toast({ title: t('common.success'), description: `${t('apiKeys.migrationSuccess').replace('{count}', migratedCount.toString())}. ${t('apiKeys.migrationErrors').replace('{count}', errorCount.toString())}`, }) } else if (migratedCount > 0) { toast({ title: t('common.success'), description: t('apiKeys.migrationSuccess').replace('{count}', migratedCount.toString()), }) } else { toast({ title: t('common.success'), description: t('apiKeys.migrationNothingToMigrate'), }) } }, onError: (error: unknown) => { toast({ title: t('common.error'), description: getApiErrorKey(error, t('common.error')), variant: 'destructive', }) }, }) } /** * Hook to migrate from ProviderConfig */ export function useMigrateFromProviderConfig() { const queryClient = useQueryClient() const { toast } = useToast() const { t } = useTranslation() return useMutation({ mutationFn: () => credentialsApi.migrateFromProviderConfig(), onSuccess: (result) => { queryClient.invalidateQueries({ queryKey: CREDENTIAL_QUERY_KEYS.all }) queryClient.invalidateQueries({ queryKey: CREDENTIAL_QUERY_KEYS.status }) queryClient.invalidateQueries({ queryKey: CREDENTIAL_QUERY_KEYS.envStatus }) queryClient.invalidateQueries({ queryKey: MODEL_QUERY_KEYS.models }) queryClient.invalidateQueries({ queryKey: MODEL_QUERY_KEYS.providers }) const migratedCount = result.migrated.length const errorCount = result.errors?.length ?? 0 if (errorCount > 0 && migratedCount === 0) { toast({ title: t('common.error'), description: t('apiKeys.migrationErrors').replace('{count}', errorCount.toString()), variant: 'destructive', }) } else if (migratedCount > 0 && errorCount > 0) { toast({ title: t('common.success'), description: `${t('apiKeys.migrationSuccess').replace('{count}', migratedCount.toString())}. ${t('apiKeys.migrationErrors').replace('{count}', errorCount.toString())}`, }) } else if (migratedCount > 0) { toast({ title: t('common.success'), description: t('apiKeys.migrationSuccess').replace('{count}', migratedCount.toString()), }) } else { toast({ title: t('common.success'), description: t('apiKeys.migrationNothingToMigrate'), }) } }, onError: (error: unknown) => { toast({ title: t('common.error'), description: getApiErrorKey(error, t('common.error')), variant: 'destructive', }) }, }) }