import type { Ref } from 'vue'

interface UseAsyncActionOptions {
  isLoading: Ref<boolean>
  error: Ref<string | null>
}

interface RunOptions {
  /** Fallback message shown when the error has no server-provided detail */
  errorMessage: string
  /** Whether to re-throw after recording the error (default true) */
  rethrow?: boolean
}

/**
 * Wraps the isLoading/error/try-catch-finally shape shared by most store actions:
 * set loading, clear error, run fn, extract a message from an axios-shaped or
 * plain Error on failure, log it, optionally re-throw, and always clear loading.
 */
export function useAsyncAction({ isLoading, error }: UseAsyncActionOptions) {
  // Overloads so callers that don't pass `rethrow: false` keep a non-optional return type
  function run<T>(fn: () => Promise<T>, options: RunOptions & { rethrow?: true }): Promise<T>
  function run<T>(fn: () => Promise<T>, options: RunOptions & { rethrow: false }): Promise<T | undefined>
  async function run<T>(fn: () => Promise<T>, options: RunOptions): Promise<T | undefined> {
    const { errorMessage, rethrow = true } = options
    try {
      isLoading.value = true
      error.value = null
      return await fn()
    } catch (err: any) {
      error.value = err?.response?.data?.detail || (err instanceof Error ? err.message : errorMessage)
      console.error(errorMessage, err)
      if (rethrow) throw err
      return undefined
    } finally {
      isLoading.value = false
    }
  }

  return { run }
}
