// packages/functions/src/firebase/crud/list.ts

/**
 * @fileoverview Generic function to list entities with pagination, filtering, and sorting.
 * @description Provides a reusable implementation for listing documents from Firestore with advanced query capabilities.
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */

import { Query } from '@donotdev/firebase/server';

import type { CallableRequest } from 'firebase-functions/v2/https';

import * as v from 'valibot';

interface ListEntityRequest {
  where?: Array<[string, any, any]>;
  orderBy?: Array<[string, 'asc' | 'desc']>;
  limit?: number;
  startAfterId?: string;
  search?: {
    field: string;
    query: string;
  };
}
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';

import { transformFirestoreData } from '../../shared/index.js';
import { filterVisibleFields } from '../../shared/index.js';
import { DoNotDevError } from '../../shared/utils.js';
import { createBaseFunction } from '../baseFunction.js';
import { CRUD_CONFIG } from '../config/constants.js';

/**
 * Generic business logic for listing entities
 * Base function handles: validation, auth, rate limiting, monitoring
 */
function listEntitiesLogicFactory(
  collection: string,
  documentSchema: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>
) {
  return async function listEntitiesLogic(
    data: ListEntityRequest,
    context: { uid: string; request: CallableRequest<ListEntityRequest> }
  ) {
    const { where = [], orderBy = [], limit = 50, startAfterId, search } = data;

    // Start with a Query (not a CollectionReference)
    const db = getFirebaseAdminFirestore();
    let query: Query = db.collection(collection);

    // Apply search if provided
    if (search) {
      const { field, query: searchQuery } = search;
      query = query
        .where(field, '>=', searchQuery)
        .where(field, '<=', searchQuery + '\uf8ff');
    }

    // Apply where clauses for filtering
    for (const [field, operator, value] of where) {
      query = query.where(field, operator, value);
    }

    // Apply ordering
    for (const [field, direction] of orderBy) {
      query = query.orderBy(field, direction);
    }

    // Apply pagination with startAfterId
    if (startAfterId) {
      const startAfterDoc = await db
        .collection(collection)
        .doc(startAfterId)
        .get();

      if (!startAfterDoc.exists) {
        throw new DoNotDevError('Start after document not found', 'not-found');
      }

      query = query.startAfter(startAfterDoc);
    }

    // Apply limit for page size
    query = query.limit(limit);

    // Execute the query
    const snapshot = await query.get();

    // Check if the user is an admin
    const user = context.request.auth;
    const isAdmin = user?.token?.isAdmin === true;

    // Filter document fields based on visibility and user role
    const docs = snapshot.docs.map((doc: any) => ({
      id: doc.id,
      ...filterVisibleFields(doc.data() || {}, documentSchema, isAdmin),
    }));

    // Return the paginated result with metadata
    return {
      items: transformFirestoreData(docs),
      lastVisible: snapshot.docs[snapshot.docs.length - 1]?.id || null,
      count: snapshot.docs.length,
      hasMore: snapshot.docs.length === limit,
    };
  };
}

/**
 * Generic function to list entities from any Firestore collection
 * @param collection - The Firestore collection name
 * @param documentSchema - The Valibot schema for document validation
 * @param customSchema - Optional custom request schema
 * @returns Firebase callable function
 */
export const listEntities = (
  collection: string,
  documentSchema: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>,
  customSchema?: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>
) => {
  const requestSchema =
    customSchema ||
    v.object({
      where: v.optional(v.array(v.tuple([v.string(), v.any(), v.any()]))),
      orderBy: v.optional(
        v.array(v.tuple([v.string(), v.picklist(['asc', 'desc'])]))
      ),
      limit: v.optional(v.pipe(v.number(), v.minValue(1), v.maxValue(100))),
      startAfterId: v.optional(v.string()),
      search: v.optional(
        v.object({
          field: v.string(),
          query: v.string(),
        })
      ),
    });

  return createBaseFunction(
    CRUD_CONFIG,
    requestSchema,
    'list_entities',
    listEntitiesLogicFactory(collection, documentSchema)
  );
};
