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

/**
 * @fileoverview Generic function to create an entity.
 * @description Provides a reusable implementation for creating documents in Firestore with validation.
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */

import * as v from 'valibot';

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

import {
  prepareForFirestore,
  transformFirestoreData,
} from '../../shared/index.js';
import { createMetadata } from '../../shared/index.js';
import { assertAdmin, validateDocument } from '../../shared/utils.js';
import { createBaseFunction } from '../baseFunction.js';
import { CRUD_CONFIG } from '../config/constants.js';

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

/**
 * Generic business logic for creating entities
 * Base function handles: validation, auth, rate limiting, monitoring
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */
function createEntityLogicFactory(
  collection: string,
  documentSchema: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>
) {
  return async function createEntityLogic(
    data: { payload: Record<string, any>; idempotencyKey?: string },
    context: { uid: string; request: CallableRequest<any> }
  ) {
    const { payload, idempotencyKey } = data;

    // Ensure the user is an admin
    const uid = await assertAdmin(context.uid);

    // Idempotency check if key provided
    if (idempotencyKey) {
      const db = getFirebaseAdminFirestore();
      const idempotencyRef = db
        .collection('idempotency')
        .doc(`create_${idempotencyKey}`);
      const idempotencyDoc = await idempotencyRef.get();
      if (idempotencyDoc.exists) {
        return idempotencyDoc.data()?.result;
      }
    }

    // Validate the document against the schema
    validateDocument(payload, documentSchema);

    // Prepare the document for Firestore and add metadata
    const documentData = {
      ...prepareForFirestore(payload),
      ...createMetadata(uid),
    };

    // Save the document to Firestore
    const db = getFirebaseAdminFirestore();
    const docRef = await db.collection(collection).add(documentData);

    // Retrieve the created document
    const doc = await docRef.get();

    // Transform the document data back to the application format
    const result = transformFirestoreData({
      id: doc.id,
      ...doc.data(),
    });

    // Store result for idempotency if key provided
    if (idempotencyKey) {
      const idempotencyRef = db
        .collection('idempotency')
        .doc(`create_${idempotencyKey}`);
      await idempotencyRef.set({
        result,
        processedAt: new Date().toISOString(),
        processedBy: uid,
      });
    }

    return result;
  };
}

/**
 * Generic function to create entities in 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
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */
export const createEntity = (
  collection: string,
  documentSchema: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>,
  customSchema?: v.BaseSchema<unknown, any, v.BaseIssue<unknown>>
) => {
  const requestSchema =
    customSchema ||
    v.object({
      payload: v.record(v.string(), v.any()),
      idempotencyKey: v.optional(v.string()),
    });

  return createBaseFunction(
    CRUD_CONFIG,
    requestSchema,
    'create_entity',
    createEntityLogicFactory(collection, documentSchema)
  );
};
