import type { TableRequestOptions, TableResponse } 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 {
  ModelComponentProps,
  ModelHookResponse,
  ModelProps,
  TableWidgetProps,
} from './types'

const PAGE_SIZE = 1000 // ponytail: matches DEFAULT_PAGE_SIZE in execute-model.ts

// ponytail: unbounded Promise.all; executeSQL single-shot path for >10k is a follow-up.
async function fetchAllTablePages(
  getTableMethod: (opts: TableRequestOptions) => Promise<TableResponse>,
  params: TableRequestOptions,
): Promise<TableResponse> {
  // Caller offset ignored (consistent with executeModel); all other params forwarded.
  const first = await getTableMethod({ ...params, limit: PAGE_SIZE, offset: 0 })
  const total = first.totalCount
  if (total <= first.rows.length) return first

  const numPages = Math.ceil((total - first.rows.length) / PAGE_SIZE)
  const pages = await Promise.all(
    Array.from({ length: numPages }, (_, i) =>
      getTableMethod({
        ...params,
        limit: PAGE_SIZE,
        offset: PAGE_SIZE * (i + 1),
      }),
    ),
  )
  const rows = [...first.rows, ...pages.flatMap((p) => p.rows)]
  // Spread `first` so any extra response fields survive, matching the
  // single-page path above which returns the response untouched.
  return { ...first, rows, totalCount: first.totalCount }
}

export const getTable: ModelQueryFactory<TableWidgetProps, TableResponse> = (
  args,
) =>
  getModelQueryOptions<TableWidgetProps, TableResponse>({
    queryKeyPrefix: ['table'],
    getWidgetMethod: (widgetModel) => (params) => {
      const opts = params as TableRequestOptions
      // ponytail: temporary — until the models are executed with executeModel
      // locally (which already paginates limit === 0), page here in the factory.
      return opts.limit === 0
        ? fetchAllTablePages((o) => widgetModel.getTable(o), opts)
        : widgetModel.getTable(opts)
    },
    ...args,
  })

const { useModelHook: useTableQuery } = createModel<
  TableWidgetProps,
  TableResponse
>({ buildQuery: getTable })

/**
 * `useTable` is a hook that retrieves tabular data with pagination support for displaying detailed records.
 * `TableModel` is the render-props component equivalent.
 *
 * @example
 * ```tsx
 * <TableModel
 *   accessToken={token}
 *   sourceId="my_source"
 *   widgetProps={{ columns: ['name', 'revenue', 'city'], sortBy: 'revenue', sortDirection: 'desc' }}
 * >
 *   {({ data }) => <DataGrid rows={data?.rows} />}
 * </TableModel>
 * ```
 */
function useTable<P extends ModelProps<TableWidgetProps, TableResponse>>(
  props: P,
): ModelHookResponse<P, TableResponse> {
  const { sourceId, sourcesId } = props
  const slices = useSourceSlices(sourceId ? [sourceId] : (sourcesId ?? []))
  return useTableQuery(props, slices)
}

function TableModel<P extends ModelProps<TableWidgetProps, TableResponse>>(
  props: ModelComponentProps<TableWidgetProps, TableResponse, P>,
): React.ReactElement {
  return props.children(useTable(props)) as React.ReactElement
}

export { useTable, TableModel }
