// Force redeployment: v100 - JWT verification disabled in config
import { serve } from "https://deno.land/std@0.190.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4";
import { validateEntityData, validateEntityDataStrict } from "./validation.ts";
import { createErrorResponse } from "../_shared/errorSanitizer.ts";
import { edgeLogger, startRequest, endRequest } from "../_shared/logger.ts";

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};

interface ApprovalRequest {
  itemIds: string[];
  submissionId: string;
}

// Allowed database fields for each entity type
const RIDE_FIELDS = [
  'name', 'slug', 'description', 'park_id', 'ride_model_id', 
  'manufacturer_id', 'designer_id', 'category', 'status',
  'opening_date', 'closing_date', 'height_requirement', 'age_requirement',
  'capacity_per_hour', 'duration_seconds', 'max_speed_kmh', 
  'max_height_meters', 'length_meters', 'inversions',
  'ride_sub_type', 'coaster_type', 'seating_type', 'intensity_level',
  'track_material', 'drop_height_meters', 'max_g_force', 'image_url',
  'banner_image_url', 'banner_image_id', 'card_image_url', 'card_image_id'
];

const PARK_FIELDS = [
  'name', 'slug', 'description', 'park_type', 'status',
  'opening_date', 'closing_date', 'location_id', 'operator_id',
  'property_owner_id', 'website_url', 'phone', 'email',
  'banner_image_url', 'banner_image_id', 'card_image_url', 'card_image_id'
];

const COMPANY_FIELDS = [
  'name', 'slug', 'description', 'company_type', 'person_type',
  'founded_year', 'headquarters_location', 'website_url', 'logo_url',
  'banner_image_url', 'banner_image_id', 'card_image_url', 'card_image_id'
];

const RIDE_MODEL_FIELDS = [
  'name', 'slug', 'description', 'category', 'ride_type',
  'manufacturer_id', 'banner_image_url', 'banner_image_id',
  'card_image_url', 'card_image_id'
];

serve(async (req) => {
  const tracking = startRequest(); // Start request tracking
  
  if (req.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  try {
    edgeLogger.info('Processing selective approval request', { 
      requestId: tracking.requestId,
      traceId: tracking.traceId
    });
    
    // Verify authentication first with a client that respects RLS
    const authHeader = req.headers.get('Authorization');
    if (!authHeader) {
      const duration = endRequest(tracking);
      edgeLogger.warn('Authentication missing', { requestId: tracking.requestId, duration });
      return new Response(
        JSON.stringify({ error: 'Authentication required. Please log in.', requestId: tracking.requestId }),
        { 
          status: 401, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }

    // Create Supabase client with user's auth token to verify authentication
    const supabaseUrl = Deno.env.get('SUPABASE_URL') ?? '';
    const supabaseAnonKey = Deno.env.get('SUPABASE_ANON_KEY') ?? '';
    const supabaseAuth = createClient(supabaseUrl, supabaseAnonKey, {
      global: { headers: { Authorization: authHeader } }
    });

    // Verify JWT and get authenticated user
    const { data: { user }, error: authError } = await supabaseAuth.auth.getUser();
    
    edgeLogger.info('User auth check', { action: 'approval_auth', hasUser: !!user, userId: user?.id });

    if (authError || !user) {
      edgeLogger.error('Auth verification failed', { 
        action: 'approval_auth',
        error: authError?.message,
        requestId: tracking.requestId
      });
      const duration = endRequest(tracking);
      return new Response(
        JSON.stringify({ 
          error: 'Invalid authentication token.',
          details: authError?.message || 'No user found',
          code: authError?.code,
          requestId: tracking.requestId
        }),
        { 
          status: 401, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }
    
    edgeLogger.info('Authentication successful', { action: 'approval_auth_success', userId: user.id });
    
    // Check if user is banned
    const { data: profile, error: profileError } = await supabaseAuth
      .from('profiles')
      .select('banned')
      .eq('user_id', user.id)
      .single();

    if (profileError || !profile) {
      edgeLogger.error('Profile check failed', { 
        action: 'approval_profile_check',
        error: profileError?.message,
        requestId: tracking.requestId
      });
      const duration = endRequest(tracking);
      return new Response(
        JSON.stringify({ 
          error: 'Unable to verify user profile',
          requestId: tracking.requestId
        }),
        { 
          status: 403, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }

    if (profile.banned) {
      edgeLogger.warn('Banned user attempted approval', { 
        action: 'approval_banned_user',
        userId: user.id,
        requestId: tracking.requestId
      });
      const duration = endRequest(tracking);
      return new Response(
        JSON.stringify({ 
          error: 'Account suspended',
          message: 'Your account has been suspended. Contact support for assistance.',
          requestId: tracking.requestId
        }),
        { 
          status: 403, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }
    
    // SECURITY NOTE: Service role key used later in this function
    // Reason: Need to bypass RLS to write approved changes to entity tables
    // (parks, rides, companies, ride_models) which have RLS policies
    // Security measures: User auth verified above, moderator role checked via RPC

    const authenticatedUserId = user.id;

    // Create service role client for privileged operations (including role check)
    const supabase = createClient(
      Deno.env.get('SUPABASE_URL') ?? '',
      Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
    );

    // Check if user has moderator permissions using service role to bypass RLS
    const { data: roles, error: rolesError } = await supabase
      .from('user_roles')
      .select('role')
      .eq('user_id', authenticatedUserId);

    edgeLogger.info('Role check query result', { action: 'approval_role_check', userId: authenticatedUserId, rolesCount: roles?.length });

    if (rolesError) {
      edgeLogger.error('Role check failed', { action: 'approval_role_check', error: rolesError.message, requestId: tracking.requestId });
      const duration = endRequest(tracking);
      return new Response(
        JSON.stringify({ error: 'Failed to verify user permissions.', requestId: tracking.requestId }),
        { 
          status: 403, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }

    const userRoles = roles?.map(r => r.role) || [];
    const isModerator = userRoles.includes('moderator') || 
                        userRoles.includes('admin') || 
                        userRoles.includes('superuser');

    edgeLogger.info('Role check result', { action: 'approval_role_result', userId: authenticatedUserId, isModerator });

    if (!isModerator) {
      edgeLogger.error('Insufficient permissions', { action: 'approval_role_insufficient', userId: authenticatedUserId, requestId: tracking.requestId });
      const duration = endRequest(tracking);
      return new Response(
        JSON.stringify({ error: 'Insufficient permissions. Moderator role required.', requestId: tracking.requestId }),
        { 
          status: 403, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }

    edgeLogger.info('User is moderator', { action: 'approval_role_verified', userId: authenticatedUserId });

    // Phase 2: AAL2 Enforcement - Check if user has MFA enrolled and requires AAL2
    // Parse JWT directly from Authorization header to get AAL level
    const jwt = authHeader.replace('Bearer ', '');
    const payload = JSON.parse(atob(jwt.split('.')[1]));
    const aal = payload.aal || 'aal1';

    edgeLogger.info('Session AAL level', { action: 'approval_aal_check', userId: authenticatedUserId, aal });

    // Check if user has MFA enrolled
    const { data: factorsData } = await supabaseAuth.auth.mfa.listFactors();
    const hasMFA = factorsData?.totp?.some(f => f.status === 'verified') || false;

    edgeLogger.info('MFA status', { action: 'approval_mfa_check', userId: authenticatedUserId, hasMFA });

    // Enforce AAL2 if MFA is enrolled
    if (hasMFA && aal !== 'aal2') {
      edgeLogger.error('AAL2 required but session is at AAL1', { action: 'approval_aal_violation', userId: authenticatedUserId, requestId: tracking.requestId });
      const duration = endRequest(tracking);
      return new Response(
        JSON.stringify({ 
          error: 'MFA verification required',
          code: 'AAL2_REQUIRED',
          message: 'Your role requires two-factor authentication. Please verify your identity to continue.',
          requestId: tracking.requestId
        }),
        { 
          status: 403, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }

    edgeLogger.info('AAL2 check passed', { action: 'approval_aal_pass', userId: authenticatedUserId, hasMFA, aal });

    const { itemIds, submissionId }: ApprovalRequest = await req.json();

    // UUID validation regex
    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

    // Validate itemIds
    if (!itemIds || !Array.isArray(itemIds)) {
      return new Response(
        JSON.stringify({ error: 'itemIds is required and must be an array', requestId: tracking.requestId }),
        { 
          status: 400, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }

    if (itemIds.length === 0) {
      return new Response(
        JSON.stringify({ error: 'itemIds must be a non-empty array', requestId: tracking.requestId }),
        { 
          status: 400, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }

    // Validate submissionId
    if (!submissionId || typeof submissionId !== 'string' || submissionId.trim() === '') {
      return new Response(
        JSON.stringify({ error: 'submissionId is required and must be a non-empty string', requestId: tracking.requestId }),
        { 
          status: 400, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }

    if (!uuidRegex.test(submissionId)) {
      return new Response(
        JSON.stringify({ error: 'submissionId must be a valid UUID format', requestId: tracking.requestId }),
        { 
          status: 400, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }

    edgeLogger.info('Processing selective approval', { action: 'approval_start', itemCount: itemIds.length, userId: authenticatedUserId, submissionId });

    // Fetch all items with relational data for the submission
    const { data: items, error: fetchError } = await supabase
      .from('submission_items')
      .select(`
        *,
        park_submission:park_submissions!item_data_id(*),
        ride_submission:ride_submissions!item_data_id(*),
        company_submission:company_submissions!item_data_id(*),
        ride_model_submission:ride_model_submissions!item_data_id(*),
        photo_submission:photo_submissions!item_data_id(
          *,
          photo_items:photo_submission_items(*)
        )
      `)
      .in('id', itemIds);

    if (fetchError) {
      throw new Error(`Failed to fetch items: ${fetchError.message}`);
    }

    // Get the submitter's user_id from the submission
    const { data: submission, error: submissionError } = await supabase
      .from('content_submissions')
      .select('user_id')
      .eq('id', submissionId)
      .single();

    if (submissionError || !submission) {
      throw new Error(`Failed to fetch submission: ${submissionError?.message}`);
    }

    const submitterId = submission.user_id;

    // Topologically sort items by dependencies
    let sortedItems;
    try {
      sortedItems = topologicalSort(items);
    } catch (sortError: unknown) {
      const errorMessage = sortError instanceof Error ? sortError.message : 'Failed to sort items';
      edgeLogger.error('Topological sort failed', {
        action: 'approval_sort_fail',
        submissionId,
        itemCount: items.length,
        userId: authenticatedUserId,
        error: errorMessage,
        requestId: tracking.requestId
      });
      return new Response(
        JSON.stringify({ 
          error: 'Invalid submission structure',
          message: errorMessage,
          details: 'The submission contains circular dependencies or missing required items',
          requestId: tracking.requestId
        }),
        { 
          status: 400, 
          headers: { 
            ...corsHeaders, 
            'Content-Type': 'application/json',
            'X-Request-ID': tracking.requestId 
          } 
        }
      );
    }
    
    const dependencyMap = new Map<string, string>();
    const approvalResults: Array<{
  itemId: string;
  entityId?: string | null;
  itemType: string;
  success: boolean;
  error?: string;
  isDependencyFailure?: boolean;
}> = [];

    // Process items in order
    for (const item of sortedItems) {
      try {
        edgeLogger.info('Processing item', { action: 'approval_process_item', itemId: item.id, itemType: item.item_type });
        
        // Extract data from relational tables based on item_type
        let itemData: any;
        switch (item.item_type) {
          case 'park':
            itemData = (item as any).park_submission;
            break;
          case 'ride':
            itemData = (item as any).ride_submission;
            break;
          case 'manufacturer':
          case 'operator':
          case 'property_owner':
          case 'designer':
            itemData = (item as any).company_submission;
            break;
          case 'ride_model':
            itemData = (item as any).ride_model_submission;
            break;
          case 'photo':
            // Combine photo_submission with its photo_items array
            itemData = {
              ...(item as any).photo_submission,
              photos: (item as any).photo_submission?.photo_items || []
            };
            break;
          default:
            // For timeline/other items not yet migrated, fall back to item_data (JSONB)
            itemData = item.item_data;
        }
        
        if (!itemData && item.item_data) {
          // Fallback to item_data if relational data not found (for backwards compatibility)
          itemData = item.item_data;
        }
        
        // Validate entity data with strict validation, passing original_data for edits
        const validation = validateEntityDataStrict(item.item_type, itemData, item.original_data);
        
        if (validation.blockingErrors.length > 0) {
          edgeLogger.error('Blocking validation errors', {
            action: 'approval_validation_fail',
            itemId: item.id,
            errors: validation.blockingErrors,
            requestId: tracking.requestId
          });
          
          // Fail the entire batch if ANY item has blocking errors
          return new Response(JSON.stringify({
            success: false,
            message: 'Validation failed: Items have blocking errors that must be fixed',
            errors: validation.blockingErrors,
            failedItemId: item.id,
            failedItemType: item.item_type,
            requestId: tracking.requestId
          }), { 
            status: 400,
            headers: { 
              ...corsHeaders, 
              'Content-Type': 'application/json',
              'X-Request-ID': tracking.requestId 
            }
          });
        }
        
        if (validation.warnings.length > 0) {
          edgeLogger.warn('Validation warnings', {
            action: 'approval_validation_warning',
            itemId: item.id,
            warnings: validation.warnings
          });
          // Continue processing - warnings don't block approval
        }
        
        // Set user context for versioning trigger
        // This allows create_relational_version() trigger to capture the submitter
        const { error: setUserIdError } = await supabase.rpc('set_config_value', {
          setting_name: 'app.current_user_id',
          setting_value: submitterId,
          is_local: false
        });
        
        if (setUserIdError) {
          console.error('[APPROVAL] Failed to set user context:', { error: setUserIdError.message });
        }
        
        // Set submission ID for version tracking
        const { error: setSubmissionIdError } = await supabase.rpc('set_config_value', {
          setting_name: 'app.submission_id',
          setting_value: submissionId,
          is_local: false
        });
        
        if (setSubmissionIdError) {
          console.error('[APPROVAL] Failed to set submission context:', { error: setSubmissionIdError.message });
        }
        
        // Resolve dependencies in item data
        const resolvedData = resolveDependencies(itemData, dependencyMap, sortedItems);
        
        // Add submitter ID to the data for photo tracking
        resolvedData._submitter_id = submitterId;
        
        let entityId: string | null = null;

        // Create entity based on type
        switch (item.item_type) {
          case 'park':
            entityId = await createPark(supabase, resolvedData);
            break;
          case 'ride':
            entityId = await createRide(supabase, resolvedData);
            break;
          case 'manufacturer':
          case 'operator':
          case 'property_owner':
          case 'designer':
            entityId = await createCompany(supabase, resolvedData, item.item_type);
            break;
          case 'ride_model':
            entityId = await createRideModel(supabase, resolvedData);
            break;
          case 'photo':
            await approvePhotos(supabase, resolvedData, item.id);
            entityId = item.id; // Use item ID as entity ID for photos
            break;
          case 'photo_edit':
            await editPhoto(supabase, resolvedData);
            entityId = resolvedData.photo_id;
            break;
          case 'photo_delete':
            await deletePhoto(supabase, resolvedData);
            entityId = resolvedData.photo_id;
            break;
          case 'milestone':
          case 'timeline_event': // Unified timeline event handling
            entityId = await createTimelineEvent(supabase, resolvedData, submitterId, authenticatedUserId, submissionId);
            break;
          default:
            throw new Error(`Unknown item type: ${item.item_type}`);
        }

        if (entityId) {
          dependencyMap.set(item.id, entityId);
        }

        // Store result for batch update later
        approvalResults.push({
          itemId: item.id,
          entityId,
          itemType: item.item_type,
          success: true
        });

        edgeLogger.info('Item approval success', { action: 'approval_item_success', itemId: item.id, entityId, itemType: item.item_type });
      } catch (error: unknown) {
        const errorMessage = error instanceof Error ? error.message : 'Unknown error';
        edgeLogger.error('Item approval failed', {
          action: 'approval_item_fail',
          itemId: item.id,
          itemType: item.item_type,
          userId: authenticatedUserId,
          submissionId,
          error: errorMessage
        });
        
        const isDependencyError = error instanceof Error && (
          error.message.includes('Missing dependency') ||
          error.message.includes('depends on') ||
          error.message.includes('Circular dependency')
        );
        
        approvalResults.push({
          itemId: item.id,
          itemType: item.item_type,
          success: false,
          error: errorMessage,
          isDependencyFailure: isDependencyError
        });
      }
    }

    // Batch update all approved items
    const approvedItemIds = approvalResults.filter(r => r.success).map(r => r.itemId);
    if (approvedItemIds.length > 0) {
      const approvedUpdates = approvalResults
        .filter(r => r.success)
        .map(r => ({
          id: r.itemId,
          status: 'approved',
          approved_entity_id: r.entityId,
          updated_at: new Date().toISOString()
        }));

      for (const update of approvedUpdates) {
        const { error: batchApproveError } = await supabase
          .from('submission_items')
          .update({
            status: update.status,
            approved_entity_id: update.approved_entity_id,
            updated_at: update.updated_at
          })
          .eq('id', update.id);
        
        if (batchApproveError) {
          edgeLogger.error('Failed to approve item', { 
            action: 'approval_batch_approve',
            itemId: update.id,
            error: batchApproveError.message
          });
        }
      }
    }

    // Batch update all rejected items
    const rejectedItemIds = approvalResults.filter(r => !r.success).map(r => r.itemId);
    if (rejectedItemIds.length > 0) {
      const rejectedUpdates = approvalResults
        .filter(r => !r.success)
        .map(r => ({
          id: r.itemId,
          status: 'rejected',
          rejection_reason: r.error || 'Unknown error',
          updated_at: new Date().toISOString()
        }));

      for (const update of rejectedUpdates) {
        const { error: batchRejectError } = await supabase
          .from('submission_items')
          .update({
            status: update.status,
            rejection_reason: update.rejection_reason,
            updated_at: update.updated_at
          })
          .eq('id', update.id);
        
        if (batchRejectError) {
          edgeLogger.error('Failed to reject item', { 
            action: 'approval_batch_reject',
            itemId: update.id,
            error: batchRejectError.message
          });
        }
      }
    }

    // Check if any failures were dependency-related
    const hasDependencyFailure = approvalResults.some(r => 
      !r.success && r.isDependencyFailure
    );

    const allApproved = approvalResults.every(r => r.success);
    const someApproved = approvalResults.some(r => r.success);
    const allFailed = approvalResults.every(r => !r.success);

    // Determine final status:
    // - If dependency validation failed: keep pending for escalation
    // - If all approved: approved
    // - If some approved: partially_approved
    // - If all failed but no dependency issues: rejected (can retry)
    const finalStatus = hasDependencyFailure && !someApproved
      ? 'pending'  // Keep pending for escalation only
      : allApproved
        ? 'approved'
        : allFailed
          ? 'rejected'  // Total failure, allow retry
          : 'partially_approved';  // Mixed results

    const reviewerNotes = hasDependencyFailure && !someApproved
      ? 'Submission has unresolved dependencies. Escalation required.'
      : undefined;

    const { error: updateError } = await supabase
      .from('content_submissions')
      .update({
        status: finalStatus,
        reviewer_id: authenticatedUserId,
        reviewed_at: new Date().toISOString(),
        reviewer_notes: reviewerNotes,
        escalated: hasDependencyFailure && !someApproved ? true : undefined
      })
      .eq('id', submissionId);

    if (updateError) {
      console.error('[APPROVAL] Failed to update submission status:', { error: updateError.message });
    }

    // Log audit trail for submission action
    try {
      const approvedCount = approvalResults.filter(r => r.success).length;
      const rejectedCount = approvalResults.filter(r => !r.success).length;
      
      await supabaseClient.rpc('log_admin_action', {
        _admin_user_id: authenticatedUserId,
        _target_user_id: submission.user_id,
        _action: finalStatus === 'approved' 
          ? 'submission_approved' 
          : finalStatus === 'partially_approved'
            ? 'submission_partially_approved'
            : 'submission_rejected',
        _details: {
          submission_id: submissionId,
          submission_type: submission.submission_type,
          items_approved: approvedCount,
          items_rejected: rejectedCount,
          total_items: approvalResults.length,
          final_status: finalStatus,
          has_dependency_failure: hasDependencyFailure,
          reviewer_notes: reviewerNotes
        }
      });
    } catch (auditError) {
      // Log but don't fail the operation
      console.error('[AUDIT] Failed to log admin action:', auditError);
    }

    const duration = endRequest(tracking);

    return new Response(
      JSON.stringify({
        success: true,
        results: approvalResults,
        submissionStatus: finalStatus,
        requestId: tracking.requestId
      }),
      { 
        headers: { 
          ...corsHeaders, 
          'Content-Type': 'application/json',
          'X-Request-ID': tracking.requestId 
        } 
      }
    );
  } catch (error: unknown) {
    const duration = endRequest(tracking);
    const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
    console.error('[APPROVAL ERROR] Process failed:', {
      error: errorMessage,
      userId: authenticatedUserId,
      timestamp: new Date().toISOString(),
      requestId: tracking.requestId,
      duration
    });
    return createErrorResponse(
      error,
      500,
      corsHeaders,
      'process-selective-approval'
    );
  }
});

// Helper functions
function topologicalSort(items: any[]): any[] {
  const sorted: any[] = [];
  const visited = new Set<string>();
  const visiting = new Set<string>();

  const visit = (item: any) => {
    if (visited.has(item.id)) return;
    if (visiting.has(item.id)) {
      throw new Error(
        `Circular dependency detected: item ${item.id} (${item.item_type}) ` +
        `creates a dependency loop. This submission requires escalation.`
      );
    }

    visiting.add(item.id);

    if (item.depends_on) {
      const parent = items.find(i => i.id === item.depends_on);
      if (!parent) {
        throw new Error(
          `Missing dependency: item ${item.id} (${item.item_type}) ` +
          `depends on ${item.depends_on} which is not in this submission or has not been approved. ` +
          `This submission requires escalation.`
        );
      }
      visit(parent);
    }

    visiting.delete(item.id);
    visited.add(item.id);
    sorted.push(item);
  };

  items.forEach(item => visit(item));
  return sorted;
}

function resolveDependencies(data: any, dependencyMap: Map<string, string>, sortedItems: any[]): any {
  if (typeof data !== 'object' || data === null) {
    return data;
  }

  if (Array.isArray(data)) {
    return data.map(item => resolveDependencies(item, dependencyMap, sortedItems));
  }

  const resolved: any = { ...data };

  // Phase 1: Resolve temporary index references FIRST
  // These reference items by their position in the sorted items array
  
  if (resolved._temp_manufacturer_ref !== undefined) {
    const refIndex = resolved._temp_manufacturer_ref;
    if (refIndex >= 0 && refIndex < sortedItems.length) {
      const refItemId = sortedItems[refIndex].id;
      if (dependencyMap.has(refItemId)) {
        resolved.manufacturer_id = dependencyMap.get(refItemId);
        edgeLogger.info('Resolved temp manufacturer ref', {
          action: 'dependency_resolve_temp_ref',
          refIndex,
          refItemId,
          resolvedId: resolved.manufacturer_id
        });
      }
    }
    delete resolved._temp_manufacturer_ref;
  }

  // Resolve temporary references using sortedItems array
  if (resolved._temp_park_ref !== undefined) {
    const refIndex = resolved._temp_park_ref;
    if (refIndex >= 0 && refIndex < sortedItems.length) {
      const refItemId = sortedItems[refIndex].id;
      if (dependencyMap.has(refItemId)) {
        resolved.park_id = dependencyMap.get(refItemId);
        edgeLogger.info('Resolved temp park ref', {
          action: 'dependency_resolve_temp_ref',
          refIndex,
          refItemId,
          resolvedId: resolved.park_id
        });
      }
    }
    delete resolved._temp_park_ref;
  }

  if (resolved._temp_manufacturer_ref !== undefined) {
    const refIndex = resolved._temp_manufacturer_ref;
    if (refIndex >= 0 && refIndex < sortedItems.length) {
      const refItemId = sortedItems[refIndex].id;
      if (dependencyMap.has(refItemId)) {
        resolved.manufacturer_id = dependencyMap.get(refItemId);
        edgeLogger.info('Resolved temp manufacturer ref', {
          action: 'dependency_resolve_temp_ref',
          refIndex,
          refItemId,
          resolvedId: resolved.manufacturer_id
        });
      }
    }
    delete resolved._temp_manufacturer_ref;
  }

  if (resolved._temp_operator_ref !== undefined) {
    const refIndex = resolved._temp_operator_ref;
    if (refIndex >= 0 && refIndex < sortedItems.length) {
      const refItemId = sortedItems[refIndex].id;
      if (dependencyMap.has(refItemId)) {
        resolved.operator_id = dependencyMap.get(refItemId);
        edgeLogger.info('Resolved temp operator ref', {
          action: 'dependency_resolve_temp_ref',
          refIndex,
          refItemId,
          resolvedId: resolved.operator_id
        });
      }
    }
    delete resolved._temp_operator_ref;
  }

  if (resolved._temp_property_owner_ref !== undefined) {
    const refIndex = resolved._temp_property_owner_ref;
    if (refIndex >= 0 && refIndex < sortedItems.length) {
      const refItemId = sortedItems[refIndex].id;
      if (dependencyMap.has(refItemId)) {
        resolved.property_owner_id = dependencyMap.get(refItemId);
        edgeLogger.info('Resolved temp property owner ref', {
          action: 'dependency_resolve_temp_ref',
          refIndex,
          refItemId,
          resolvedId: resolved.property_owner_id
        });
      }
    }
    delete resolved._temp_property_owner_ref;
  }

  if (resolved._temp_ride_model_ref !== undefined) {
    const refIndex = resolved._temp_ride_model_ref;
    if (refIndex >= 0 && refIndex < sortedItems.length) {
      const refItemId = sortedItems[refIndex].id;
      if (dependencyMap.has(refItemId)) {
        resolved.ride_model_id = dependencyMap.get(refItemId);
        edgeLogger.info('Resolved temp ride model ref', {
          action: 'dependency_resolve_temp_ref',
          refIndex,
          refItemId,
          resolvedId: resolved.ride_model_id
        });
      }
    }
    delete resolved._temp_ride_model_ref;
  }

  if (resolved._temp_designer_ref !== undefined) {
    const refIndex = resolved._temp_designer_ref;
    if (refIndex >= 0 && refIndex < sortedItems.length) {
      const refItemId = sortedItems[refIndex].id;
      if (dependencyMap.has(refItemId)) {
        resolved.designer_id = dependencyMap.get(refItemId);
        edgeLogger.info('Resolved temp designer ref', {
          action: 'dependency_resolve_temp_ref',
          refIndex,
          refItemId,
          resolvedId: resolved.designer_id
        });
      }
    }
    delete resolved._temp_designer_ref;
  }

  // Phase 2: Resolve direct foreign key references
  // These are submission_item IDs that reference other items in the same submission
  const foreignKeys = [
    'park_id',
    'manufacturer_id',
    'designer_id',
    'operator_id',
    'property_owner_id',
    'ride_model_id'
  ];

  for (const key of foreignKeys) {
    if (resolved[key] && typeof resolved[key] === 'string' && dependencyMap.has(resolved[key])) {
      const oldValue = resolved[key];
      resolved[key] = dependencyMap.get(resolved[key]);
      edgeLogger.info('Resolved direct foreign key', {
        action: 'dependency_resolve_fk',
        key,
        oldValue,
        newValue: resolved[key]
      });
    }
  }

  // Phase 3: Recursively resolve nested objects
  for (const [key, value] of Object.entries(resolved)) {
    if (typeof value === 'object' && value !== null && !foreignKeys.includes(key)) {
      resolved[key] = resolveDependencies(value, dependencyMap, sortedItems);
    }
  }

  return resolved;
}

function sanitizeDateFields(data: any): any {
  const dateFields = ['opening_date', 'closing_date', 'date_changed', 'date_taken', 'visit_date'];
  const sanitized = { ...data };
  
  for (const field of dateFields) {
    if (field in sanitized && sanitized[field] === '') {
      sanitized[field] = null;
    }
  }
  
  return sanitized;
}

function filterDatabaseFields(data: any, allowedFields: string[]): any {
  const filtered: any = {};
  for (const field of allowedFields) {
    if (field in data && data[field] !== undefined) {
      filtered[field] = data[field];
    }
  }
  return filtered;
}

function normalizeStatusValue(data: any): any {
  if (data.status) {
    // Map display values to database values
    const statusMap: Record<string, string> = {
      'Operating': 'operating',
      'Seasonal': 'operating',
      'Closed Temporarily': 'maintenance',
      'Closed Permanently': 'closed',
      'Under Construction': 'under_construction',
      'Planned': 'under_construction',
      'SBNO': 'sbno',
      // Also handle already-lowercase values
      'operating': 'operating',
      'closed': 'closed',
      'under_construction': 'under_construction',
      'maintenance': 'maintenance',
      'sbno': 'sbno'
    };
    
    data.status = statusMap[data.status] || 'operating';
  }
  return data;
}

async function createPark(supabase: any, data: any): Promise<string> {
  const submitterId = data._submitter_id;
  let uploadedPhotos: any[] = [];
  
  // Transform images object if present
  if (data.images) {
    const { uploaded, banner_assignment, card_assignment } = data.images;
    
    if (uploaded && Array.isArray(uploaded)) {
      // Store uploaded photos for later insertion into photos table
      uploadedPhotos = uploaded;
      
      // Assign banner image
      if (banner_assignment !== undefined && uploaded[banner_assignment]) {
        data.banner_image_id = uploaded[banner_assignment].cloudflare_id;
        data.banner_image_url = uploaded[banner_assignment].url;
      }
      
      // Assign card image
      if (card_assignment !== undefined && uploaded[card_assignment]) {
        data.card_image_id = uploaded[card_assignment].cloudflare_id;
        data.card_image_url = uploaded[card_assignment].url;
      }
    }
    
    // Remove images object
    delete data.images;
  }

  // Remove internal fields
  delete data._submitter_id;

  let parkId: string;

  // Check if this is an edit (has park_id) or a new creation
  if (data.park_id) {
    console.log(`Updating existing park ${data.park_id}`);
    parkId = data.park_id;
    delete data.park_id; // Remove ID from update data
    
    const normalizedData = normalizeStatusValue(data);
    const sanitizedData = sanitizeDateFields(normalizedData);
    const filteredData = filterDatabaseFields(sanitizedData, PARK_FIELDS);
    const { error } = await supabase
      .from('parks')
      .update(filteredData)
      .eq('id', parkId);

    if (error) throw new Error(`Failed to update park: ${error.message}`);
  } else {
    console.log('Creating new park');
    const normalizedData = normalizeStatusValue(data);
    const sanitizedData = sanitizeDateFields(normalizedData);
    const filteredData = filterDatabaseFields(sanitizedData, PARK_FIELDS);
    const { data: park, error } = await supabase
      .from('parks')
      .insert(filteredData)
      .select('id')
      .single();

    if (error) throw new Error(`Failed to create park: ${error.message}`);
    parkId = park.id;
  }

  // Insert photos into photos table
  if (uploadedPhotos.length > 0 && submitterId) {
    console.log(`Inserting ${uploadedPhotos.length} photos for park ${parkId}`);
    for (let i = 0; i < uploadedPhotos.length; i++) {
      const photo = uploadedPhotos[i];
      if (photo.cloudflare_id && photo.url) {
        const { error: photoError } = await supabase.from('photos').insert({
          entity_id: parkId,
          entity_type: 'park',
          cloudflare_image_id: photo.cloudflare_id,
          cloudflare_image_url: photo.url,
          caption: photo.caption || null,
          title: null,
          submitted_by: submitterId,
          approved_at: new Date().toISOString(),
          order_index: i,
        });

        if (photoError) {
          console.error(`Failed to insert photo ${i}:`, photoError);
        }
      }
    }
  }

  return parkId;
}

async function createRide(supabase: any, data: any): Promise<string> {
  const submitterId = data._submitter_id;
  let uploadedPhotos: any[] = [];
  
  // Extract relational data before transformation
  const technicalSpecifications = data._technical_specifications || [];
  const coasterStatistics = data._coaster_statistics || [];
  const nameHistory = data._name_history || [];
  
  // Transform images object if present
  if (data.images) {
    const { uploaded, banner_assignment, card_assignment } = data.images;
    
    if (uploaded && Array.isArray(uploaded)) {
      // Store uploaded photos for later insertion into photos table
      uploadedPhotos = uploaded;
      
      // Assign banner image
      if (banner_assignment !== undefined && uploaded[banner_assignment]) {
        data.banner_image_id = uploaded[banner_assignment].cloudflare_id;
        data.banner_image_url = uploaded[banner_assignment].url;
      }
      
      // Assign card image
      if (card_assignment !== undefined && uploaded[card_assignment]) {
        data.card_image_id = uploaded[card_assignment].cloudflare_id;
        data.card_image_url = uploaded[card_assignment].url;
      }
    }
    
    // Remove images object
    delete data.images;
  }

  // Remove internal fields and store park_id before filtering
  delete data._submitter_id;
  delete data._technical_specifications;
  delete data._coaster_statistics;
  delete data._name_history;
  const parkId = data.park_id;

  let rideId: string;

  // Check if this is an edit (has ride_id) or a new creation
  if (data.ride_id) {
    console.log(`Updating existing ride ${data.ride_id}`);
    rideId = data.ride_id;
    delete data.ride_id; // Remove ID from update data
    
    const normalizedData = normalizeStatusValue(data);
    const sanitizedData = sanitizeDateFields(normalizedData);
    const filteredData = filterDatabaseFields(sanitizedData, RIDE_FIELDS);
    const { error } = await supabase
      .from('rides')
      .update(filteredData)
      .eq('id', rideId);

    if (error) throw new Error(`Failed to update ride: ${error.message}`);
    
    // Update park ride counts after successful ride update
    if (parkId) {
      console.log(`Updating ride counts for park ${parkId}`);
      const { error: countError } = await supabase.rpc('update_park_ride_counts', {
        target_park_id: parkId
      });
      
      if (countError) {
        console.error('Failed to update park counts:', countError);
      }
    }
  } else {
    console.log('Creating new ride');
    const normalizedData = normalizeStatusValue(data);
    const sanitizedData = sanitizeDateFields(normalizedData);
    const filteredData = filterDatabaseFields(sanitizedData, RIDE_FIELDS);
    const { data: ride, error } = await supabase
      .from('rides')
      .insert(filteredData)
      .select('id')
      .single();

    if (error) throw new Error(`Failed to create ride: ${error.message}`);
    rideId = ride.id;
    
    // Update park ride counts after successful ride creation
    if (parkId) {
      console.log(`Updating ride counts for park ${parkId}`);
      const { error: countError } = await supabase.rpc('update_park_ride_counts', {
        target_park_id: parkId
      });
      
      if (countError) {
        console.error('Failed to update park counts:', countError);
      }
    }
  }

  // Insert photos into photos table
  if (uploadedPhotos.length > 0 && submitterId) {
    console.log(`Inserting ${uploadedPhotos.length} photos for ride ${rideId}`);
    for (let i = 0; i < uploadedPhotos.length; i++) {
      const photo = uploadedPhotos[i];
      if (photo.cloudflare_id && photo.url) {
        const { error: photoError } = await supabase.from('photos').insert({
          entity_id: rideId,
          entity_type: 'ride',
          cloudflare_image_id: photo.cloudflare_id,
          cloudflare_image_url: photo.url,
          caption: photo.caption || null,
          title: null,
          submitted_by: submitterId,
          approved_at: new Date().toISOString(),
          order_index: i,
        });

        if (photoError) {
          console.error(`Failed to insert photo ${i}:`, photoError);
        }
      }
    }
  }

  // Insert technical specifications
  if (technicalSpecifications.length > 0) {
    console.log(`Inserting ${technicalSpecifications.length} technical specs for ride ${rideId}`);
    const techSpecsToInsert = technicalSpecifications.map((spec: any) => ({
      ride_id: rideId,
      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
    }));
    
    const { error: techSpecError } = await supabase
      .from('ride_technical_specifications')
      .insert(techSpecsToInsert);
    
    if (techSpecError) {
      console.error('Failed to insert technical specifications:', techSpecError);
    }
  }

  // Insert coaster statistics
  if (coasterStatistics.length > 0) {
    console.log(`Inserting ${coasterStatistics.length} coaster stats for ride ${rideId}`);
    const statsToInsert = coasterStatistics.map((stat: any) => ({
      ride_id: rideId,
      stat_name: stat.stat_name,
      stat_value: stat.stat_value,
      unit: stat.unit || null,
      category: stat.category || null,
      description: stat.description || null,
      display_order: stat.display_order || 0
    }));
    
    const { error: statsError } = await supabase
      .from('ride_coaster_stats')
      .insert(statsToInsert);
    
    if (statsError) {
      console.error('Failed to insert coaster statistics:', statsError);
    }
  }

  // Insert name history
  if (nameHistory.length > 0) {
    console.log(`Inserting ${nameHistory.length} former names for ride ${rideId}`);
    const namesToInsert = nameHistory.map((name: any) => ({
      ride_id: rideId,
      former_name: name.former_name,
      date_changed: name.date_changed || null,
      reason: name.reason || null,
      from_year: name.from_year || null,
      to_year: name.to_year || null,
      order_index: name.order_index || 0
    }));
    
    const { error: namesError } = await supabase
      .from('ride_name_history')
      .insert(namesToInsert);
    
    if (namesError) {
      console.error('Failed to insert name history:', namesError);
    }
  }

  return rideId;
}

async function createCompany(supabase: any, data: any, companyType: string): Promise<string> {
  // Transform images object if present
  if (data.images) {
    const { uploaded, banner_assignment, card_assignment } = data.images;
    
    if (uploaded && Array.isArray(uploaded)) {
      // Assign banner image
      if (banner_assignment !== undefined && uploaded[banner_assignment]) {
        data.banner_image_id = uploaded[banner_assignment].cloudflare_id;
        data.banner_image_url = uploaded[banner_assignment].url;
      }
      
      // Assign card image
      if (card_assignment !== undefined && uploaded[card_assignment]) {
        data.card_image_id = uploaded[card_assignment].cloudflare_id;
        data.card_image_url = uploaded[card_assignment].url;
      }
    }
    
    // Remove images object
    delete data.images;
  }

  // Check if this is an edit (has company_id or id) or a new creation
  const companyId = data.company_id || data.id;
  
  if (companyId) {
    console.log(`Updating existing company ${companyId}`);
    const updateData = sanitizeDateFields({ ...data, company_type: companyType });
    delete updateData.company_id;
    delete updateData.id; // Remove ID from update data
    const filteredData = filterDatabaseFields(updateData, COMPANY_FIELDS);
    
    const { error } = await supabase
      .from('companies')
      .update(filteredData)
      .eq('id', companyId);

    if (error) throw new Error(`Failed to update company: ${error.message}`);
    return companyId;
  } else {
    console.log('Creating new company');
    const companyData = sanitizeDateFields({ ...data, company_type: companyType });
    const filteredData = filterDatabaseFields(companyData, COMPANY_FIELDS);
    const { data: company, error } = await supabase
      .from('companies')
      .insert(filteredData)
      .select('id')
      .single();

    if (error) throw new Error(`Failed to create company: ${error.message}`);
    return company.id;
  }
}

async function createRideModel(supabase: any, data: any): Promise<string> {
  let rideModelId: string;

  // Extract relational data before transformation
  const technicalSpecifications = data._technical_specifications || [];

  // Remove internal fields
  delete data._technical_specifications;

  // Check if this is an edit (has ride_model_id) or a new creation
  if (data.ride_model_id) {
    console.log(`Updating existing ride model ${data.ride_model_id}`);
    rideModelId = data.ride_model_id;
    delete data.ride_model_id; // Remove ID from update data
    
    const sanitizedData = sanitizeDateFields(data);
    const filteredData = filterDatabaseFields(sanitizedData, RIDE_MODEL_FIELDS);
    const { error } = await supabase
      .from('ride_models')
      .update(filteredData)
      .eq('id', rideModelId);

    if (error) throw new Error(`Failed to update ride model: ${error.message}`);
  } else {
    console.log('Creating new ride model');
    
    // Validate required fields
    if (!data.manufacturer_id) {
      throw new Error('Ride model must be associated with a manufacturer');
    }
    if (!data.name || !data.slug) {
      throw new Error('Ride model must have a name and slug');
    }
    
    const sanitizedData = sanitizeDateFields(data);
    const filteredData = filterDatabaseFields(sanitizedData, RIDE_MODEL_FIELDS);
    const { data: model, error } = await supabase
      .from('ride_models')
      .insert(filteredData)
      .select('id')
      .single();

    if (error) throw new Error(`Failed to create ride model: ${error.message}`);
    rideModelId = model.id;
  }

  // Insert technical specifications
  if (technicalSpecifications.length > 0) {
    console.log(`Inserting ${technicalSpecifications.length} technical specs for ride model ${rideModelId}`);
    const techSpecsToInsert = technicalSpecifications.map((spec: any) => ({
      ride_model_id: rideModelId,
      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
    }));
    
    const { error: techSpecError } = await supabase
      .from('ride_model_technical_specifications')
      .insert(techSpecsToInsert);
    
    if (techSpecError) {
      console.error('Failed to insert technical specifications:', techSpecError);
    }
  }

  return rideModelId;
}

async function approvePhotos(supabase: any, data: any, submissionItemId: string): Promise<void> {
  const photos = data.photos || [];
  
  for (const photo of photos) {
    const photoData = {
      entity_id: data.entity_id,
      entity_type: data.context,
      cloudflare_image_id: extractImageId(photo.url),
      cloudflare_image_url: photo.url,
      title: photo.title,
      caption: photo.caption,
      date_taken: photo.date,
      order_index: photo.order,
      submission_id: submissionItemId
    };

    const { error } = await supabase.from('photos').insert(photoData);
    if (error) {
      console.error('Failed to insert photo:', error);
      throw new Error(`Failed to insert photo: ${error.message}`);
    }
  }
}

function extractImageId(url: string): string {
  const matches = url.match(/\/([^\/]+)\/public$/);
  return matches ? matches[1] : url;
}

async function editPhoto(supabase: any, data: any): Promise<void> {
  console.log(`Editing photo ${data.photo_id}`);
  const { error } = await supabase
    .from('photos')
    .update({
      caption: data.new_caption,
    })
    .eq('id', data.photo_id);

  if (error) throw new Error(`Failed to edit photo: ${error.message}`);
}

async function deletePhoto(supabase: any, data: any): Promise<void> {
  console.log(`Deleting photo ${data.photo_id}`);
  const { error } = await supabase
    .from('photos')
    .delete()
    .eq('id', data.photo_id);

  if (error) throw new Error(`Failed to delete photo: ${error.message}`);
}

async function createTimelineEvent(
  supabase: any, 
  data: any, 
  submitterId: string, 
  approvingUserId: string,
  submissionId: string
): Promise<string> {
  // Determine if this is an edit based on presence of event_id in data
  // Note: Timeline events from frontend use 'id' field, not 'event_id'
  const eventId = data.id || data.event_id;
  
  if (eventId) {
    console.log(`Updating existing timeline event ${eventId}`);
    
    // Prepare update data (exclude ID and audit fields)
    const updateData: any = {
      event_type: data.event_type,
      event_date: data.event_date,
      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,
    };
    
    // Remove undefined/null values
    Object.keys(updateData).forEach(key => 
      updateData[key] === undefined && delete updateData[key]
    );

    const { error } = await supabase
      .from('entity_timeline_events')
      .update(updateData)
      .eq('id', eventId);

    if (error) throw new Error(`Failed to update timeline event: ${error.message}`);
    return eventId;
  } else {
    console.log('Creating new timeline event');
    
    const eventData = {
      entity_id: data.entity_id,
      entity_type: data.entity_type,
      event_type: data.event_type,
      event_date: data.event_date,
      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,
      created_by: submitterId,
      approved_by: approvingUserId,
      submission_id: submissionId,
    };

    const { data: event, error } = await supabase
      .from('entity_timeline_events')
      .insert(eventData)
      .select('id')
      .single();

    if (error) throw new Error(`Failed to create timeline event: ${error.message}`);
    return event.id;
  }
}
