/**
 * Shared loader for `integration-connector` compositions. Wraps a
 * Promise-returning `onLoad` callback (and an optional per-row
 * `onConnect`) with the in-flight token / error / loading state every
 * connector composition needs.
 *
 *   const {
 *     connectors, isLoading, error, refresh,
 *     isConnecting, connect,
 *   } = useConnectorLoader(() => fetch("/connectors").then((r) => r.json()))
 *
 * `refresh()` re-invokes `onLoad` and replaces the cached list. `connect`
 * forwards to a caller-supplied `onConnect`; when that resolves the list
 * is refreshed automatically so the new status surfaces.
 */
import { onBeforeUnmount, onMounted, ref, shallowRef } from "vue"
import type { Ref } from "vue"
import type { Connector } from "@/definitions/shared/connector"

export interface UseConnectorLoaderReturn {
  connectors: Ref<Connector[]>
  isLoading: Ref<boolean>
  error: Ref<string | null>
  /** Re-invokes the loader. Resolves once the new list is committed. */
  refresh: () => Promise<void>
  /** True while a per-id `connect()` is pending. */
  isConnecting: Ref<Record<string, boolean>>
  /** Calls the caller-supplied `onConnect` then refreshes the list. */
  connect: (
    id: string,
    onConnect?: (id: string) => Promise<void>,
  ) => Promise<void>
}

export function useConnectorLoader(
  onLoad: () => Promise<Connector[]>,
): UseConnectorLoaderReturn {
  const connectors = shallowRef<Connector[]>([])
  const isLoading = ref<boolean>(false)
  const error = ref<string | null>(null)
  const isConnecting = ref<Record<string, boolean>>({})

  let token = 0
  let disposed = false

  async function refresh(): Promise<void> {
    const myToken = ++token
    isLoading.value = true
    error.value = null
    try {
      const next = await onLoad()
      if (disposed || myToken !== token) {
        return
      }
      connectors.value = Array.isArray(next) ? next : []
    } catch (err) {
      if (disposed || myToken !== token) {
        return
      }
      connectors.value = []
      error.value =
        err instanceof Error && err.message ? err.message : "Load failed"
    } finally {
      if (!disposed && myToken === token) {
        isLoading.value = false
      }
    }
  }

  async function connect(
    id: string,
    onConnect?: (id: string) => Promise<void>,
  ): Promise<void> {
    if (!onConnect) {
      return
    }
    isConnecting.value = { ...isConnecting.value, [id]: true }
    try {
      await onConnect(id)
      await refresh()
    } finally {
      const next = { ...isConnecting.value }
      delete next[id]
      isConnecting.value = next
    }
  }

  onMounted(() => {
    void refresh()
  })

  onBeforeUnmount(() => {
    disposed = true
    token++ // invalidate any in-flight promise
  })

  return { connectors, isLoading, error, refresh, isConnecting, connect }
}
