import { supabase } from '@/lib/supabaseClient';
import type { Json } from '@/integrations/supabase/types';
import { ImageAssignments } from '@/components/upload/EntityMultiImageUploader';
import { uploadPendingImages } from './imageUploadHelper';
import type { ProcessedImage } from './supabaseHelpers';
import { extractChangedFields } from './submissionChangeDetection';
import type { CompanyDatabaseRecord, TimelineEventDatabaseRecord } from '@/types/company-data';
import { logger } from './logger';
import { handleError } from './errorHandler';
import type { TimelineEventFormData, EntityType } from '@/types/timeline';
import { breadcrumb } from './errorBreadcrumbs';
import { isRetryableError, isRateLimitError, extractRetryAfter } from './retryHelpers';
import { 
  validateParkCreateFields, 
  validateRideCreateFields, 
  validateCompanyCreateFields, 
  validateRideModelCreateFields,
  assertValid 
} from './submissionValidation';
import { checkSubmissionRateLimit, recordSubmissionAttempt } from './submissionRateLimiter';
import { sanitizeErrorMessage } from './errorSanitizer';
import { reportRateLimitViolation, reportBanEvasionAttempt } from './pipelineAlerts';

// ============================================
// COMPOSITE SUBMISSION TYPES
// ============================================

interface CompositeSubmissionDependency {
  type: 'park' | 'ride' | 'company' | 'ride_model';
  data: any;
  tempId: string;
  companyType?: 'manufacturer' | 'designer' | 'operator' | 'property_owner';
  parentTempId?: string; // For linking ride_model to manufacturer
}

interface CompositeSubmissionData {
  primaryEntity: {
    type: 'park' | 'ride';
    data: any;
  };
  dependencies: CompositeSubmissionDependency[];
}

/**
 * ═══════════════════════════════════════════════════════════════════
 * SUBMISSION PATTERN STANDARD - CRITICAL PROJECT RULE
 * ═══════════════════════════════════════════════════════════════════
 * 
 * ⚠️ NEVER STORE JSON IN SQL COLUMNS ⚠️
 * 
 * content_submissions.content should ONLY contain:
 * ✅ action: 'create' | 'edit' | 'delete'
 * ✅ Minimal reference IDs (entity_id, parent_id, etc.) - MAX 3 fields
 * ❌ NO actual form data
 * ❌ NO submission content
 * ❌ NO large objects
 * 
 * ALL actual data MUST go in:
 * ✅ submission_items.item_data (new data)
 * ✅ submission_items.original_data (for edits)
 * ✅ Specialized relational tables:
 *    - photo_submissions + photo_submission_items
 *    - park_submissions
 *    - ride_submissions
 *    - company_submissions
 *    - ride_model_submissions
 * 
 * If your data is relational, model it relationally.
 * JSON blobs destroy:
 * - Queryability (can't filter/join)
 * - Performance (slower, larger)
 * - Data integrity (no constraints)
 * - Maintainability (impossible to refactor)
 * 
 * EXAMPLES:
 * 
 * ✅ CORRECT:
 * content: { action: 'create' }
 * content: { action: 'edit', park_id: uuid }
 * content: { action: 'delete', photo_id: uuid }
 * 
 * ❌ WRONG:
 * content: { name: '...', description: '...', ...formData }
 * content: { photos: [...], metadata: {...} }
 * content: data  // entire object dump
 * 
 * ═══════════════════════════════════════════════════════════════════
 */

export interface ParkFormData {
  name: string;
  slug: string;
  description?: string;
  park_type: string;
  status: string;
  opening_date?: string;
  opening_date_precision?: string;
  closing_date?: string;
  closing_date_precision?: string;
  website_url?: string;
  phone?: string;
  email?: string;
  operator_id?: string;
  property_owner_id?: string;
  
  // Location can be stored as object for new submissions or ID for editing
  location?: {
    name: string;
    street_address?: string;
    city?: string;
    state_province?: string;
    country: string;
    postal_code?: string;
    latitude: number;
    longitude: number;
    timezone?: string;
    display_name: string;
  };
  location_id?: string;
  
  images?: ImageAssignments;
  banner_image_url?: string;
  banner_image_id?: string;
  card_image_url?: string;
  card_image_id?: string;
}

export interface RideFormData {
  name: string;
  slug: string;
  description?: string;
  category: string;
  status: string;
  park_id: string;
  manufacturer_id?: string;
  designer_id?: string;
  ride_model_id?: string;
  opening_date?: string;
  opening_date_precision?: string;
  closing_date?: string;
  closing_date_precision?: string;
  max_speed_kmh?: number;
  max_height_meters?: number;
  length_meters?: number;
  duration_seconds?: number;
  capacity_per_hour?: number;
  height_requirement?: number;
  age_requirement?: number;
  inversions?: number;
  drop_height_meters?: number;
  max_g_force?: number;
  intensity_level?: string;
  coaster_type?: string;
  seating_type?: string;
  ride_sub_type?: string;
  images?: ImageAssignments;
  banner_image_url?: string;
  banner_image_id?: string;
  card_image_url?: string;
  card_image_id?: string;
}

export interface CompanyFormData {
  name: string;
  slug: string;
  description?: string;
  person_type: 'company' | 'individual' | 'firm' | 'organization';
  founded_year?: number;
  founded_date?: string;
  founded_date_precision?: string;
  headquarters_location?: string;
  website_url?: string;
  images?: ImageAssignments;
  banner_image_url?: string;
  banner_image_id?: string;
  card_image_url?: string;
  card_image_id?: string;
}

interface TechnicalSpecification {
  spec_name: string;
  spec_value: string;
  spec_type?: 'string' | 'number' | 'boolean' | 'date';
  category?: string;
  unit?: string;
  display_order?: number;
}

export interface RideModelFormData {
  name: string;
  slug: string;
  manufacturer_id: string;
  category: string;
  ride_type?: string;
  description?: string;
  images?: ImageAssignments;
  banner_image_url?: string;
  banner_image_id?: string;
  card_image_url?: string;
  card_image_id?: string;
  _technical_specifications?: TechnicalSpecification[];
}

/**
 * ═══════════════════════════════════════════════════════════════════
 * RATE LIMITING HELPER
 * ═══════════════════════════════════════════════════════════════════
 * 
 * Checks rate limits before allowing submission creation
 * Part of Sacred Pipeline Phase 3: Enhanced Error Handling
 */
function checkRateLimitOrThrow(userId: string, action: string): void {
  const rateLimit = checkSubmissionRateLimit(userId);
  
  if (!rateLimit.allowed) {
    const sanitizedMessage = sanitizeErrorMessage(rateLimit.reason || 'Rate limit exceeded');
    
    logger.warn('[RateLimit] Submission blocked', {
      userId,
      action,
      reason: rateLimit.reason,
      retryAfter: rateLimit.retryAfter,
    });
    
    // Report to system alerts for admin visibility
    reportRateLimitViolation(userId, action, rateLimit.retryAfter || 60).catch(() => {
      // Non-blocking - don't fail submission if alert fails
    });
    
    throw new Error(sanitizedMessage);
  }
  
  logger.info('[RateLimit] Submission allowed', {
    userId,
    action,
    remaining: rateLimit.remaining,
  });
}

/**
 * ═══════════════════════════════════════════════════════════════════
 * COMPOSITE SUBMISSION HANDLER
 * ═══════════════════════════════════════════════════════════════════
 * 
 * Creates a single submission containing multiple related entities
 * (e.g., Park + Operator, Ride + Manufacturer + Model)
 * 
 * All entities are submitted atomically through the moderation queue,
 * with dependency tracking to ensure correct approval order.
 * 
 * @param primaryEntity - The main entity being created (park or ride)
 * @param dependencies - Array of dependent entities (companies, models)
 * @param userId - The ID of the user submitting
 * @returns Object containing submitted boolean and submissionId
 */
async function submitCompositeCreation(
  primaryEntity: { type: 'park' | 'ride'; data: any },
  dependencies: CompositeSubmissionDependency[],
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  const { withRetry } = await import('./retryHelpers');
  
  try {
    // Phase 3: Rate limiting check
    checkRateLimitOrThrow(userId, 'composite_creation');
    recordSubmissionAttempt(userId);
    
    breadcrumb.userAction('Start composite submission', 'submitCompositeCreation', {
      primaryType: primaryEntity.type,
      dependencyCount: dependencies.length,
      userId
    });

    // Check if user is banned with retry logic
    breadcrumb.apiCall('profiles', 'SELECT');
    const profile = await withRetry(
      async () => {
        const { data, error } = await supabase
          .from('profiles')
          .select('banned')
          .eq('user_id', userId)
          .single();
        if (error) throw error;
        return data;
      },
      { maxAttempts: 2 }
    );

    if (profile?.banned) {
      // Report ban evasion attempt
      reportBanEvasionAttempt(userId, 'composite_creation').catch(() => {
        // Non-blocking - don't fail if alert fails
      });
      throw new Error('Account suspended. Contact support for assistance.');
    }

    // Upload all pending images for all entities
    breadcrumb.userAction('Upload images', 'submitCompositeCreation', {
      totalImages: dependencies.reduce((sum, dep) => sum + (dep.data.images?.uploaded?.length || 0), 0) + 
                   (primaryEntity.data.images?.uploaded?.length || 0)
    });

    const uploadedEntities = await Promise.all([
      ...dependencies.map(async (dep, index) => {
        try {
          if (dep.data.images?.uploaded && dep.data.images.uploaded.length > 0) {
            const uploadedImages = await uploadPendingImages(dep.data.images.uploaded);
            return {
              ...dep,
              data: {
                ...dep.data,
                images: { ...dep.data.images, uploaded: uploadedImages }
              }
            };
          }
          return dep;
        } catch (error) {
          const errorMsg = error instanceof Error ? error.message : String(error);
          throw new Error(
            `Failed to upload images for ${dep.type} "${dep.data.name || 'unnamed'}": ${errorMsg}`
          );
        }
      }),
      (async () => {
        try {
          if (primaryEntity.data.images?.uploaded && primaryEntity.data.images.uploaded.length > 0) {
            const uploadedImages = await uploadPendingImages(primaryEntity.data.images.uploaded);
            return {
              ...primaryEntity,
              data: {
                ...primaryEntity.data,
                images: { ...primaryEntity.data.images, uploaded: uploadedImages }
              }
            };
          }
          return primaryEntity;
        } catch (error) {
          const errorMsg = error instanceof Error ? error.message : String(error);
          throw new Error(
            `Failed to upload images for ${primaryEntity.type} "${primaryEntity.data.name || 'unnamed'}": ${errorMsg}`
          );
        }
      })()
    ]);

    const uploadedDependencies = uploadedEntities.slice(0, -1) as CompositeSubmissionDependency[];
    const uploadedPrimary = uploadedEntities[uploadedEntities.length - 1] as typeof primaryEntity;

    // Validate dependencies structure
    breadcrumb.stateChange('Validating dependencies', {
      dependencyCount: uploadedDependencies.length
    });

    for (const dep of uploadedDependencies) {
      if (!dep.type) throw new Error('Dependency missing type');
      if (!dep.tempId) throw new Error('Dependency missing tempId');
      if (!dep.data) throw new Error('Dependency missing data');
      if (dep.type === 'company' && !dep.companyType) {
        throw new Error(`Company dependency "${dep.data.name || 'unnamed'}" missing companyType`);
      }
    }

    // Build submission items array with dependencies first
    const submissionItems: any[] = [];
    const tempIdMap = new Map<string, number>(); // Maps tempId to order_index

    // Add dependency items (companies, models) first
    let orderIndex = 0;
    for (const dep of uploadedDependencies) {
    const itemType = dep.type === 'company' ? dep.companyType : dep.type;
    tempIdMap.set(dep.tempId, orderIndex);
    
    // ✅ FIXED: Don't use extractChangedFields for CREATE - include ALL data
    const { images, ...dataWithoutImages } = dep.data;
    const itemData: any = {
      ...dataWithoutImages,
      images: images as unknown as Json
    };
    
    // Add company_type for company entities
    if (dep.type === 'company') {
      itemData.company_type = dep.companyType;
    }

    // Add manufacturer dependency for ride models
    if (dep.type === 'ride_model' && dep.parentTempId) {
      const parentOrderIndex = tempIdMap.get(dep.parentTempId);
      if (parentOrderIndex !== undefined) {
        itemData._temp_manufacturer_ref = parentOrderIndex;
      }
    }

    submissionItems.push({
      item_type: itemType,
      action_type: 'create' as const,
      item_data: itemData,
      status: 'pending' as const,
      order_index: orderIndex,
      depends_on: null // Dependencies don't have parents
    });

    orderIndex++;
  }

  // Add primary entity last
  // ✅ FIXED: Don't use extractChangedFields for CREATE - include ALL data
  const { images: primaryImages, ...primaryDataWithoutImages } = uploadedPrimary.data;
  const primaryData: any = {
    ...primaryDataWithoutImages,
    images: primaryImages as unknown as Json
  };

  // Store location reference for park submissions (will be created in relational table)
  if (uploadedPrimary.type === 'park' && uploadedPrimary.data.location) {
    primaryData._temp_location = uploadedPrimary.data.location;
    delete primaryData.location; // Remove the original location object
    console.log('[submitCompositeCreation] Stored location for relational insert:', primaryData._temp_location);
  }

  // Map temporary IDs to order indices for foreign keys
  if (uploadedPrimary.type === 'park') {
    if (uploadedPrimary.data.operator_id?.startsWith('temp-')) {
      const opIndex = tempIdMap.get('temp-operator');
      if (opIndex !== undefined) primaryData._temp_operator_ref = opIndex;
      delete primaryData.operator_id;
    }
    if (uploadedPrimary.data.property_owner_id?.startsWith('temp-')) {
      const ownerIndex = tempIdMap.get('temp-property-owner');
      const operatorIndex = tempIdMap.get('temp-operator');
      
      if (ownerIndex !== undefined) {
        primaryData._temp_property_owner_ref = ownerIndex;
      } else if (operatorIndex !== undefined) {
        // Property owner references operator (operator is owner scenario)
        primaryData._temp_property_owner_ref = operatorIndex;
      }
      delete primaryData.property_owner_id;
    }
  } else if (uploadedPrimary.type === 'ride') {
    if (uploadedPrimary.data.park_id?.startsWith('temp-')) {
      const parkIndex = tempIdMap.get(uploadedPrimary.data.park_id);
      if (parkIndex !== undefined) primaryData._temp_park_ref = parkIndex;
      delete primaryData.park_id;
    }
    if (uploadedPrimary.data.manufacturer_id?.startsWith('temp-')) {
      const mfgIndex = tempIdMap.get(uploadedPrimary.data.manufacturer_id);
      if (mfgIndex !== undefined) primaryData._temp_manufacturer_ref = mfgIndex;
      delete primaryData.manufacturer_id;
    }
    if (uploadedPrimary.data.designer_id?.startsWith('temp-')) {
      const designerIndex = tempIdMap.get(uploadedPrimary.data.designer_id);
      if (designerIndex !== undefined) primaryData._temp_designer_ref = designerIndex;
      delete primaryData.designer_id;
    }
    if (uploadedPrimary.data.ride_model_id?.startsWith('temp-')) {
      const modelIndex = tempIdMap.get(uploadedPrimary.data.ride_model_id);
      if (modelIndex !== undefined) primaryData._temp_ride_model_ref = modelIndex;
      delete primaryData.ride_model_id;
    }
  }

  // CRITICAL: Validate all temp refs were properly resolved
  const validateTempRefs = () => {
    const errors: string[] = [];
    
    if (uploadedPrimary.type === 'park') {
      if ('_temp_operator_ref' in primaryData && primaryData._temp_operator_ref === undefined) {
        errors.push('Invalid operator reference - dependency not found');
      }
      if ('_temp_property_owner_ref' in primaryData && primaryData._temp_property_owner_ref === undefined) {
        errors.push('Invalid property owner reference - dependency not found');
      }
    } else if (uploadedPrimary.type === 'ride') {
      if ('_temp_park_ref' in primaryData && primaryData._temp_park_ref === undefined) {
        errors.push('Invalid park reference - dependency not found');
      }
      if ('_temp_manufacturer_ref' in primaryData && primaryData._temp_manufacturer_ref === undefined) {
        errors.push('Invalid manufacturer reference - dependency not found');
      }
      if ('_temp_designer_ref' in primaryData && primaryData._temp_designer_ref === undefined) {
        errors.push('Invalid designer reference - dependency not found');
      }
      if ('_temp_ride_model_ref' in primaryData && primaryData._temp_ride_model_ref === undefined) {
        errors.push('Invalid ride model reference - dependency not found');
      }
    }
    
    if (errors.length > 0) {
      // Report to system alerts (non-blocking)
      import('./pipelineAlerts').then(async ({ reportTempRefError }) => {
        try {
          const { data: { user } } = await supabase.auth.getUser();
          if (user) {
            await reportTempRefError(uploadedPrimary.type, errors, user.id);
          }
        } catch (e) {
          console.warn('Failed to report temp ref error:', e);
        }
      });
      
      throw new Error(`Temp reference validation failed: ${errors.join(', ')}`);
    }
  };

  validateTempRefs();

  submissionItems.push({
    item_type: uploadedPrimary.type,
    action_type: 'create' as const,
    item_data: primaryData,
    status: 'pending' as const,
    order_index: orderIndex,
    depends_on: null // Will be set by RPC based on refs
  });

  // Pre-validation to catch issues early with actionable error messages
  if (uploadedPrimary.type === 'park') {
    if (!primaryData.name) throw new Error('Park name is required');
    if (!primaryData.slug) throw new Error('Park slug is required');
    if (!primaryData.park_type) throw new Error('Park type is required');
    if (!primaryData.status) throw new Error('Park status is required');
  } else if (uploadedPrimary.type === 'ride') {
    if (!primaryData.name) throw new Error('Ride name is required');
    if (!primaryData.slug) throw new Error('Ride slug is required');
    if (!primaryData.status) throw new Error('Ride status is required');
  }

  // Validate dependencies
  for (const dep of uploadedDependencies) {
    if (dep.type === 'company') {
      if (!dep.data.name) throw new Error(`${dep.companyType || 'Company'} name is required`);
      if (!dep.data.slug) throw new Error(`${dep.companyType || 'Company'} slug is required`);
      if (!dep.data.company_type && !dep.companyType) {
        throw new Error('Company type is required');
      }
    }
  }

    // Use RPC to create submission with items atomically with retry logic
    breadcrumb.apiCall('create_submission_with_items', 'RPC');
    const { toast } = await import('@/hooks/use-toast');
    
    const result = await withRetry(
    async () => {
      const { data, error } = await supabase.rpc('create_submission_with_items', {
        p_user_id: userId,
        p_submission_type: uploadedPrimary.type,
        p_content: { action: 'create' } as unknown as Json,
        p_items: submissionItems as unknown as Json[]
      });

      if (error) {
        // Extract Supabase error details for better error logging
        const supabaseError = error as { message?: string; code?: string; details?: string; hint?: string };
        const errorMessage = supabaseError.message || 'Unknown error';
        const errorCode = supabaseError.code;
        const errorDetails = supabaseError.details;
        const errorHint = supabaseError.hint;
        
        // Create proper Error instance with enhanced context
        const enhancedError = new Error(
          `Composite submission failed: ${errorMessage}${errorDetails ? `\nDetails: ${errorDetails}` : ''}${errorHint ? `\nHint: ${errorHint}` : ''}`
        );
        
        // Attach Supabase metadata for retry logic
        (enhancedError as any).supabaseCode = errorCode;
        (enhancedError as any).supabaseDetails = errorDetails;
        (enhancedError as any).supabaseHint = errorHint;
        
        throw enhancedError;
      }

      return data;
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      maxDelay: 10000,
      onRetry: (attempt, error, delay) => {
        logger.warn('Retrying composite submission', {
          attempt,
          maxAttempts: 3,
          delay,
          error: error instanceof Error ? error.message : String(error),
          primaryType: uploadedPrimary.type,
          dependencyCount: dependencies.length
        });
        
        // Show user feedback
        toast({
          title: 'Submission retry',
          description: `Attempt ${attempt}/3 - Retrying in ${Math.round(delay / 1000)}s...`,
        });
      },
      shouldRetry: (error) => {
        // Don't retry validation errors
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('suspended')) return false;
          if (message.includes('slug')) return false;
          if (message.includes('already exists')) return false;
          if (message.includes('duplicate')) return false;
          if (message.includes('permission')) return false;
          if (message.includes('forbidden')) return false;
          if (message.includes('unauthorized')) return false;
        }
        
        // Use default retryable error detection from retryHelpers
        return isRetryableError(error);
      }
    }
    ).catch((error) => {
      // Final failure - log and throw
      handleError(error, {
        action: 'Composite submission',
        metadata: { 
          primaryType: uploadedPrimary.type, 
          dependencyCount: dependencies.length,
          supabaseCode: (error as any).supabaseCode,
          supabaseDetails: (error as any).supabaseDetails,
          supabaseHint: (error as any).supabaseHint,
          retriesExhausted: true
        },
      });
      
      throw error;
    });

    breadcrumb.stateChange('Composite submission successful', {
      submissionId: result
    });

    return { submitted: true, submissionId: result };
  } catch (error) {
    // Ensure error is always an Error instance with context
    const enrichedError = error instanceof Error 
      ? error 
      : new Error(`Composite submission failed: ${String(error)}`);
    
    // Attach metadata for better debugging
    (enrichedError as any).originalError = error;
    (enrichedError as any).primaryType = primaryEntity?.type;
    (enrichedError as any).dependencyCount = dependencies?.length;
    
    throw enrichedError;
  }
}

/**
 * ⚠️ CRITICAL SECURITY PATTERN ⚠️
 * 
 * Submits a new park for creation through the moderation queue.
 * This is the ONLY correct way to create parks.
 * 
 * DO NOT use direct database inserts:
 * ❌ await supabase.from('parks').insert(data)  // BYPASSES MODERATION!
 * ✅ await submitParkCreation(data, userId)      // CORRECT
 * 
 * Flow: User Submit → Moderation Queue → Approval → Versioning → Live
 * 
 * Even moderators/admins must use this function to ensure proper versioning and audit trails.
 * 
 * @see docs/SUBMISSION_FLOW.md for complete documentation
 * @param data - The park form data to submit
 * @param userId - The ID of the user submitting the park
 * @returns Object containing submitted boolean and submissionId
 */
export async function submitParkCreation(
  data: ParkFormData & { _compositeSubmission?: any },
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Phase 3: Rate limiting check
  checkRateLimitOrThrow(userId, 'park_creation');
  recordSubmissionAttempt(userId);
  
  console.info('[submitParkCreation] Received data:', {
    hasLocation: !!data.location,
    hasLocationId: !!data.location_id,
    locationData: data.location,
    parkName: data.name,
    hasComposite: !!data._compositeSubmission
  });
  
  // Validate required fields client-side
  assertValid(validateParkCreateFields(data));
  
  // Check for composite submission with dependencies
  if (data._compositeSubmission) {
    const dependencies: CompositeSubmissionDependency[] = [];
    
    // Check if operator and owner are the same new entity
    const operatorIsOwner = 
      data._compositeSubmission.new_operator && 
      data._compositeSubmission.new_property_owner &&
      data._compositeSubmission.new_operator === data._compositeSubmission.new_property_owner;
    
    if (data._compositeSubmission.new_operator) {
      dependencies.push({
        type: 'company',
        data: { ...data._compositeSubmission.new_operator, company_type: 'operator' },
        tempId: 'temp-operator',
        companyType: 'operator'
      });
    }
    
    // Only add separate property owner if different from operator
    if (data._compositeSubmission.new_property_owner && !operatorIsOwner) {
      dependencies.push({
        type: 'company',
        data: { ...data._compositeSubmission.new_property_owner, company_type: 'property_owner' },
        tempId: 'temp-property-owner',
        companyType: 'property_owner'
      });
    }
    
    if (dependencies.length > 0) {
      // Copy location data into composite park data
      const parkData = {
        ...data._compositeSubmission.park,
        location: data.location,
        location_id: data.location_id
      };
      
      return submitCompositeCreation(
        { type: 'park', data: parkData },
        dependencies,
        userId
      );
    }
  }

  // Standard single-entity creation with retry logic
  const { withRetry } = await import('./retryHelpers');
  
  // Check if user is banned (with quick retry for read operation)
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'park_creation').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }

  // Upload any pending local images first (no retry - handled internally)
  let processedImages = data.images;
  if (data.images?.uploaded && data.images.uploaded.length > 0) {
    try {
      const uploadedImages = await uploadPendingImages(data.images.uploaded);
      processedImages = {
        ...data.images,
        uploaded: uploadedImages
      };
    } catch (error: unknown) {
      handleError(error, {
        action: 'Upload park images',
      });
      throw new Error(`Failed to upload images: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  // Create submission with retry logic
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      // Create the main submission record
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'park',
          status: 'pending' as const
        })
        .select('id')
        .single();

      if (submissionError) throw submissionError;

      // Get image URLs/IDs from processed images using assignments
      const uploadedImages = processedImages?.uploaded || [];
      const bannerIndex = processedImages?.banner_assignment;
      const cardIndex = processedImages?.card_assignment;
      
      const bannerImage = (bannerIndex !== null && bannerIndex !== undefined) ? uploadedImages[bannerIndex] : null;
      const cardImage = (cardIndex !== null && cardIndex !== undefined) ? uploadedImages[cardIndex] : null;
      
      // Insert into relational park_submissions table
      const tempLocationData = data.location ? {
        name: data.location.name,
        street_address: data.location.street_address || null,
        city: data.location.city || null,
        state_province: data.location.state_province || null,
        country: data.location.country,
        latitude: data.location.latitude,
        longitude: data.location.longitude,
        timezone: data.location.timezone || null,
        postal_code: data.location.postal_code || null,
        display_name: data.location.display_name
      } : null;
      
      console.info('[submitParkCreation] Saving to park_submissions:', {
        name: data.name,
        hasLocation: !!data.location,
        hasLocationId: !!data.location_id,
        hasLocationData: !!tempLocationData
      });
      
      const { data: parkSubmission, error: parkSubmissionError } = await supabase
        .from('park_submissions' as any)
        .insert({
          submission_id: submissionData.id,
          name: data.name,
          slug: data.slug,
          description: data.description || null,
          park_type: data.park_type,
          status: data.status,
          opening_date: data.opening_date ? new Date(data.opening_date).toISOString().split('T')[0] : null,
          closing_date: data.closing_date ? new Date(data.closing_date).toISOString().split('T')[0] : null,
          website_url: data.website_url || null,
          phone: data.phone || null,
          email: data.email || null,
          operator_id: data.operator_id || null,
          property_owner_id: data.property_owner_id || null,
          location_id: data.location_id || null,
          banner_image_url: bannerImage?.url || data.banner_image_url || null,
          banner_image_id: bannerImage?.cloudflare_id || data.banner_image_id || null,
          card_image_url: cardImage?.url || data.card_image_url || null,
          card_image_id: cardImage?.cloudflare_id || data.card_image_id || null
        } as any)
        .select('id')
        .single();

      if (parkSubmissionError) throw parkSubmissionError;

      // Create location in relational table if provided
      if (tempLocationData) {
        const { error: locationError } = await supabase
          .from('park_submission_locations' as any)
          .insert({
            park_submission_id: (parkSubmission as any).id,
            ...tempLocationData
          } as any);

        if (locationError) {
          console.error('[submitParkCreation] Failed to create location:', locationError);
          throw new Error(`Failed to save location data: ${locationError.message}`);
        }

        console.info('[submitParkCreation] Created park_submission_location', {
          parkSubmissionId: (parkSubmission as any).id,
          locationName: tempLocationData.name
        });
      }

      // Create submission_items record linking to park_submissions
      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'park',
          action_type: 'create',
          park_submission_id: (parkSubmission as any).id,
          status: 'pending' as const,
          order_index: 0
        } as any);

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying park submission', { 
          attempt, 
          delay, 
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error) 
        });
        
        // Emit event for UI indicator with rate limit info
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'park',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        // Don't retry validation/business logic errors
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('suspended')) return false;
          if (message.includes('slug')) return false;
          if (message.includes('already exists')) return false;
          if (message.includes('duplicate')) return false;
          if (message.includes('permission')) return false;
          if (message.includes('forbidden')) return false;
          if (message.includes('unauthorized')) return false;
        }
        
        return isRetryableError(error);
      }
    }
  ).then((data) => {
    // Emit success event
    window.dispatchEvent(new CustomEvent('submission-retry-success', {
      detail: { id: retryId }
    }));
    return data;
  }).catch((error) => {
    const errorId = handleError(error, {
      action: 'Park submission',
      metadata: { retriesExhausted: true },
    });
    
    // Emit failure event
    window.dispatchEvent(new CustomEvent('submission-retry-failed', {
      detail: { id: retryId, errorId }
    }));
    
    throw error;
  });

  return result;
}

/**
 * ⚠️ CRITICAL SECURITY PATTERN ⚠️
 * 
 * Submits an update to an existing park through the moderation queue.
 * This is the ONLY correct way to update parks.
 * 
 * DO NOT use direct database updates:
 * ❌ await supabase.from('parks').update(data)  // BYPASSES MODERATION!
 * ✅ await submitParkUpdate(parkId, data, userId) // CORRECT
 * 
 * Flow: User Submit → Moderation Queue → Approval → Versioning → Live
 * 
 * Even moderators/admins must use this function to ensure proper versioning and audit trails.
 * 
 * @see docs/SUBMISSION_FLOW.md for complete documentation
 * @param parkId - The ID of the park to update
 * @param data - The updated park form data
 * @param userId - The ID of the user submitting the update
 * @returns Object containing submitted boolean and submissionId
 */
export async function submitParkUpdate(
  parkId: string,
  data: ParkFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Phase 3: Rate limiting check
  checkRateLimitOrThrow(userId, 'park_update');
  recordSubmissionAttempt(userId);
  
  const { withRetry, isRetryableError } = await import('./retryHelpers');

  // Check if user is banned - with retry for transient failures
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'park_update').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }

  // Fetch existing park data first - with retry for transient failures
  const existingPark = await withRetry(
    async () => {
      const { data: existingPark, error: fetchError } = await supabase
        .from('parks')
        .select('id, name, slug, description, park_type, status, opening_date, opening_date_precision, closing_date, closing_date_precision, website_url, phone, email, location_id, operator_id, property_owner_id, banner_image_url, banner_image_id, card_image_url, card_image_id')
        .eq('id', parkId)
        .single();

      if (fetchError) throw new Error(`Failed to fetch park: ${fetchError.message}`);
      if (!existingPark) throw new Error('Park not found');
      
      return existingPark;
    },
    { maxAttempts: 2 }
  );

  // CRITICAL: Block new photo uploads on edits
  // Photos can only be submitted during creation or via the photo gallery
  if (data.images?.uploaded && data.images.uploaded.some(img => img.isLocal)) {
    throw new Error('Photo uploads are not allowed during edits. Please use the photo gallery to submit additional photos.');
  }

  // Only allow banner/card reassignments from existing photos
  let processedImages = data.images;

  // Main submission logic with retry and error handling
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      // Create the main submission record
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'park',
          status: 'pending' as const
        })
        .select('id')
        .single();

      if (submissionError) throw submissionError;

      // Extract changed fields
      const changedFields = extractChangedFields(data, existingPark as any);

      // Handle location data properly
      let tempLocationData: any = null;
      if (data.location) {
        tempLocationData = {
          name: data.location.name,
          street_address: data.location.street_address || null,
          city: data.location.city || null,
          state_province: data.location.state_province || null,
          country: data.location.country,
          latitude: data.location.latitude,
          longitude: data.location.longitude,
          timezone: data.location.timezone || null,
          postal_code: data.location.postal_code || null,
          display_name: data.location.display_name
        };
      }

      // ✅ FIXED: Insert into park_submissions table (relational pattern)
      const { data: parkSubmission, error: parkSubmissionError } = await supabase
        .from('park_submissions')
        .insert({
          submission_id: submissionData.id,
          name: changedFields.name ?? existingPark.name,
          slug: changedFields.slug ?? existingPark.slug,
          description: changedFields.description !== undefined ? changedFields.description : existingPark.description,
          park_type: changedFields.park_type ?? existingPark.park_type,
          status: changedFields.status ?? existingPark.status,
          opening_date: changedFields.opening_date !== undefined ? changedFields.opening_date : existingPark.opening_date,
          opening_date_precision: changedFields.opening_date_precision !== undefined ? changedFields.opening_date_precision : existingPark.opening_date_precision,
          closing_date: changedFields.closing_date !== undefined ? changedFields.closing_date : existingPark.closing_date,
          closing_date_precision: changedFields.closing_date_precision !== undefined ? changedFields.closing_date_precision : existingPark.closing_date_precision,
          website_url: changedFields.website_url !== undefined ? changedFields.website_url : existingPark.website_url,
          phone: changedFields.phone !== undefined ? changedFields.phone : existingPark.phone,
          email: changedFields.email !== undefined ? changedFields.email : existingPark.email,
          operator_id: changedFields.operator_id !== undefined ? changedFields.operator_id : existingPark.operator_id,
          property_owner_id: changedFields.property_owner_id !== undefined ? changedFields.property_owner_id : existingPark.property_owner_id,
          location_id: changedFields.location_id !== undefined ? changedFields.location_id : existingPark.location_id,
          banner_image_url: changedFields.banner_image_url !== undefined ? changedFields.banner_image_url : existingPark.banner_image_url,
          banner_image_id: changedFields.banner_image_id !== undefined ? changedFields.banner_image_id : existingPark.banner_image_id,
          card_image_url: changedFields.card_image_url !== undefined ? changedFields.card_image_url : existingPark.card_image_url,
          card_image_id: changedFields.card_image_id !== undefined ? changedFields.card_image_id : existingPark.card_image_id,
        })
        .select('id')
        .single();

      if (parkSubmissionError) throw parkSubmissionError;

      // Create location in relational table if provided
      if (tempLocationData) {
        const { error: locationError } = await supabase
          .from('park_submission_locations' as any)
          .insert({
            park_submission_id: (parkSubmission as any).id,
            ...tempLocationData
          } as any);

        if (locationError) {
          console.error('[submitParkEdit] Failed to create location:', locationError);
          throw new Error(`Failed to save location data: ${locationError.message}`);
        }

        console.info('[submitParkEdit] Created park_submission_location', {
          parkSubmissionId: (parkSubmission as any).id,
          locationName: tempLocationData.name
        });
      }

      // ✅ Create submission_items referencing park_submission (no JSON data)
      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'park',
          action_type: 'edit',
          item_data: {
            park_id: parkId,  // Only reference IDs
            images: processedImages as unknown as Json
          },
          original_data: JSON.parse(JSON.stringify(existingPark)),
          status: 'pending' as const,
          order_index: 0,
          park_submission_id: parkSubmission.id
        });

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying park update submission', { 
          attempt, 
          delay, 
          parkId,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        
        // Emit event for UI retry indicator with rate limit info
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'park update',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        // Don't retry validation/business logic errors
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('slug')) return false;
          if (message.includes('permission')) return false;
          if (message.includes('not found')) return false;
          if (message.includes('not allowed')) return false;
        }
        
        return isRetryableError(error);
      }
    }
  ).then((data) => {
    // Emit success event
    window.dispatchEvent(new CustomEvent('submission-retry-success', {
      detail: { id: retryId }
    }));
    return data;
  }).catch((error) => {
    const errorId = handleError(error, {
      action: 'Park update submission',
      userId,
      metadata: { retriesExhausted: true, parkId },
    });
    
    // Emit failure event
    window.dispatchEvent(new CustomEvent('submission-retry-failed', {
      detail: { id: retryId, errorId }
    }));
    
    throw error;
  });

  return result;
}

/**
 * ⚠️ CRITICAL SECURITY PATTERN ⚠️
 * 
 * Submits a new ride for creation through the moderation queue.
 * This is the ONLY correct way to create rides.
 * 
 * DO NOT use direct database inserts:
 * ❌ await supabase.from('rides').insert(data)  // BYPASSES MODERATION!
 * ✅ await submitRideCreation(data, userId)      // CORRECT
 * 
 * Flow: User Submit → Moderation Queue → Approval → Versioning → Live
 * 
 * Even moderators/admins must use this function to ensure proper versioning and audit trails.
 * 
 * @see docs/SUBMISSION_FLOW.md for complete documentation
 * @param data - The ride form data to submit
 * @param userId - The ID of the user submitting the ride
 * @returns Object containing submitted boolean and submissionId
 */
export async function submitRideCreation(
  data: RideFormData & { 
    _tempNewPark?: any;
    _tempNewManufacturer?: any; 
    _tempNewDesigner?: any;
    _tempNewRideModel?: any;
  },
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Phase 3: Rate limiting check
  checkRateLimitOrThrow(userId, 'ride_creation');
  recordSubmissionAttempt(userId);
  
  // Validate required fields client-side
  assertValid(validateRideCreateFields(data));
  
  // Check for composite submission with dependencies
  if (data._tempNewPark || data._tempNewManufacturer || data._tempNewDesigner || data._tempNewRideModel) {
    const dependencies: CompositeSubmissionDependency[] = [];
    
    // Handle new park operator (from nested park)
    if (data._tempNewPark?._tempNewOperator) {
      dependencies.push({
        type: 'company',
        data: { ...data._tempNewPark._tempNewOperator, company_type: 'operator' },
        tempId: 'temp-park-operator',
        companyType: 'operator'
      });
    }
    
    // Handle new park property owner (from nested park)
    if (data._tempNewPark?._tempNewPropertyOwner) {
      dependencies.push({
        type: 'company',
        data: { ...data._tempNewPark._tempNewPropertyOwner, company_type: 'property_owner' },
        tempId: 'temp-park-owner',
        companyType: 'property_owner'
      });
    }
    
    // Handle new park (depends on operator/owner)
    if (data._tempNewPark) {
      dependencies.push({
        type: 'park',
        data: {
          ...data._tempNewPark,
          operator_id: data._tempNewPark._tempNewOperator ? 'temp-park-operator' : data._tempNewPark.operator_id,
          property_owner_id: data._tempNewPark._tempNewPropertyOwner ? 'temp-park-owner' : data._tempNewPark.property_owner_id,
          _tempNewOperator: undefined,
          _tempNewPropertyOwner: undefined
        },
        tempId: 'temp-park'
      });
    }
    
    // Handle new manufacturer
    if (data._tempNewManufacturer) {
      dependencies.push({
        type: 'company',
        data: { ...data._tempNewManufacturer, company_type: 'manufacturer' },
        tempId: 'temp-manufacturer',
        companyType: 'manufacturer'
      });
    }
    
    // Handle new designer
    if (data._tempNewDesigner) {
      dependencies.push({
        type: 'company',
        data: { ...data._tempNewDesigner, company_type: 'designer' },
        tempId: 'temp-designer',
        companyType: 'designer'
      });
    }
    
    // Handle new ride model (depends on manufacturer)
    if (data._tempNewRideModel) {
      dependencies.push({
        type: 'ride_model',
        data: {
          ...data._tempNewRideModel,
          manufacturer_id: data._tempNewManufacturer ? 'temp-manufacturer' : data._tempNewRideModel.manufacturer_id
        },
        tempId: 'temp-ride-model',
        parentTempId: data._tempNewManufacturer ? 'temp-manufacturer' : undefined
      });
    }
    
    if (dependencies.length > 0) {
      // Prepare ride data with temp references
      const rideData = {
        ...data,
        park_id: data._tempNewPark ? 'temp-park' : data.park_id,
        manufacturer_id: data._tempNewManufacturer ? 'temp-manufacturer' : data.manufacturer_id,
        designer_id: data._tempNewDesigner ? 'temp-designer' : data.designer_id,
        ride_model_id: data._tempNewRideModel ? 'temp-ride-model' : data.ride_model_id,
        _tempNewPark: undefined,
        _tempNewManufacturer: undefined,
        _tempNewDesigner: undefined,
        _tempNewRideModel: undefined
      };
      
      return submitCompositeCreation(
        { type: 'ride', data: rideData },
        dependencies,
        userId
      );
    }
  }

  // Standard single-entity creation with retry logic
  const { withRetry } = await import('./retryHelpers');
  
  // Check if user is banned (with quick retry for read operation)
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'ride_creation').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }

  // Upload any pending local images first (no retry - handled internally)
  let processedImages = data.images;
  if (data.images?.uploaded && data.images.uploaded.length > 0) {
    try {
      const uploadedImages = await uploadPendingImages(data.images.uploaded);
      processedImages = {
        ...data.images,
        uploaded: uploadedImages
      };
    } catch (error: unknown) {
      handleError(error, {
        action: 'Upload ride images',
      });
      throw new Error(`Failed to upload images: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  // Create submission with retry logic
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      // Create the main submission record
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'ride',
          status: 'pending' as const
        })
        .select('id')
        .single();

      if (submissionError) throw submissionError;

      // Get image URLs/IDs from processed images using assignments
      const uploadedImages = processedImages?.uploaded || [];
      const bannerIndex = processedImages?.banner_assignment;
      const cardIndex = processedImages?.card_assignment;
      
      const bannerImage = (bannerIndex !== null && bannerIndex !== undefined) ? uploadedImages[bannerIndex] : null;
      const cardImage = (cardIndex !== null && cardIndex !== undefined) ? uploadedImages[cardIndex] : null;

      // Insert into relational ride_submissions table
      const { data: rideSubmission, error: rideSubmissionError } = await supabase
        .from('ride_submissions' as any)
        .insert({
          submission_id: submissionData.id,
          park_id: data.park_id || null,
          name: data.name,
          slug: data.slug,
          description: data.description || null,
          category: data.category,
          ride_sub_type: data.ride_sub_type || null,
          status: data.status,
          opening_date: data.opening_date ? new Date(data.opening_date).toISOString().split('T')[0] : null,
          closing_date: data.closing_date ? new Date(data.closing_date).toISOString().split('T')[0] : null,
          manufacturer_id: data.manufacturer_id || null,
          designer_id: data.designer_id || null,
          ride_model_id: data.ride_model_id || null,
          height_requirement: data.height_requirement || null,
          age_requirement: data.age_requirement || null,
          capacity_per_hour: data.capacity_per_hour || null,
          duration_seconds: data.duration_seconds || null,
          max_speed_kmh: data.max_speed_kmh || null,
          max_height_meters: data.max_height_meters || null,
          length_meters: data.length_meters || null,
          drop_height_meters: data.drop_height_meters || null,
          inversions: data.inversions || 0,
          max_g_force: data.max_g_force || null,
          coaster_type: data.coaster_type || null,
          seating_type: data.seating_type || null,
          intensity_level: data.intensity_level || null,
          banner_image_url: bannerImage?.url || data.banner_image_url || null,
          banner_image_id: bannerImage?.cloudflare_id || data.banner_image_id || null,
          card_image_url: cardImage?.url || data.card_image_url || null,
          card_image_id: cardImage?.cloudflare_id || data.card_image_id || null,
          image_url: null,
          // Category-specific fields
          track_material: (data as any).track_material || null,
          support_material: (data as any).support_material || null,
          propulsion_method: (data as any).propulsion_method || null,
          water_depth_cm: (data as any).water_depth_cm || null,
          splash_height_meters: (data as any).splash_height_meters || null,
          wetness_level: (data as any).wetness_level || null,
          flume_type: (data as any).flume_type || null,
          boat_capacity: (data as any).boat_capacity || null,
          theme_name: (data as any).theme_name || null,
          story_description: (data as any).story_description || null,
          show_duration_seconds: (data as any).show_duration_seconds || null,
          animatronics_count: (data as any).animatronics_count || null,
          projection_type: (data as any).projection_type || null,
          ride_system: (data as any).ride_system || null,
          scenes_count: (data as any).scenes_count || null,
          rotation_type: (data as any).rotation_type || null,
          motion_pattern: (data as any).motion_pattern || null,
          platform_count: (data as any).platform_count || null,
          swing_angle_degrees: (data as any).swing_angle_degrees || null,
          rotation_speed_rpm: (data as any).rotation_speed_rpm || null,
          arm_length_meters: (data as any).arm_length_meters || null,
          max_height_reached_meters: (data as any).max_height_reached_meters || null,
          min_age: (data as any).min_age || null,
          max_age: (data as any).max_age || null,
          educational_theme: (data as any).educational_theme || null,
          character_theme: (data as any).character_theme || null,
          transport_type: (data as any).transport_type || null,
          route_length_meters: (data as any).route_length_meters || null,
          stations_count: (data as any).stations_count || null,
          vehicle_capacity: (data as any).vehicle_capacity || null,
          vehicles_count: (data as any).vehicles_count || null,
          round_trip_duration_seconds: (data as any).round_trip_duration_seconds || null
        } as any)
        .select('id')
        .single();

      if (rideSubmissionError) throw rideSubmissionError;

      // Insert technical specifications if present
      if ((data as any)._technical_specifications?.length > 0) {
        const { error: techSpecError } = await supabase
          .from('ride_technical_specs' as any)
          .insert(
            (data as any)._technical_specifications.map((spec: any) => ({
              ride_submission_id: (rideSubmission as any).id,
              spec_name: spec.spec_name,
              spec_value: spec.spec_value,
              spec_type: spec.spec_type,
              category: spec.category || null,
              unit: spec.unit || null
            }))
          );
        
        if (techSpecError) {
          logger.error('Failed to insert technical specs', { error: techSpecError });
          throw techSpecError;
        }
      }

      // Insert coaster statistics if present
      if ((data as any)._coaster_statistics?.length > 0) {
        const { error: statsError } = await supabase
          .from('ride_coaster_stats' as any)
          .insert(
            (data as any)._coaster_statistics.map((stat: any) => ({
              ride_submission_id: (rideSubmission as any).id,
              stat_name: stat.stat_name,
              stat_value: stat.stat_value,
              unit: stat.unit || null,
              category: stat.category || null
            }))
          );
        
        if (statsError) {
          logger.error('Failed to insert coaster stats', { error: statsError });
          throw statsError;
        }
      }

      // Insert name history if present
      if ((data as any)._name_history?.length > 0) {
        const { error: historyError } = await supabase
          .from('ride_name_history_submissions' as any)
          .insert(
            (data as any)._name_history.map((name: any) => ({
              ride_submission_id: (rideSubmission as any).id,
              former_name: name.former_name,
              date_changed: name.date_changed ? new Date(name.date_changed).toISOString().split('T')[0] : null,
              reason: name.reason || null,
              from_year: name.from_year || null,
              to_year: name.to_year || null,
              order_index: name.order_index || 0
            }))
          );
        
        if (historyError) {
          logger.error('Failed to insert name history', { error: historyError });
          throw historyError;
        }
      }

      // Create submission_items record linking to ride_submissions
      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'ride',
          action_type: 'create',
          ride_submission_id: (rideSubmission as any).id,
          status: 'pending' as const,
          order_index: 0
        } as any);

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying ride submission', { 
          attempt, 
          delay,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        
        // Emit event for UI indicator with rate limit info
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'ride',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        // Don't retry validation/business logic errors
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('suspended')) return false;
          if (message.includes('slug')) return false;
          if (message.includes('already exists')) return false;
          if (message.includes('duplicate')) return false;
          if (message.includes('permission')) return false;
          if (message.includes('forbidden')) return false;
          if (message.includes('unauthorized')) return false;
        }
        
        return isRetryableError(error);
      }
    }
  ).then((data) => {
    // Emit success event
    window.dispatchEvent(new CustomEvent('submission-retry-success', {
      detail: { id: retryId }
    }));
    return data;
  }).catch((error) => {
    const errorId = handleError(error, {
      action: 'Ride submission',
      metadata: { retriesExhausted: true },
    });
    
    // Emit failure event
    window.dispatchEvent(new CustomEvent('submission-retry-failed', {
      detail: { id: retryId, errorId }
    }));
    
    throw error;
  });

  return result;
}

/**
 * ⚠️ CRITICAL SECURITY PATTERN ⚠️
 * 
 * Submits an update to an existing ride through the moderation queue.
 * This is the ONLY correct way to update rides.
 * 
 * DO NOT use direct database updates:
 * ❌ await supabase.from('rides').update(data)  // BYPASSES MODERATION!
 * ✅ await submitRideUpdate(rideId, data, userId) // CORRECT
 * 
 * Flow: User Submit → Moderation Queue → Approval → Versioning → Live
 * 
 * Even moderators/admins must use this function to ensure proper versioning and audit trails.
 * 
 * @see docs/SUBMISSION_FLOW.md for complete documentation
 * @param rideId - The ID of the ride to update
 * @param data - The updated ride form data
 * @param userId - The ID of the user submitting the update
 * @returns Object containing submitted boolean and submissionId
 */
export async function submitRideUpdate(
  rideId: string,
  data: RideFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Phase 3: Rate limiting check
  checkRateLimitOrThrow(userId, 'ride_update');
  recordSubmissionAttempt(userId);
  
  const { withRetry, isRetryableError } = await import('./retryHelpers');

  // Check if user is banned - with retry for transient failures
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'ride_update').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }

  // Fetch existing ride data first - with retry for transient failures
  const existingRide = await withRetry(
    async () => {
      const { data: existingRide, error: fetchError } = await supabase
        .from('rides')
        .select('*')
        .eq('id', rideId)
        .single();

      if (fetchError) throw new Error(`Failed to fetch ride: ${fetchError.message}`);
      if (!existingRide) throw new Error('Ride not found');
      
      return existingRide;
    },
    { maxAttempts: 2 }
  );

  // CRITICAL: Block new photo uploads on edits
  // Photos can only be submitted during creation or via the photo gallery
  if (data.images?.uploaded && data.images.uploaded.some(img => img.isLocal)) {
    throw new Error('Photo uploads are not allowed during edits. Please use the photo gallery to submit additional photos.');
  }

  // Only allow banner/card reassignments from existing photos
  let processedImages = data.images;

  // Main submission logic with retry and error handling
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      // Create the main submission record
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'ride',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      // Extract changed fields
      const changedFields = extractChangedFields(data, existingRide as any);

      // ✅ FIXED: Insert into ride_submissions table (relational pattern)
      const { data: rideSubmission, error: rideSubmissionError } = await supabase
        .from('ride_submissions')
        .insert({
          submission_id: submissionData.id,
          name: changedFields.name ?? existingRide.name,
          slug: changedFields.slug ?? existingRide.slug,
          description: changedFields.description !== undefined ? changedFields.description : existingRide.description,
          category: changedFields.category ?? existingRide.category,
          status: changedFields.status ?? existingRide.status,
          park_id: changedFields.park_id !== undefined ? changedFields.park_id : existingRide.park_id,
          manufacturer_id: changedFields.manufacturer_id !== undefined ? changedFields.manufacturer_id : existingRide.manufacturer_id,
          designer_id: changedFields.designer_id !== undefined ? changedFields.designer_id : existingRide.designer_id,
          ride_model_id: changedFields.ride_model_id !== undefined ? changedFields.ride_model_id : existingRide.ride_model_id,
          opening_date: changedFields.opening_date !== undefined ? changedFields.opening_date : existingRide.opening_date,
          opening_date_precision: changedFields.opening_date_precision !== undefined ? changedFields.opening_date_precision : existingRide.opening_date_precision,
          closing_date: changedFields.closing_date !== undefined ? changedFields.closing_date : existingRide.closing_date,
          closing_date_precision: changedFields.closing_date_precision !== undefined ? changedFields.closing_date_precision : existingRide.closing_date_precision,
          max_speed_kmh: changedFields.max_speed_kmh !== undefined ? changedFields.max_speed_kmh : existingRide.max_speed_kmh,
          max_height_meters: changedFields.max_height_meters !== undefined ? changedFields.max_height_meters : existingRide.max_height_meters,
          length_meters: changedFields.length_meters !== undefined ? changedFields.length_meters : existingRide.length_meters,
          duration_seconds: changedFields.duration_seconds !== undefined ? changedFields.duration_seconds : existingRide.duration_seconds,
          capacity_per_hour: changedFields.capacity_per_hour !== undefined ? changedFields.capacity_per_hour : existingRide.capacity_per_hour,
          height_requirement: changedFields.height_requirement !== undefined ? changedFields.height_requirement : existingRide.height_requirement,
          age_requirement: changedFields.age_requirement !== undefined ? changedFields.age_requirement : existingRide.age_requirement,
          inversions: changedFields.inversions !== undefined ? changedFields.inversions : existingRide.inversions,
          drop_height_meters: changedFields.drop_height_meters !== undefined ? changedFields.drop_height_meters : existingRide.drop_height_meters,
          max_g_force: changedFields.max_g_force !== undefined ? changedFields.max_g_force : existingRide.max_g_force,
          intensity_level: changedFields.intensity_level !== undefined ? changedFields.intensity_level : existingRide.intensity_level,
          coaster_type: changedFields.coaster_type !== undefined ? changedFields.coaster_type : existingRide.coaster_type,
          seating_type: changedFields.seating_type !== undefined ? changedFields.seating_type : existingRide.seating_type,
          ride_sub_type: changedFields.ride_sub_type !== undefined ? changedFields.ride_sub_type : existingRide.ride_sub_type,
          banner_image_url: changedFields.banner_image_url !== undefined ? changedFields.banner_image_url : existingRide.banner_image_url,
          banner_image_id: changedFields.banner_image_id !== undefined ? changedFields.banner_image_id : existingRide.banner_image_id,
          card_image_url: changedFields.card_image_url !== undefined ? changedFields.card_image_url : existingRide.card_image_url,
          card_image_id: changedFields.card_image_id !== undefined ? changedFields.card_image_id : existingRide.card_image_id,
        })
        .select('id')
        .single();

      if (rideSubmissionError) throw rideSubmissionError;

      // ✅ Create submission_items referencing ride_submission (no JSON data)
      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'ride',
          action_type: 'edit',
          item_data: {
            ride_id: rideId,  // Only reference IDs
            images: processedImages as unknown as Json
          },
          original_data: JSON.parse(JSON.stringify(existingRide)),
          status: 'pending' as const,
          order_index: 0,
          ride_submission_id: rideSubmission.id
        });

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying ride update submission', { 
          attempt, 
          delay, 
          rideId,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        
        // Emit event for UI retry indicator with rate limit info
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'ride update',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        // Don't retry validation/business logic errors
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('suspended')) return false;
          if (message.includes('slug')) return false;
          if (message.includes('already exists')) return false;
          if (message.includes('duplicate')) return false;
          if (message.includes('permission')) return false;
          if (message.includes('forbidden')) return false;
          if (message.includes('unauthorized')) return false;
          if (message.includes('not found')) return false;
          if (message.includes('not allowed')) return false;
        }
        
        return isRetryableError(error);
      }
    }
  ).then((data) => {
    // Emit success event
    window.dispatchEvent(new CustomEvent('submission-retry-success', {
      detail: { id: retryId }
    }));
    return data;
  }).catch((error) => {
    const errorId = handleError(error, {
      action: 'Ride update submission',
      userId,
      metadata: { retriesExhausted: true, rideId },
    });
    
    // Emit failure event
    window.dispatchEvent(new CustomEvent('submission-retry-failed', {
      detail: { id: retryId, errorId }
    }));
    
    throw error;
  });

  return result;
}

/**
 * ⚠️ CRITICAL SECURITY PATTERN ⚠️
 * 
 * Submits a new ride model for creation through the moderation queue.
 * This is the ONLY correct way to create ride models.
 * 
 * DO NOT use direct database inserts:
 * ❌ await supabase.from('ride_models').insert(data)  // BYPASSES MODERATION!
 * ✅ await submitRideModelCreation(data, userId)      // CORRECT
 * 
 * Flow: User Submit → Moderation Queue → Approval → Versioning → Live
 * 
 * @param data - The ride model form data to submit
 * @param userId - The ID of the user submitting the ride model
 * @returns Object containing submitted boolean and submissionId
 */
export async function submitRideModelCreation(
  data: RideModelFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Rate limiting check
  checkRateLimitOrThrow(userId, 'ride_model_creation');
  recordSubmissionAttempt(userId);
  
  // Breadcrumb tracking
  breadcrumb.userAction('Start ride model submission', 'submitRideModelCreation', { userId });
  
  // Validate required fields client-side
  assertValid(validateRideModelCreateFields(data));
  
  // Ban check with retry logic
  const { withRetry } = await import('./retryHelpers');
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'ride_model_creation').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }

  // Upload any pending local images first
  let processedImages = data.images;
  if (data.images?.uploaded && data.images.uploaded.length > 0) {
    try {
      const uploadedImages = await uploadPendingImages(data.images.uploaded);
      processedImages = {
        ...data.images,
        uploaded: uploadedImages
      };
    } catch (error: unknown) {
      handleError(error, {
        action: 'Upload ride model images',
      });
      throw new Error(`Failed to upload images: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  // Submit with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      // Create the main submission record
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'ride_model',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      // Create the submission item with actual ride model data
      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'ride_model',
          action_type: 'create',
          item_data: {
            // ✅ FIXED: Don't use extractChangedFields for CREATE - include ALL data
            ...(() => {
              const { images, ...dataWithoutImages } = data;
              return dataWithoutImages;
            })(),
            images: processedImages as unknown as Json
          },
          status: 'pending' as const,
          order_index: 0
        });

      if (itemError) throw itemError;

      // Insert into ride_model_submissions table for relational integrity
      const { data: rideModelSubmissionData, error: rideModelSubmissionError } = await supabase
        .from('ride_model_submissions')
        .insert({
          submission_id: submissionData.id,
          name: data.name,
          slug: data.slug,
          manufacturer_id: data.manufacturer_id,
          category: data.category,
          ride_type: data.ride_type || data.category,
          description: data.description || null,
          banner_image_url: data.banner_image_url || null,
          banner_image_id: data.banner_image_id || null,
          card_image_url: data.card_image_url || null,
          card_image_id: data.card_image_id || null
        })
        .select()
        .single();

      if (rideModelSubmissionError) {
        logger.error('Failed to insert ride model submission', { error: rideModelSubmissionError });
        throw rideModelSubmissionError;
      }

      // Insert technical specifications into submission table
      if ((data as any)._technical_specifications?.length > 0) {
        const { error: techSpecError } = await supabase
          .from('ride_model_submission_technical_specifications')
          .insert(
            (data as any)._technical_specifications.map((spec: any) => ({
              ride_model_submission_id: rideModelSubmissionData.id,
              spec_name: spec.spec_name,
              spec_value: spec.spec_value,
              spec_unit: spec.spec_unit || null,
              category: spec.category || null,
              display_order: spec.display_order || 0
            }))
          );
        
        if (techSpecError) {
          logger.error('Failed to insert ride model technical specs', { error: techSpecError });
          throw techSpecError;
        }
        
        logger.log('✅ Ride model technical specifications inserted:', (data as any)._technical_specifications.length);
      }

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying ride model submission', { 
          attempt, 
          delay,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'ride_model',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('suspended')) return false;
          if (message.includes('slug')) return false;
          if (message.includes('already exists')) return false;
          if (message.includes('duplicate')) return false;
          if (message.includes('permission')) return false;
          if (message.includes('forbidden')) return false;
          if (message.includes('unauthorized')) return false;
        }
        return isRetryableError(error);
      }
    }
  ).then((data) => {
    // Emit success event
    window.dispatchEvent(new CustomEvent('submission-retry-success', {
      detail: { id: retryId }
    }));
    return data;
  }).catch((error) => {
    const errorId = handleError(error, {
      action: 'Ride model submission',
      metadata: { retriesExhausted: true },
    });
    
    // Emit failure event
    window.dispatchEvent(new CustomEvent('submission-retry-failed', {
      detail: { id: retryId, errorId }
    }));
    
    throw error;
  });

  return result;
}

/**
 * ⚠️ CRITICAL SECURITY PATTERN ⚠️
 * 
 * Submits a ride model update through the moderation queue.
 * This is the ONLY correct way to update ride models.
 */
export async function submitRideModelUpdate(
  rideModelId: string,
  data: RideModelFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Rate limiting check
  checkRateLimitOrThrow(userId, 'ride_model_update');
  recordSubmissionAttempt(userId);
  
  // Breadcrumb tracking
  breadcrumb.userAction('Start ride model update', 'submitRideModelUpdate', { userId, rideModelId });
  
  // Ban check with retry logic
  const { withRetry } = await import('./retryHelpers');
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'ride_model_update').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }

  // Fetch existing ride model
  const { data: existingModel, error: fetchError } = await supabase
    .from('ride_models')
    .select('*')
    .eq('id', rideModelId)
    .single();

  if (fetchError) throw new Error(`Failed to fetch ride model: ${fetchError.message}`);
  if (!existingModel) throw new Error('Ride model not found');

  // CRITICAL: Block new photo uploads on edits
  if (data.images?.uploaded && data.images.uploaded.some(img => img.isLocal)) {
    throw new Error('Photo uploads are not allowed during edits. Please use the photo gallery to submit additional photos.');
  }

  let processedImages = data.images;

  // Submit with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      // Create the main submission record
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'ride_model',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      // Create the submission item with actual ride model data
      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'ride_model',
          action_type: 'edit',
          item_data: {
            ...extractChangedFields(data, existingModel as any),
            ride_model_id: rideModelId,  // Always include for relational integrity
            images: processedImages as unknown as Json
          },
          original_data: JSON.parse(JSON.stringify(existingModel)),
          status: 'pending' as const,
          order_index: 0
        });

      if (itemError) throw itemError;

      // Insert into ride_model_submissions table for relational integrity
      const { data: rideModelSubmissionData, error: rideModelSubmissionError } = await supabase
        .from('ride_model_submissions')
        .insert({
          submission_id: submissionData.id,
          name: data.name,
          slug: data.slug,
          manufacturer_id: data.manufacturer_id,
          category: data.category,
          ride_type: data.ride_type || data.category,
          description: data.description || null,
          banner_image_url: data.banner_image_url || null,
          banner_image_id: data.banner_image_id || null,
          card_image_url: data.card_image_url || null,
          card_image_id: data.card_image_id || null
        })
        .select()
        .single();

      if (rideModelSubmissionError) {
        logger.error('Failed to insert ride model update submission', { error: rideModelSubmissionError });
        throw rideModelSubmissionError;
      }

      // Insert technical specifications into submission table
      if ((data as any)._technical_specifications?.length > 0) {
        const { error: techSpecError } = await supabase
          .from('ride_model_submission_technical_specifications')
          .insert(
            (data as any)._technical_specifications.map((spec: any) => ({
              ride_model_submission_id: rideModelSubmissionData.id,
              spec_name: spec.spec_name,
              spec_value: spec.spec_value,
              spec_unit: spec.spec_unit || null,
              category: spec.category || null,
              display_order: spec.display_order || 0
            }))
          );
        
        if (techSpecError) {
          logger.error('Failed to insert ride model update technical specs', { error: techSpecError });
          throw techSpecError;
        }
        
        logger.log('✅ Ride model update technical specifications inserted:', (data as any)._technical_specifications.length);
      }

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying ride model update', { 
          attempt, 
          delay,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'ride_model_update',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('suspended')) return false;
          if (message.includes('slug')) return false;
          if (message.includes('already exists')) return false;
          if (message.includes('duplicate')) return false;
          if (message.includes('permission')) return false;
          if (message.includes('forbidden')) return false;
          if (message.includes('unauthorized')) return false;
        }
        return isRetryableError(error);
      }
    }
  ).then((data) => {
    window.dispatchEvent(new CustomEvent('submission-retry-success', {
      detail: { id: retryId }
    }));
    return data;
  }).catch((error) => {
    const errorId = handleError(error, {
      action: 'Ride model update submission',
      metadata: { retriesExhausted: true },
    });
    
    window.dispatchEvent(new CustomEvent('submission-retry-failed', {
      detail: { id: retryId, errorId }
    }));
    
    throw error;
  });

  return result;
}

/**
 * ⚠️ CRITICAL SECURITY PATTERN ⚠️
 * 
 * Submits a new manufacturer for creation through the moderation queue.
 */
export async function submitManufacturerCreation(
  data: CompanyFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Rate limiting check
  checkRateLimitOrThrow(userId, 'manufacturer_creation');
  recordSubmissionAttempt(userId);
  
  // Breadcrumb tracking
  breadcrumb.userAction('Start manufacturer submission', 'submitManufacturerCreation', { userId });
  
  // Validate required fields client-side
  assertValid(validateCompanyCreateFields({ ...data, company_type: 'manufacturer' }));
  
  // Ban check with retry logic
  const { withRetry } = await import('./retryHelpers');
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'manufacturer_creation').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }
  
  // Upload images
  let processedImages = data.images;
  if (data.images?.uploaded && data.images.uploaded.length > 0) {
    try {
      const uploadedImages = await uploadPendingImages(data.images.uploaded);
      processedImages = { ...data.images, uploaded: uploadedImages };
    } catch (error: unknown) {
      handleError(error, {
        action: 'Upload manufacturer images',
      });
      throw new Error(`Failed to upload images: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  // Submit with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'manufacturer',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'manufacturer',
          action_type: 'create',
          item_data: {
            // ✅ FIXED: Don't use extractChangedFields for CREATE - include ALL data
            ...(() => {
              const { images, ...dataWithoutImages } = data;
              return dataWithoutImages;
            })(),
            company_type: 'manufacturer',
            images: processedImages as unknown as Json
          },
          status: 'pending' as const,
          order_index: 0
        });

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying manufacturer submission', { 
          attempt, 
          delay,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'manufacturer',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('suspended')) return false;
          if (message.includes('slug')) return false;
          if (message.includes('already exists')) return false;
          if (message.includes('duplicate')) return false;
          if (message.includes('permission')) return false;
          if (message.includes('forbidden')) return false;
          if (message.includes('unauthorized')) return false;
        }
        return isRetryableError(error);
      }
    }
  ).then((data) => {
    window.dispatchEvent(new CustomEvent('submission-retry-success', {
      detail: { id: retryId }
    }));
    return data;
  }).catch((error) => {
    const errorId = handleError(error, {
      action: 'Manufacturer submission',
      metadata: { retriesExhausted: true },
    });
    
    window.dispatchEvent(new CustomEvent('submission-retry-failed', {
      detail: { id: retryId, errorId }
    }));
    
    throw error;
  });

  return result;
}

export async function submitManufacturerUpdate(
  companyId: string,
  data: CompanyFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Rate limiting check
  checkRateLimitOrThrow(userId, 'manufacturer_update');
  recordSubmissionAttempt(userId);
  
  // Breadcrumb tracking
  breadcrumb.userAction('Start manufacturer update', 'submitManufacturerUpdate', { userId, companyId });
  
  // Ban check with retry logic
  const { withRetry } = await import('./retryHelpers');
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'manufacturer_update').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }
  
  const { data: existingCompany, error: fetchError } = await supabase
    .from('companies')
    .select('*')
    .eq('id', companyId)
    .single();

  if (fetchError) throw new Error(`Failed to fetch manufacturer: ${fetchError.message}`);
  if (!existingCompany) throw new Error('Manufacturer not found');

  // CRITICAL: Block new photo uploads on edits
  if (data.images?.uploaded && data.images.uploaded.some(img => img.isLocal)) {
    throw new Error('Photo uploads are not allowed during edits. Please use the photo gallery to submit additional photos.');
  }

  let processedImages = data.images;

  // Submit with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'manufacturer',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'manufacturer',
          action_type: 'edit',
          item_data: {
            ...extractChangedFields(data, existingCompany as any),
            company_id: companyId,  // Always include for relational integrity
            company_type: 'manufacturer',  // Always include for entity type discrimination
            images: processedImages as unknown as Json
          },
          original_data: JSON.parse(JSON.stringify(existingCompany)),
          status: 'pending' as const,
          order_index: 0
        });

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying manufacturer update', { 
          attempt, 
          delay,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'manufacturer_update',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('slug')) return false;
        }
        return isRetryableError(error);
      }
    }
  );

  return result;
}

export async function submitDesignerCreation(
  data: CompanyFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Rate limiting check
  checkRateLimitOrThrow(userId, 'designer_creation');
  recordSubmissionAttempt(userId);
  
  // Breadcrumb tracking
  breadcrumb.userAction('Start designer submission', 'submitDesignerCreation', { userId });
  
  // Validate required fields client-side
  assertValid(validateCompanyCreateFields({ ...data, company_type: 'designer' }));
  
  // Ban check with retry logic
  const { withRetry } = await import('./retryHelpers');
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'designer_creation').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }
  
  // Upload images
  let processedImages = data.images;
  if (data.images?.uploaded && data.images.uploaded.length > 0) {
    try {
      const uploadedImages = await uploadPendingImages(data.images.uploaded);
      processedImages = { ...data.images, uploaded: uploadedImages };
    } catch (error: unknown) {
      handleError(error, {
        action: 'Upload designer images',
      });
      throw new Error(`Failed to upload images: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  // Submit with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'designer',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'designer',
          action_type: 'create',
          item_data: {
            // ✅ FIXED: Don't use extractChangedFields for CREATE - include ALL data
            ...(() => {
              const { images, ...dataWithoutImages } = data;
              return dataWithoutImages;
            })(),
            company_type: 'designer',
            images: processedImages as unknown as Json
          },
          status: 'pending' as const,
          order_index: 0
        });

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying designer submission', { 
          attempt, 
          delay,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'designer',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('slug')) return false;
        }
        return isRetryableError(error);
      }
    }
  );

  return result;
}

export async function submitDesignerUpdate(
  companyId: string,
  data: CompanyFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Rate limiting check
  checkRateLimitOrThrow(userId, 'designer_update');
  recordSubmissionAttempt(userId);
  
  // Breadcrumb tracking
  breadcrumb.userAction('Start designer update', 'submitDesignerUpdate', { userId, companyId });
  
  // Ban check with retry logic
  const { withRetry } = await import('./retryHelpers');
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'designer_update').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }
  
  const { data: existingCompany, error: fetchError } = await supabase
    .from('companies')
    .select('*')
    .eq('id', companyId)
    .single();

  if (fetchError) throw new Error(`Failed to fetch designer: ${fetchError.message}`);
  if (!existingCompany) throw new Error('Designer not found');

  // CRITICAL: Block new photo uploads on edits
  if (data.images?.uploaded && data.images.uploaded.some(img => img.isLocal)) {
    throw new Error('Photo uploads are not allowed during edits. Please use the photo gallery to submit additional photos.');
  }

  let processedImages = data.images;

  // Submit with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'designer',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'designer',
          action_type: 'edit',
          item_data: {
            ...extractChangedFields(data, existingCompany as any),
            company_id: companyId,  // Always include for relational integrity
            company_type: 'designer',  // Always include for entity type discrimination
            images: processedImages as unknown as Json
          },
          original_data: JSON.parse(JSON.stringify(existingCompany)),
          status: 'pending' as const,
          order_index: 0
        });

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying designer update', { 
          attempt, 
          delay,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'designer_update',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('slug')) return false;
        }
        return isRetryableError(error);
      }
    }
  );

  return result;
}

export async function submitOperatorCreation(
  data: CompanyFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Rate limiting check
  checkRateLimitOrThrow(userId, 'operator_creation');
  recordSubmissionAttempt(userId);
  
  // Breadcrumb tracking
  breadcrumb.userAction('Start operator submission', 'submitOperatorCreation', { userId });
  
  // Validate required fields client-side
  assertValid(validateCompanyCreateFields({ ...data, company_type: 'operator' }));
  
  // Ban check with retry logic
  const { withRetry } = await import('./retryHelpers');
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'operator_creation').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }
  
  // Upload images
  let processedImages = data.images;
  if (data.images?.uploaded && data.images.uploaded.length > 0) {
    try {
      const uploadedImages = await uploadPendingImages(data.images.uploaded);
      processedImages = { ...data.images, uploaded: uploadedImages };
    } catch (error: unknown) {
      handleError(error, {
        action: 'Upload operator images',
      });
      throw new Error(`Failed to upload images: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  // Submit with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'operator',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'operator',
          action_type: 'create',
          item_data: {
            // ✅ FIXED: Don't use extractChangedFields for CREATE - include ALL data
            ...(() => {
              const { images, ...dataWithoutImages } = data;
              return dataWithoutImages;
            })(),
            company_type: 'operator',
            images: processedImages as unknown as Json
          },
          status: 'pending' as const,
          order_index: 0
        });

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        logger.warn('Retrying operator submission', { 
          attempt, 
          delay,
          error: error instanceof Error ? error.message : String(error)
        });
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { id: retryId, attempt, maxAttempts: 3, delay, type: 'operator' }
        }));
      },
      shouldRetry: (error) => {
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('suspended')) return false;
          if (message.includes('slug')) return false;
          if (message.includes('already exists')) return false;
          if (message.includes('duplicate')) return false;
          if (message.includes('permission')) return false;
          if (message.includes('forbidden')) return false;
          if (message.includes('unauthorized')) return false;
        }
        return isRetryableError(error);
      }
    }
  ).then((data) => {
    window.dispatchEvent(new CustomEvent('submission-retry-success', {
      detail: { id: retryId }
    }));
    return data;
  }).catch((error) => {
    const errorId = handleError(error, {
      action: 'Operator submission',
      metadata: { retriesExhausted: true },
    });
    
    window.dispatchEvent(new CustomEvent('submission-retry-failed', {
      detail: { id: retryId, errorId }
    }));
    
    throw error;
  });

  return result;
}

export async function submitOperatorUpdate(
  companyId: string,
  data: CompanyFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Rate limiting check
  checkRateLimitOrThrow(userId, 'operator_update');
  recordSubmissionAttempt(userId);
  
  // Breadcrumb tracking
  breadcrumb.userAction('Start operator update', 'submitOperatorUpdate', { userId, companyId });
  
  // Ban check with retry logic
  const { withRetry } = await import('./retryHelpers');
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'operator_update').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }
  
  const { data: existingCompany, error: fetchError } = await supabase
    .from('companies')
    .select('*')
    .eq('id', companyId)
    .single();

  if (fetchError) throw new Error(`Failed to fetch operator: ${fetchError.message}`);
  if (!existingCompany) throw new Error('Operator not found');

  // CRITICAL: Block new photo uploads on edits
  if (data.images?.uploaded && data.images.uploaded.some(img => img.isLocal)) {
    throw new Error('Photo uploads are not allowed during edits. Please use the photo gallery to submit additional photos.');
  }

  let processedImages = data.images;

  // Submit with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'operator',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'operator',
          action_type: 'edit',
          item_data: {
            ...extractChangedFields(data, existingCompany as any),
            company_id: companyId,  // Always include for relational integrity
            company_type: 'operator',  // Always include for entity type discrimination
            images: processedImages as unknown as Json
          },
          original_data: JSON.parse(JSON.stringify(existingCompany)),
          status: 'pending' as const,
          order_index: 0
        });

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying operator update', { 
          attempt, 
          delay,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'operator_update',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('slug')) return false;
        }
        return isRetryableError(error);
      }
    }
  );

  return result;
}

export async function submitPropertyOwnerCreation(
  data: CompanyFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Rate limiting check
  checkRateLimitOrThrow(userId, 'property_owner_creation');
  recordSubmissionAttempt(userId);
  
  // Breadcrumb tracking
  breadcrumb.userAction('Start property owner submission', 'submitPropertyOwnerCreation', { userId });
  
  // Validate required fields client-side
  assertValid(validateCompanyCreateFields({ ...data, company_type: 'property_owner' }));
  
  // Ban check with retry logic
  const { withRetry } = await import('./retryHelpers');
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'property_owner_creation').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }
  
  // Upload images
  let processedImages = data.images;
  if (data.images?.uploaded && data.images.uploaded.length > 0) {
    try {
      const uploadedImages = await uploadPendingImages(data.images.uploaded);
      processedImages = { ...data.images, uploaded: uploadedImages };
    } catch (error: unknown) {
      handleError(error, {
        action: 'Upload property owner images',
      });
      throw new Error(`Failed to upload images: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  // Submit with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'property_owner',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'property_owner',
          action_type: 'create',
          item_data: {
            // ✅ FIXED: Don't use extractChangedFields for CREATE - include ALL data
            ...(() => {
              const { images, ...dataWithoutImages } = data;
              return dataWithoutImages;
            })(),
            company_type: 'property_owner',
            images: processedImages as unknown as Json
          },
          status: 'pending' as const,
          order_index: 0
        });

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        logger.warn('Retrying property owner submission', { 
          attempt, 
          delay,
          error: error instanceof Error ? error.message : String(error)
        });
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { id: retryId, attempt, maxAttempts: 3, delay, type: 'property_owner' }
        }));
      },
      shouldRetry: (error) => {
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('suspended')) return false;
          if (message.includes('slug')) return false;
          if (message.includes('already exists')) return false;
          if (message.includes('duplicate')) return false;
          if (message.includes('permission')) return false;
          if (message.includes('forbidden')) return false;
          if (message.includes('unauthorized')) return false;
        }
        return isRetryableError(error);
      }
    }
  ).then((data) => {
    window.dispatchEvent(new CustomEvent('submission-retry-success', {
      detail: { id: retryId }
    }));
    return data;
  }).catch((error) => {
    const errorId = handleError(error, {
      action: 'Property owner submission',
      metadata: { retriesExhausted: true },
    });
    
    window.dispatchEvent(new CustomEvent('submission-retry-failed', {
      detail: { id: retryId, errorId }
    }));
    
    throw error;
  });

  return result;
}

export async function submitPropertyOwnerUpdate(
  companyId: string,
  data: CompanyFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // Rate limiting check
  checkRateLimitOrThrow(userId, 'property_owner_update');
  recordSubmissionAttempt(userId);
  
  // Breadcrumb tracking
  breadcrumb.userAction('Start property owner update', 'submitPropertyOwnerUpdate', { userId, companyId });
  
  // Ban check with retry logic
  const { withRetry } = await import('./retryHelpers');
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data: profile } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      return profile;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'property_owner_update').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }
  
  const { data: existingCompany, error: fetchError } = await supabase
    .from('companies')
    .select('*')
    .eq('id', companyId)
    .single();

  if (fetchError) throw new Error(`Failed to fetch property owner: ${fetchError.message}`);
  if (!existingCompany) throw new Error('Property owner not found');

  // CRITICAL: Block new photo uploads on edits
  if (data.images?.uploaded && data.images.uploaded.some(img => img.isLocal)) {
    throw new Error('Photo uploads are not allowed during edits. Please use the photo gallery to submit additional photos.');
  }

  let processedImages = data.images;

  // Submit with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const retryId = crypto.randomUUID();
  
  const result = await withRetry(
    async () => {
      const { data: submissionData, error: submissionError } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'property_owner',
          status: 'pending' as const
        })
        .select()
        .single();

      if (submissionError) throw submissionError;

      const { error: itemError } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'property_owner',
          action_type: 'edit',
          item_data: {
            ...extractChangedFields(data, existingCompany as any),
            company_id: companyId,  // Always include for relational integrity
            company_type: 'property_owner',  // Always include for entity type discrimination
            images: processedImages as unknown as Json
          },
          original_data: JSON.parse(JSON.stringify(existingCompany)),
          status: 'pending' as const,
          order_index: 0
        });

      if (itemError) throw itemError;

      return { submitted: true, submissionId: submissionData.id };
    },
    {
      maxAttempts: 3,
      baseDelay: 1000,
      onRetry: (attempt, error, delay) => {
        const isRateLimit = isRateLimitError(error);
        const retryAfter = isRateLimit ? extractRetryAfter(error) : null;
        
        logger.warn('Retrying property owner update', { 
          attempt, 
          delay,
          isRateLimit,
          retryAfter,
          error: error instanceof Error ? error.message : String(error)
        });
        window.dispatchEvent(new CustomEvent('submission-retry', {
          detail: { 
            id: retryId, 
            attempt, 
            maxAttempts: 3, 
            delay, 
            type: 'property_owner_update',
            isRateLimit,
            retryAfter
          }
        }));
      },
      shouldRetry: (error) => {
        if (error instanceof Error) {
          const message = error.message.toLowerCase();
          if (message.includes('required')) return false;
          if (message.includes('banned')) return false;
          if (message.includes('slug')) return false;
        }
        return isRetryableError(error);
      }
    }
  );

  return result;
}

/**
 * ⚠️ CRITICAL SECURITY PATTERN ⚠️
 * 
 * Submits a new timeline event for an entity through the moderation queue.
 * 
 * DO NOT write directly to entity_timeline_events:
 * ❌ await supabase.from('entity_timeline_events').insert(data)  // BYPASSES MODERATION!
 * ✅ await submitTimelineEvent(entityType, entityId, data, userId)  // CORRECT
 * 
 * Flow: User Submit → Moderation Queue → Approval → Database Write
 * 
 * @param entityType - Type of entity (park, ride, company)
 * @param entityId - ID of the entity
 * @param data - The timeline event form data
 * @param userId - The ID of the user submitting
 * @returns Object containing submitted boolean and submissionId
 */
export async function submitTimelineEvent(
  entityType: EntityType,
  entityId: string,
  data: TimelineEventFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // ✅ Phase 4: Validate user
  if (!userId) {
    throw new Error('User ID is required for timeline event submission');
  }

  // ✅ Phase 4: Rate limiting check
  checkRateLimitOrThrow(userId, 'timeline_event_creation');
  recordSubmissionAttempt(userId);

  // ✅ Phase 4: Validation
  if (!data.title?.trim()) {
    throw new Error('Timeline event title is required');
  }
  if (!data.event_date) {
    throw new Error('Timeline event date is required');
  }
  if (!data.event_type) {
    throw new Error('Timeline event type is required');
  }

  // ✅ Phase 4: Breadcrumb tracking
  breadcrumb.userAction('Start timeline event submission', 'submitTimelineEvent', {
    entityType,
    entityId,
    eventType: data.event_type,
    userId
  });

  // ✅ Phase 4: Ban check with retry
  breadcrumb.apiCall('profiles', 'SELECT');
  const { withRetry } = await import('./retryHelpers');
  
  const profile = await withRetry(
    async () => {
      const { data, error } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      
      if (error) throw error;
      return data;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'timeline_event_creation').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }

  // ✅ Phase 4: Create submission with retry logic
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const submissionData = await withRetry(
    async () => {
      const { data, error } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'timeline_event',
          status: 'pending' as const
        })
        .select('id')
        .single();

      if (error) throw error;
      if (!data) throw new Error('Failed to create timeline event submission');
      
      return data;
    },
    {
      onRetry: (attempt, error, delay) => {
        logger.warn('Retrying timeline event submission creation', { 
          attempt, 
          delay,
          userId,
          eventType: data.event_type 
        });
      }
    }
  );

  // ✅ Phase 4: Insert timeline_event_submission with retry
  breadcrumb.apiCall('timeline_event_submissions', 'INSERT');
  const timelineSubmission = await withRetry(
    async () => {
      const { data: insertedData, error } = await supabase
        .from('timeline_event_submissions')
        .insert({
          submission_id: submissionData.id,
          entity_type: entityType,
          entity_id: entityId,
          event_type: data.event_type,
          event_date: data.event_date.toISOString().split('T')[0],
          event_date_precision: data.event_date_precision,
          title: data.title,
          description: data.description,
          from_value: data.from_value,
          to_value: data.to_value,
          from_entity_id: data.from_entity_id,
          to_entity_id: data.to_entity_id,
          from_location_id: data.from_location_id,
          to_location_id: data.to_location_id,
          is_public: true,
        })
        .select('id')
        .single();

      if (error) throw error;
      if (!insertedData) throw new Error('Failed to submit timeline event for review');
      
      return insertedData;
    },
    {
      onRetry: (attempt, error, delay) => {
        logger.warn('Retrying timeline event data insertion', { 
          attempt, 
          delay,
          submissionId: submissionData.id 
        });
      }
    }
  );

  // ✅ Phase 4: Create submission_items with retry
  breadcrumb.apiCall('submission_items', 'INSERT');
  await withRetry(
    async () => {
      const { error } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'timeline_event',
          action_type: 'create',
          item_data: {
            entity_type: entityType,
            entity_id: entityId
          } as Json,
          status: 'pending' as const,
          order_index: 0,
          timeline_event_submission_id: timelineSubmission.id
        });

      if (error) throw error;
    },
    {
      onRetry: (attempt, error, delay) => {
        logger.warn('Retrying timeline event submission item creation', { 
          attempt, 
          delay,
          submissionId: submissionData.id 
        });
      }
    }
  );

  return {
    submitted: true,
    submissionId: submissionData.id,
  };
}

/**
 * ⚠️ CRITICAL SECURITY PATTERN ⚠️
 * 
 * Submits an update to an existing timeline event through the moderation queue.
 * 
 * @param eventId - ID of the existing timeline event
 * @param data - The updated timeline event data
 * @param userId - The ID of the user submitting the update
 * @returns Object containing submitted boolean and submissionId
 */
export async function submitTimelineEventUpdate(
  eventId: string,
  data: TimelineEventFormData,
  userId: string
): Promise<{ submitted: boolean; submissionId: string }> {
  // ✅ Phase 4: Validate user
  if (!userId) {
    throw new Error('User ID is required for timeline event update');
  }

  // ✅ Phase 4: Rate limiting check
  checkRateLimitOrThrow(userId, 'timeline_event_update');
  recordSubmissionAttempt(userId);

  // ✅ Phase 4: Validation
  if (!data.title?.trim()) {
    throw new Error('Timeline event title is required');
  }
  if (!data.event_date) {
    throw new Error('Timeline event date is required');
  }

  // ✅ Phase 4: Breadcrumb tracking
  breadcrumb.userAction('Start timeline event update', 'submitTimelineEventUpdate', {
    eventId,
    userId
  });

  // ✅ Phase 4: Ban check with retry
  const { withRetry } = await import('./retryHelpers');
  
  breadcrumb.apiCall('profiles', 'SELECT');
  const profile = await withRetry(
    async () => {
      const { data, error } = await supabase
        .from('profiles')
        .select('banned')
        .eq('user_id', userId)
        .single();
      
      if (error) throw error;
      return data;
    },
    { maxAttempts: 2 }
  );

  if (profile?.banned) {
    // Report ban evasion attempt
    reportBanEvasionAttempt(userId, 'timeline_event_update').catch(() => {
      // Non-blocking - don't fail if alert fails
    });
    throw new Error('Account suspended. Contact support for assistance.');
  }

  // Fetch original event with retry
  breadcrumb.apiCall('entity_timeline_events', 'SELECT');
  const originalEvent = await withRetry(
    async () => {
      const { data, error } = await supabase
        .from('entity_timeline_events')
        .select('*')
        .eq('id', eventId)
        .single();

      if (error) throw error;
      if (!data) throw new Error('Failed to fetch original timeline event');
      
      return data;
    },
    { maxAttempts: 2 }
  );

  // Extract only changed fields from form data
  const changedFields = extractChangedFields(data, originalEvent as Partial<Record<string, unknown>>);

  // ✅ Phase 4: Create submission with retry
  breadcrumb.apiCall('content_submissions', 'INSERT');
  const submissionData = await withRetry(
    async () => {
      const { data, error } = await supabase
        .from('content_submissions')
        .insert({
          user_id: userId,
          submission_type: 'timeline_event',
          status: 'pending' as const
        })
        .select('id')
        .single();

      if (error) throw error;
      if (!data) throw new Error('Failed to create timeline event update submission');
      
      return data;
    },
    {
      onRetry: (attempt, error, delay) => {
        logger.warn('Retrying timeline event update submission', { 
          attempt, 
          delay,
          eventId,
          userId 
        });
      }
    }
  );

  // ✅ Phase 4: Insert timeline_event_submission with retry
  breadcrumb.apiCall('timeline_event_submissions', 'INSERT');
  const timelineSubmission = await withRetry(
    async () => {
      const { data: insertedData, error } = await supabase
        .from('timeline_event_submissions')
        .insert({
          submission_id: submissionData.id,
          entity_type: originalEvent.entity_type,
          entity_id: originalEvent.entity_id,
          event_type: changedFields.event_type !== undefined ? changedFields.event_type : originalEvent.event_type,
          event_date: changedFields.event_date !== undefined ? (typeof changedFields.event_date === 'string' ? changedFields.event_date : changedFields.event_date.toISOString().split('T')[0]) : originalEvent.event_date,
          event_date_precision: (changedFields.event_date_precision !== undefined ? changedFields.event_date_precision : originalEvent.event_date_precision) || 'exact',
          title: changedFields.title !== undefined ? changedFields.title : originalEvent.title,
          description: changedFields.description !== undefined ? changedFields.description : originalEvent.description,
          from_value: changedFields.from_value !== undefined ? changedFields.from_value : originalEvent.from_value,
          to_value: changedFields.to_value !== undefined ? changedFields.to_value : originalEvent.to_value,
          from_entity_id: changedFields.from_entity_id !== undefined ? changedFields.from_entity_id : originalEvent.from_entity_id,
          to_entity_id: changedFields.to_entity_id !== undefined ? changedFields.to_entity_id : originalEvent.to_entity_id,
          from_location_id: changedFields.from_location_id !== undefined ? changedFields.from_location_id : originalEvent.from_location_id,
          to_location_id: changedFields.to_location_id !== undefined ? changedFields.to_location_id : originalEvent.to_location_id,
          is_public: true,
        })
        .select('id')
        .single();

      if (error) throw error;
      if (!insertedData) throw new Error('Failed to submit timeline event update');
      
      return insertedData;
    },
    {
      onRetry: (attempt, error, delay) => {
        logger.warn('Retrying timeline event update data insertion', { 
          attempt, 
          delay,
          eventId,
          submissionId: submissionData.id 
        });
      }
    }
  );

  // ✅ Phase 4: Create submission_items with retry
  breadcrumb.apiCall('submission_items', 'INSERT');
  await withRetry(
    async () => {
      const { error } = await supabase
        .from('submission_items')
        .insert({
          submission_id: submissionData.id,
          item_type: 'timeline_event',
          action_type: 'edit',
          item_data: {
            event_id: eventId,
            entity_type: originalEvent.entity_type,
            entity_id: originalEvent.entity_id
          } as Json,
          original_data: JSON.parse(JSON.stringify(originalEvent)),
          status: 'pending' as const,
          order_index: 0,
          timeline_event_submission_id: timelineSubmission.id
        });

      if (error) throw error;
    },
    {
      onRetry: (attempt, error, delay) => {
        logger.warn('Retrying timeline event update item creation', { 
          attempt, 
          delay,
          eventId,
          submissionId: submissionData.id 
        });
      }
    }
  );

  breadcrumb.userAction('Timeline event update submitted', 'submitTimelineEventUpdate', {
    eventId,
    submissionId: submissionData.id
  });

  return {
    submitted: true,
    submissionId: submissionData.id,
  };
}


export async function deleteTimelineEvent(
  eventId: string,
  userId: string
): Promise<void> {
  // First verify the event exists and user has permission
  const { data: event, error: fetchError } = await supabase
    .from('entity_timeline_events')
    .select('created_by, approved_by')
    .eq('id', eventId)
    .single();

  if (fetchError) {
    handleError(fetchError, {
      action: 'Delete timeline event',
      metadata: { eventId },
    });
    throw new Error('Timeline event not found');
  }

  if (!event) {
    throw new Error('Timeline event not found');
  }

  // Only allow deletion of own unapproved events
  if (event.created_by !== userId) {
    throw new Error('You can only delete your own timeline events');
  }

  if (event.approved_by !== null) {
    throw new Error('Cannot delete approved timeline events');
  }

  // Delete the event
  const { error: deleteError } = await supabase
    .from('entity_timeline_events')
    .delete()
    .eq('id', eventId);

  if (deleteError) {
    handleError(deleteError, {
      action: 'Delete timeline event',
      metadata: { eventId },
    });
    throw new Error('Failed to delete timeline event');
  }

  logger.info('Timeline event deleted', {
    action: 'delete_timeline_event',
    eventId,
    userId
  });
}
