/**
 * Edge Function Request Tracking Wrapper
 * 
 * Wraps Supabase function invocations with request tracking for debugging and monitoring.
 * Provides correlation IDs for tracing requests across the system.
 */

import { supabase } from '@/lib/supabaseClient';
import { trackRequest } from './requestTracking';
import { getErrorMessage } from './errorHandler';
import { withRetry, isRetryableError, type RetryOptions } from './retryHelpers';
import { breadcrumb } from './errorBreadcrumbs';

/**
 * Invoke a Supabase edge function with request tracking
 * 
 * @param functionName - Name of the edge function to invoke
 * @param payload - Request payload
 * @param userId - User ID for tracking (optional)
 * @param parentRequestId - Parent request ID for chaining (optional)
 * @param traceId - Trace ID for distributed tracing (optional)
 * @returns Response data with requestId
 */
export async function invokeWithTracking<T = any>(
  functionName: string,
  payload: any = {},
  userId?: string,
  parentRequestId?: string,
  traceId?: string,
  timeout: number = 30000, // Default 30s timeout
  retryOptions?: Partial<RetryOptions> // NEW: Optional retry configuration
): Promise<{ data: T | null; error: any; requestId: string; duration: number; attempts?: number }> {
  // Configure retry options with defaults
  const effectiveRetryOptions: RetryOptions = {
    maxAttempts: retryOptions?.maxAttempts ?? 3,
    baseDelay: retryOptions?.baseDelay ?? 1000,
    maxDelay: retryOptions?.maxDelay ?? 10000,
    backoffMultiplier: retryOptions?.backoffMultiplier ?? 2,
    jitter: true,
    shouldRetry: isRetryableError,
    onRetry: (attempt, error, delay) => {
      // Log retry attempt to breadcrumbs
      breadcrumb.apiCall(
        `/functions/${functionName}`,
        'POST',
        undefined // status unknown during retry
      );
      
      console.info(`Retrying ${functionName} (attempt ${attempt}) after ${delay}ms:`, 
        getErrorMessage(error)
      );
    },
  };

  let attemptCount = 0;
  
  try {
    const { result, requestId, duration } = await trackRequest(
      {
        endpoint: `/functions/${functionName}`,
        method: 'POST',
        userId,
        parentRequestId,
        traceId,
      },
      async (context) => {
        return await withRetry(
          async () => {
            attemptCount++;
            
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), timeout);
            
            try {
              const { data, error } = await supabase.functions.invoke<T>(functionName, {
                body: { ...payload, clientRequestId: context.requestId },
                signal: controller.signal,
              });
              
              clearTimeout(timeoutId);
              
              if (error) {
                // Enhance error with status for retry logic
                const enhancedError = new Error(error.message || 'Edge function error');
                (enhancedError as any).status = error.status;
                throw enhancedError;
              }
              
              return data;
            } catch (error) {
              clearTimeout(timeoutId);
              throw error;
            }
          },
          effectiveRetryOptions
        );
      }
    );

    return { data: result, error: null, requestId, duration, attempts: attemptCount };
  } catch (error: unknown) {
    // Handle AbortError specifically
    if (error instanceof Error && error.name === 'AbortError') {
      return {
        data: null,
        error: { 
          message: `Request timeout: ${functionName} took longer than ${timeout}ms to respond`,
          code: 'TIMEOUT',
        },
        requestId: 'timeout',
        duration: timeout,
        attempts: attemptCount,
      };
    }
    
    const errorMessage = getErrorMessage(error);
    return {
      data: null,
      error: { message: errorMessage },
      requestId: 'unknown',
      duration: 0,
      attempts: attemptCount,
    };
  }
}

/**
 * Invoke multiple edge functions in parallel with batch tracking
 * 
 * Uses a shared trace ID to correlate all operations.
 * 
 * @param operations - Array of function invocation configurations
 * @param userId - User ID for tracking
 * @returns Array of results with their request IDs
 */
export async function invokeBatchWithTracking<T = any>(
  operations: Array<{
    functionName: string;
    payload: any;
    retryOptions?: Partial<RetryOptions>;
  }>,
  userId?: string
): Promise<
  Array<{
    functionName: string;
    data: T | null;
    error: any;
    requestId: string;
    duration: number;
    attempts?: number;
  }>
> {
  const traceId = crypto.randomUUID();

  const results = await Promise.allSettled(
    operations.map(async (op) => {
      const result = await invokeWithTracking<T>(
        op.functionName,
        op.payload,
        userId,
        undefined,
        traceId,
        30000, // default timeout
        op.retryOptions // Pass through retry options
      );
      return { functionName: op.functionName, ...result };
    })
  );

  return results.map((result, index) => {
    if (result.status === 'fulfilled') {
      return result.value;
    } else {
      return {
        functionName: operations[index].functionName,
        data: null,
        error: { message: result.reason?.message || 'Unknown error' },
        requestId: 'unknown',
        duration: 0,
      };
    }
  });
}
