/**
 * 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';
import { breadcrumbManager } from './errorBreadcrumbs';
import { captureEnvironmentContext } from './environmentContext';
import { handleNonCriticalError } from './errorHandler';
import { logger } from './logger';

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 => {
      handleNonCriticalError(err, {
        action: 'Log request metadata (success)',
        userId: options.userId,
        metadata: { 
          endpoint: options.endpoint,
          method: options.method,
          statusCode: 200,
          requestId: context.requestId
        }
      });
    });
    
    // 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,
          stack: error.stack ? error.stack.slice(0, 5000) : undefined // Limit to 5000 chars
        }
      : { type: 'UnknownError', message: String(error), stack: undefined };
    
    // Capture environment context and breadcrumbs
    const envContext = captureEnvironmentContext();
    const breadcrumbs = breadcrumbManager.getAll();
    
    // 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,
      errorStack: errorInfo.stack,
      breadcrumbs,
      userAgent: context.userAgent,
      clientVersion: context.clientVersion,
      parentRequestId: options.parentRequestId,
      traceId: context.traceId,
      timezone: envContext.timezone,
      referrer: typeof document !== 'undefined' ? document.referrer : undefined,
    }).catch(err => {
      handleNonCriticalError(err, {
        action: 'Log request metadata (error)',
        userId: options.userId,
        metadata: { 
          endpoint: options.endpoint,
          method: options.method,
          statusCode: 500,
          requestId: context.requestId,
          errorType: errorInfo.type
        }
      });
    });
    
    // 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;
  errorStack?: string;
  breadcrumbs?: any[];
  userAgent?: string;
  clientVersion?: string;
  parentRequestId?: string;
  traceId?: string;
  timezone?: string;
  referrer?: 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_error_stack: metadata.errorStack ?? undefined,
    p_breadcrumbs: metadata.breadcrumbs ? JSON.stringify(metadata.breadcrumbs) : '[]',
    p_environment_context: '{}', // Legacy parameter - no longer used
    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,
    p_timezone: metadata.timezone ?? undefined,
    p_referrer: metadata.referrer ?? undefined,
  });
  
  if (error) {
    // Already logged by handleNonCriticalError in requestTracking
  }
}

/**
 * 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();
}
