import {
  type QueryClient,
  type QueryOptions,
  skipToken
} from "@tanstack/query-core"
import type { InferData } from "../../lib/auth-client"
import { createAuthQueryFetchOptions } from "../../lib/auth-query-retry"
import type { OrganizationAuthClient } from "./organization-auth-client"
import { organizationQueryKeys } from "./organization-query-keys"

export type ListOrganizationsData<
  TAuthClient extends OrganizationAuthClient = OrganizationAuthClient
> = InferData<TAuthClient["organization"]["list"]>

export type ListOrganization<
  TAuthClient extends OrganizationAuthClient = OrganizationAuthClient
> = NonNullable<ListOrganizationsData<TAuthClient>>[number]

export type ListOrganizationsParams<
  TAuthClient extends OrganizationAuthClient = OrganizationAuthClient
> = Parameters<TAuthClient["organization"]["list"]>[0]

export type ListOrganizationsOptions<
  TAuthClient extends OrganizationAuthClient = OrganizationAuthClient
> = Omit<QueryOptions<ListOrganizationsData<TAuthClient>>, "queryKey"> &
  ListOrganizationsParams<TAuthClient>

/**
 * Query options factory for organizations available to the current user.
 *
 * @param authClient - The Better Auth organization client.
 * @param userId - The current signed-in user's ID. Used for cache partitioning.
 * @param params - Parameters forwarded to `authClient.organization.list`.
 */
export function listOrganizationsOptions<
  TAuthClient extends OrganizationAuthClient
>(
  authClient: TAuthClient,
  userId?: string,
  params?: ListOrganizationsParams<TAuthClient>
) {
  type TData = ListOrganizationsData<TAuthClient>
  const queryKey = organizationQueryKeys.list(userId, params?.query)

  return {
    queryKey,
    queryFn: userId
      ? ({ signal }) =>
          authClient.organization.list({
            ...params,
            fetchOptions: createAuthQueryFetchOptions(
              params?.fetchOptions,
              signal
            )
          }) as Promise<TData>
      : skipToken
  } satisfies QueryOptions
}

/**
 * Get organizations from the cache, fetching if needed.
 */
export const ensureListOrganizations = <
  TAuthClient extends OrganizationAuthClient
>(
  queryClient: QueryClient,
  authClient: TAuthClient,
  userId: string,
  options?: ListOrganizationsOptions<TAuthClient>
) => {
  const { fetchOptions, query, ...queryOptions } = options ?? {}

  return queryClient.ensureQueryData({
    ...listOrganizationsOptions(authClient, userId, { query, fetchOptions }),
    ...queryOptions
  })
}

/**
 * Prefetch organizations into the query cache.
 */
export const prefetchListOrganizations = <
  TAuthClient extends OrganizationAuthClient
>(
  queryClient: QueryClient,
  authClient: TAuthClient,
  userId: string,
  options?: ListOrganizationsOptions<TAuthClient>
) => {
  const { fetchOptions, query, ...queryOptions } = options ?? {}

  return queryClient.prefetchQuery({
    ...listOrganizationsOptions(authClient, userId, { query, fetchOptions }),
    ...queryOptions
  })
}

/**
 * Fetch and cache organizations, resolving with data or throwing on error.
 */
export const fetchListOrganizations = <
  TAuthClient extends OrganizationAuthClient
>(
  queryClient: QueryClient,
  authClient: TAuthClient,
  userId: string,
  options?: ListOrganizationsOptions<TAuthClient>
) => {
  const { fetchOptions, query, ...queryOptions } = options ?? {}

  return queryClient.fetchQuery({
    ...listOrganizationsOptions(authClient, userId, { query, fetchOptions }),
    ...queryOptions
  })
}
/**
 * Read organizations synchronously from the query cache without fetching.
 */
export const getListOrganizations = <
  TAuthClient extends OrganizationAuthClient = OrganizationAuthClient
>(
  queryClient: QueryClient,
  _authClient?: TAuthClient,
  userId?: string,
  params?: ListOrganizationsParams<TAuthClient>
) => {
  const queryKey = organizationQueryKeys.list(userId, params?.query)
  return queryClient.getQueryData<ListOrganizationsData<TAuthClient>>(queryKey)
}
