// packages/functions/src/vercel/baseFunction.ts

/**
 * @fileoverview Base Vercel function that handles all common concerns
 * @description Rate limiting, monitoring, authentication, validation - all in one place
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */

import * as v from 'valibot';

import { handleError } from '../shared/errorHandling.js';
import { recordPaymentMetrics } from '../shared/utils/internal/monitoring.js';
import {
  checkRateLimitWithFirestore,
  DEFAULT_RATE_LIMITS,
} from '../shared/utils/internal/rateLimiter.js';
import { assertAuthenticated } from '../shared/utils.js';

import type { NextApiRequest, NextApiResponse } from 'next';

/**
 * Base Vercel function that handles all common concerns
 * Users just provide their business logic
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */
export function createVercelBaseFunction<TRequest, TResponse>(
  method: string,
  schema: v.BaseSchema<unknown, TRequest, v.BaseIssue<unknown>>,
  operation: string,
  businessLogic: (
    req: NextApiRequest,
    res: NextApiResponse,
    data: TRequest,
    context: {
      uid: string;
    }
  ) => Promise<TResponse>
) {
  return async (
    req: NextApiRequest,
    res: NextApiResponse
  ): Promise<TResponse> => {
    try {
      // Method validation
      if (req.method !== method) {
        return res
          .status(405)
          .json({ error: 'Method not allowed' }) as TResponse;
      }

      // Validate with schema - this handles ALL validation
      const validationResult = v.safeParse(schema, req.body);
      if (!validationResult.success) {
        return res.status(400).json({
          error: `Validation failed: ${validationResult.issues.map((e) => e.message).join(', ')}`,
        }) as TResponse;
      }

      const validatedData = validationResult.output;

      // Verify authentication
      const uid = assertAuthenticated(req.headers.authorization);

      // Rate limiting
      const rateLimitKey = `${operation}_${uid}`;
      const rateLimitConfig =
        (DEFAULT_RATE_LIMITS as any)[operation] || DEFAULT_RATE_LIMITS.api;
      const rateLimitResult = await checkRateLimitWithFirestore(
        rateLimitKey,
        rateLimitConfig
      );

      if (!rateLimitResult.allowed) {
        return res.status(429).json({
          error: `Rate limit exceeded. Try again in ${rateLimitResult.blockRemainingSeconds} seconds.`,
          retryAfter: rateLimitResult.blockRemainingSeconds,
        }) as TResponse;
      }

      // Call user's business logic
      const result = await businessLogic(req, res, validatedData, { uid });

      // Record metrics
      await recordPaymentMetrics({
        operation,
        userId: uid,
        status: 'success',
        timestamp: new Date().toISOString(),
        metadata: {
          requestId: (req.headers['x-request-id'] as string) || 'unknown',
        },
      });

      return result;
    } catch (error) {
      // Record error metrics
      await recordPaymentMetrics({
        operation,
        userId: req.headers.authorization ? 'authenticated' : 'anonymous',
        status: 'failed' as const,
        timestamp: new Date().toISOString(),
        metadata: {
          error: error instanceof Error ? error.message : 'Unknown error',
          requestId: (req.headers['x-request-id'] as string) || 'unknown',
        },
      });

      // Handle error and return appropriate response
      const errorResponse = handleVercelError(error);
      return res
        .status(errorResponse.status)
        .json({ error: errorResponse.message }) as TResponse;
    }
  };
}

/**
 * Handle Vercel-specific error responses
 */
function handleVercelError(error: unknown): {
  status: number;
  message: string;
} {
  if (error instanceof Error) {
    // Check for specific error types
    if (error.message.includes('Rate limit exceeded')) {
      return { status: 429, message: error.message };
    }
    if (error.message.includes('Validation failed')) {
      return { status: 400, message: error.message };
    }
    if (error.message.includes('permission-denied')) {
      return { status: 403, message: 'Permission denied' };
    }
    if (error.message.includes('not-found')) {
      return { status: 404, message: 'Resource not found' };
    }
    if (error.message.includes('already-exists')) {
      return { status: 409, message: 'Resource already exists' };
    }
    if (error.message.includes('invalid-argument')) {
      return { status: 400, message: error.message };
    }
  }

  // Default to internal server error
  return { status: 500, message: 'Internal server error' };
}
