import { ref, type Ref } from 'vue'
import { toast } from 'vue-sonner'

export interface AsyncOperationOptions {
  successMessage?: string
  errorMessage?: string
  showToast?: boolean
  showSuccessToast?: boolean
  showErrorToast?: boolean
}

export interface AsyncOperationState<T> {
  isLoading: Ref<boolean>
  error: Ref<string | null>
  data: Ref<T | null>
}

export interface AsyncOperationReturn<T> extends AsyncOperationState<T> {
  execute: (operation: () => Promise<T>, options?: AsyncOperationOptions) => Promise<T | null>
  reset: () => void
  clear: () => void
}

/**
 * Composable for standardized async operation handling
 * Eliminates duplicate loading/error/success patterns across modules
 * 
 * @example
 * ```typescript
 * const { isLoading, error, data, execute } = useAsyncOperation<OrderData>()
 * 
 * const handleOrder = async () => {
 *   await execute(async () => {
 *     return await createOrder(orderData)
 *   }, {
 *     successMessage: 'Order created successfully!',
 *     errorMessage: 'Failed to create order'
 *   })
 * }
 * ```
 */
export function useAsyncOperation<T = any>(): AsyncOperationReturn<T> {
  const isLoading = ref(false)
  const error = ref<string | null>(null)
  const data = ref(null) as Ref<T | null>

  /**
   * Execute an async operation with standardized error handling and loading states
   */
  const execute = async (
    operation: () => Promise<T>, 
    options: AsyncOperationOptions = {}
  ): Promise<T | null> => {
    const {
      successMessage,
      errorMessage = 'Operation failed',
      showToast = true,
      showSuccessToast = showToast,
      showErrorToast = showToast
    } = options

    try {
      isLoading.value = true
      error.value = null

      const result = await operation()
      data.value = result

      // Show success toast if configured
      if (showSuccessToast && successMessage) {
        toast.success(successMessage)
      }

      return result
    } catch (err) {
      const errorMsg = err instanceof Error ? err.message : String(err)
      error.value = errorMsg

      // Show error toast if configured
      if (showErrorToast) {
        toast.error(errorMessage, {
          description: errorMsg !== errorMessage ? errorMsg : undefined
        })
      }

      // Re-throw to allow caller to handle if needed
      throw err
    } finally {
      isLoading.value = false
    }
  }

  /**
   * Reset the operation state (clear error, keep data)
   */
  const reset = (): void => {
    isLoading.value = false
    error.value = null
  }

  /**
   * Clear all state (error, data, loading)
   */
  const clear = (): void => {
    isLoading.value = false
    error.value = null
    data.value = null
  }

  return {
    // State
    isLoading,
    error,
    data,
    
    // Methods
    execute,
    reset,
    clear
  }
}

/**
 * Specialized version for operations that don't return data
 */
export function useAsyncAction(): Omit<AsyncOperationReturn<void>, 'data'> {
  const { data, ...rest } = useAsyncOperation<void>()
  return rest
}

/**
 * Multiple async operations manager
 * Useful when you need to track multiple independent operations
 */
export function useMultiAsyncOperation<T extends Record<string, any>>() {
  const operations = ref<Record<keyof T, AsyncOperationReturn<any>>>({} as any)

  const createOperation = <K extends keyof T>(key: K): AsyncOperationReturn<T[K]> => {
    if (!operations.value[key]) {
      operations.value[key] = useAsyncOperation<T[K]>()
    }
    return operations.value[key] as AsyncOperationReturn<T[K]>
  }

  const isAnyLoading = (): boolean => {
    return Object.values(operations.value).some((op: any) => op.isLoading.value)
  }

  const hasAnyError = (): boolean => {
    return Object.values(operations.value).some((op: any) => op.error.value !== null)
  }

  const clearAll = (): void => {
    Object.values(operations.value).forEach((op: any) => op.clear())
  }

  return {
    operations,
    createOperation,
    isAnyLoading,
    hasAnyError,
    clearAll
  }
}