/**
 * Get documents with cursor-based pagination (for infinite scroll)
 *
 * Uses early termination to avoid reading all documents,
 * preventing the "Too many bytes read" error regardless of data volume.
 */

import type { Doc, Id } from '../_generated/dataModel';
import type { QueryCtx } from '../_generated/server';
import { getMetadataString } from '../lib/metadata/get_metadata_string';
import { paginateWithFilter, DEFAULT_PAGE_SIZE } from '../lib/pagination';
import { hasTeamAccess } from '../lib/team_access';
import { isActiveDocument } from './_helpers';
import { transformDocumentsBatch } from './transform_to_document_item';
import type { DocumentItemResponse } from './types';

export interface GetDocumentsCursorArgs {
  organizationId: string;
  numItems?: number;
  cursor: string | null;
  query?: string;
  folderId?: Id<'folders'>;
  userTeamIds?: string[];
  filterTeamId?: string;
}

export interface CursorPaginatedDocumentsResult {
  page: DocumentItemResponse[];
  isDone: boolean;
  continueCursor: string;
}

export async function getDocumentsCursor(
  ctx: QueryCtx,
  args: GetDocumentsCursorArgs,
): Promise<CursorPaginatedDocumentsResult> {
  const numItems = args.numItems ?? DEFAULT_PAGE_SIZE;
  const searchQuery = args.query?.trim().toLowerCase() ?? '';

  const baseQuery = args.folderId
    ? ctx.db
        .query('documents')
        .withIndex('by_organizationId_and_folderId', (q) =>
          q
            .eq('organizationId', args.organizationId)
            .eq('folderId', args.folderId),
        )
        .order('desc')
    : ctx.db
        .query('documents')
        .withIndex('by_organizationId', (q) =>
          q.eq('organizationId', args.organizationId),
        )
        .order('desc');

  const filter = (doc: Doc<'documents'>): boolean => {
    // Skip soft-deleted (trashed) documents
    if (!isActiveDocument(doc)) {
      return false;
    }

    if (args.userTeamIds !== undefined) {
      if (!hasTeamAccess(doc, args.userTeamIds)) {
        return false;
      }
    }

    // Team filter: show org-wide docs + docs belonging to the selected team
    if (args.filterTeamId) {
      if (doc.teamId && doc.teamId !== args.filterTeamId) {
        return false;
      }
    }

    // Apply search filter (case-insensitive contains)
    if (searchQuery) {
      const titleMatch = doc.title?.toLowerCase().includes(searchQuery);
      const nameMatch = getMetadataString(doc.metadata, 'name')
        ?.toLowerCase()
        .includes(searchQuery);

      if (!titleMatch && !nameMatch) {
        return false;
      }
    }

    return true;
  };

  // Use paginateWithFilter for early termination
  const result = await paginateWithFilter(baseQuery, {
    numItems,
    cursor: args.cursor,
    filter,
  });

  // Transform documents to DocumentItemResponse format using batch processing
  // This efficiently fetches user names and storage URLs in parallel
  const items = await transformDocumentsBatch(ctx, result.page);

  return {
    page: items,
    isDone: result.isDone,
    continueCursor: result.continueCursor,
  };
}
