import type {
  CategoryRequestOptions,
  CategoryResponse,
} from '@carto/api-client'
import { createModel, type ModelQueryFactory } from './utils/create-model'
import { getModelQueryOptions } from './utils/get-model-query-options'
import { useSourceSlices } from './utils/use-source-slices'
import type {
  CategoryWidgetProps,
  ModelComponentProps,
  ModelHookResponse,
  ModelProps,
} from './types'

/**
 * Imperative query options factory for the Categories widget. Returns a
 * React Query options object usable with `queryClient.prefetchQuery`,
 * `fetchQuery`, `ensureQueryData`, etc. Also powers `useCategories` /
 * `CategoriesModel` so the imperative path and the hook share one source of
 * truth for query-key + query-fn construction.
 */
export const getCategories: ModelQueryFactory<
  CategoryWidgetProps,
  CategoryResponse
> = (args) =>
  getModelQueryOptions<CategoryWidgetProps, CategoryResponse>({
    queryKeyPrefix: ['categories'],
    getWidgetMethod: (widgetModel) => (params) =>
      widgetModel.getCategories(params as CategoryRequestOptions),
    ...args,
  })

const { useModelHook: useCategoriesQuery } = createModel<
  CategoryWidgetProps,
  CategoryResponse
>({ buildQuery: getCategories })

/**
 * `useCategories` is a hook that groups records by a categorical column and optionally aggregates each group.
 * `CategoriesModel` is the render-props component equivalent.
 *
 * @example
 * ```tsx
 * <CategoriesModel
 *   accessToken={token}
 *   sourceId="my_source"
 *   widgetProps={{ column: 'category', operation: 'count' }}
 * >
 *   {({ data }) => <CategoryChart data={data} />}
 * </CategoriesModel>
 * ```
 */
function useCategories<
  P extends ModelProps<CategoryWidgetProps, CategoryResponse>,
>(props: P): ModelHookResponse<P, CategoryResponse> {
  const { sourceId, sourcesId } = props
  const slices = useSourceSlices(sourceId ? [sourceId] : (sourcesId ?? []))
  return useCategoriesQuery(props, slices)
}

function CategoriesModel<
  P extends ModelProps<CategoryWidgetProps, CategoryResponse>,
>(
  props: ModelComponentProps<CategoryWidgetProps, CategoryResponse, P>,
): React.ReactElement {
  return props.children(useCategories(props)) as React.ReactElement
}

export { useCategories, CategoriesModel }
