import { useMemo } from 'react';

/* -------------------------------------------------------------------------- */
/*  normalise / useNormalise                                                  */
/*                                                                            */
/*  The IT0 backend APIs return paginated data in multiple shapes:            */
/*    1. Plain array          — T[]                                           */
/*    2. Object with `items`  — { items: T[], total: number }                 */
/*    3. Object with `data`   — { data: T[], total: number }                  */
/*    4. Object with `rows`   — { rows: T[], total: number, ... }             */
/*                                                                            */
/*  This utility normalises any of those shapes into a single interface:      */
/*    { items: T[], total: number }                                           */
/*                                                                            */
/*  It is critical for the web admin's data-fetching patterns because it      */
/*  lets pages/components consume API responses generically without caring    */
/*  about the exact envelope the backend happened to use.                     */
/* -------------------------------------------------------------------------- */

export interface NormalisedResponse<T> {
  items: T[];
  total: number;
}

/**
 * Pure function that normalises a backend API response into `{ items, total }`.
 *
 * Handles:
 * - `T[]`
 * - `{ items: T[], total?: number }`
 * - `{ data: T[], total?: number }`
 * - `{ rows: T[], total?: number }`
 * - `{ results: T[], total?: number, count?: number }`
 *
 * Unknown shapes fall back to `{ items: [], total: 0 }`.
 */
export function normalise<T>(response: unknown): NormalisedResponse<T> {
  // 1. Array
  if (Array.isArray(response)) {
    return { items: response as T[], total: response.length };
  }

  // 2. Object envelope
  if (response && typeof response === 'object') {
    const obj = response as Record<string, unknown>;

    // Try known array-valued keys in priority order
    const arrayKey = (['items', 'data', 'rows', 'results'] as const).find(
      (k) => Array.isArray(obj[k]),
    );

    if (arrayKey) {
      const items = obj[arrayKey] as T[];
      const total = resolveTotal(obj, items.length);
      return { items, total };
    }

    // Edge case: response is an object but none of the known keys exist.
    // Some endpoints return { [id]: entity } maps — not supported here.
  }

  // Fallback
  return { items: [], total: 0 };
}

/**
 * React hook wrapper around `normalise`.
 *
 * Memoises the result so downstream consumers do not re-render unnecessarily.
 *
 * @example
 * ```tsx
 * const { data } = useQuery({ queryKey: queryKeys.servers.list(), queryFn: fetchServers });
 * const { items: servers, total } = useNormalise<Server>(data);
 * ```
 */
export function useNormalise<T>(response: unknown): NormalisedResponse<T> {
  return useMemo(() => normalise<T>(response), [response]);
}

/* -------------------------------------------------------------------------- */
/*  Helpers                                                                   */
/* -------------------------------------------------------------------------- */

function resolveTotal(obj: Record<string, unknown>, fallback: number): number {
  // `total` is the canonical key
  if (typeof obj.total === 'number') return obj.total;
  // Some endpoints use `count`
  if (typeof obj.count === 'number') return obj.count;
  // `totalCount` (pagination metadata)
  if (typeof obj.totalCount === 'number') return obj.totalCount;
  // Fallback to the length of the items array
  return fallback;
}
