/**
 * Request Tracking Service
 * Tracks API requests with correlation IDs and stores metadata for monitoring
 */

import { supabase } from '@/integrations/supabase/client';
import { requestContext, type RequestContext } from './requestContext';

export interface RequestTrackingOptions {
  endpoint: string;
  method: string;
  userId?: string;
  parentRequestId?: string;
  traceId?: string;
}

export interface RequestResult {
  requestId: string;
  statusCode: number;
  duration: number;
  error?: {
    type: string;
    message: string;
  };
}

/**
 * Track a request and store metadata
 * Returns requestId for correlation and support
 */
export async function trackRequest<T>(
  options: RequestTrackingOptions,
  fn: (context: RequestContext) => Promise<T>
): Promise<{ result: T; requestId: string; duration: number }> {
  const context = requestContext.create(options.userId, options.traceId);
  const start = Date.now();
  
  try {
    const result = await fn(context);
    const duration = Date.now() - start;
    
    // Log to database (fire and forget - don't block response)
    logRequestMetadata({
      requestId: context.requestId,
      userId: options.userId,
      endpoint: options.endpoint,
      method: options.method,
      statusCode: 200,
      duration,
      userAgent: context.userAgent,
      clientVersion: context.clientVersion,
      parentRequestId: options.parentRequestId,
      traceId: context.traceId,
    }).catch(err => {
      console.error('[RequestTracking] Failed to log metadata:', err);
    });
    
    // Cleanup context
    requestContext.cleanup(context.requestId);
    
    return { result, requestId: context.requestId, duration };
    
  } catch (error: unknown) {
    const duration = Date.now() - start;
    const errorInfo = error instanceof Error 
      ? { type: error.name, message: error.message }
      : { type: 'UnknownError', message: String(error) };
    
    // Log error to database (fire and forget)
    logRequestMetadata({
      requestId: context.requestId,
      userId: options.userId,
      endpoint: options.endpoint,
      method: options.method,
      statusCode: 500,
      duration,
      errorType: errorInfo.type,
      errorMessage: errorInfo.message,
      userAgent: context.userAgent,
      clientVersion: context.clientVersion,
      parentRequestId: options.parentRequestId,
      traceId: context.traceId,
    }).catch(err => {
      console.error('[RequestTracking] Failed to log error metadata:', err);
    });
    
    // Cleanup context
    requestContext.cleanup(context.requestId);
    
    throw error;
  }
}

interface RequestMetadata {
  requestId: string;
  userId?: string;
  endpoint: string;
  method: string;
  statusCode: number;
  duration: number;
  errorType?: string;
  errorMessage?: string;
  userAgent?: string;
  clientVersion?: string;
  parentRequestId?: string;
  traceId?: string;
}

async function logRequestMetadata(metadata: RequestMetadata): Promise<void> {
  // Safe cast - RPC function exists in database
  const { error } = await supabase.rpc('log_request_metadata' as 'log_request_metadata', {
    p_request_id: metadata.requestId,
    p_user_id: metadata.userId ?? undefined,
    p_endpoint: metadata.endpoint,
    p_method: metadata.method,
    p_status_code: metadata.statusCode,
    p_duration_ms: metadata.duration,
    p_error_type: metadata.errorType ?? undefined,
    p_error_message: metadata.errorMessage ?? undefined,
    p_user_agent: metadata.userAgent ?? undefined,
    p_client_version: metadata.clientVersion ?? undefined,
    p_parent_request_id: metadata.parentRequestId ?? undefined,
    p_trace_id: metadata.traceId ?? undefined,
  });
  
  if (error) {
    console.error('[RequestTracking] Failed to log metadata:', error);
  }
}

/**
 * Simple wrapper for tracking without async operations
 */
export function createRequestContext(
  userId?: string,
  traceId?: string
): RequestContext {
  return requestContext.create(userId, traceId);
}

/**
 * Get request context for current operation
 */
export function getCurrentRequestContext(): RequestContext | undefined {
  return requestContext.getCurrentContext();
}
