import { PaginatedResponse, PaginationMeta } from '@edr/types';
import { ObjectLiteral, SelectQueryBuilder } from 'typeorm';

/** Raw page/pageSize as they arrive from a query DTO (both optional). */
export interface PageRequest {
  page?: number;
  pageSize?: number;
}

export interface PaginationOptions {
  defaultPageSize?: number;
  maxPageSize?: number;
}

export interface NormalizedPage {
  page: number;
  pageSize: number;
  skip: number;
  take: number;
}

const DEFAULT_PAGE_SIZE = 20;
const MAX_PAGE_SIZE = 100;

/** Clamp raw query values into a safe page window (page ≥ 1, pageSize capped). */
export function normalizePagination(
  request: PageRequest,
  options: PaginationOptions = {},
): NormalizedPage {
  const defaultPageSize = options.defaultPageSize ?? DEFAULT_PAGE_SIZE;
  const maxPageSize = options.maxPageSize ?? MAX_PAGE_SIZE;

  const page = Math.max(1, Math.floor(request.page ?? 1) || 1);
  const requested = Math.floor(request.pageSize ?? defaultPageSize) || defaultPageSize;
  const pageSize = Math.min(Math.max(1, requested), maxPageSize);

  return { page, pageSize, skip: (page - 1) * pageSize, take: pageSize };
}

export function buildPaginationMeta(
  total: number,
  page: number,
  pageSize: number,
): PaginationMeta {
  const totalPages = pageSize > 0 ? Math.ceil(total / pageSize) : 0;
  return {
    page,
    pageSize,
    total,
    totalPages,
    hasNextPage: page < totalPages,
    hasPreviousPage: page > 1,
  };
}

/**
 * Apply skip/take to a query builder, run it, and wrap the result in the
 * shared `PaginatedResponse` envelope. Ordering and filtering must already be
 * applied by the caller.
 */
export async function paginateQuery<T extends ObjectLiteral>(
  qb: SelectQueryBuilder<T>,
  request: PageRequest,
  options?: PaginationOptions,
): Promise<PaginatedResponse<T>> {
  const { page, pageSize, skip, take } = normalizePagination(request, options);
  const [items, total] = await qb.skip(skip).take(take).getManyAndCount();
  return { items, meta: buildPaginationMeta(total, page, pageSize) };
}

/**
 * Paginate an already-materialized array. Prefer `paginateQuery` (DB-level
 * LIMIT/OFFSET); use this only for lists that are inherently in-memory.
 */
export function paginateArray<T>(
  rows: readonly T[],
  request: PageRequest,
  options?: PaginationOptions,
): PaginatedResponse<T> {
  const { page, pageSize, skip } = normalizePagination(request, options);
  return {
    items: rows.slice(skip, skip + pageSize),
    meta: buildPaginationMeta(rows.length, page, pageSize),
  };
}
