import { supabase } from '@/integrations/supabase/client';
import { getErrorMessage } from './errorHandler';
import { logger } from './logger';
import { extractCloudflareImageId } from './cloudflareImageUtils';

// Core submission item interface with dependencies
// NOTE: item_data and original_data use `unknown` because they contain dynamic structures
// that vary by item_type. Use type guards from @/types/submission-item-data.ts to access safely.

export interface SubmissionItemWithDeps {
  id: string;
  submission_id: string;
  item_type: string;
  item_data: unknown; // Dynamic structure - use type guards for safe access
  original_data?: unknown; // Dynamic structure - use type guards for safe access
  action_type?: 'create' | 'edit' | 'delete';
  status: 'pending' | 'approved' | 'rejected' | 'flagged' | 'skipped'; // Matches ReviewStatus from statuses.ts
  depends_on: string | null;
  order_index: number;
  approved_entity_id: string | null;
  rejection_reason: string | null;
  created_at: string;
  updated_at: string;
  dependencies?: SubmissionItemWithDeps[];
  dependents?: SubmissionItemWithDeps[];
}

export interface DependencyConflict {
  itemId: string;
  type: 'missing_parent' | 'rejected_parent' | 'circular_dependency';
  message: string;
  suggestions: Array<{
    action: 'link_existing' | 'cascade_reject' | 'escalate' | 'create_parent';
    label: string;
    entityId?: string;
  }>;
}

export interface ConflictCheckResult {
  hasConflict: boolean;
  clientVersion: {
    last_modified_at: string;
  };
  serverVersion?: {
    last_modified_at: string;
    last_modified_by: string;
    modified_by_profile?: {
      username: string;
      display_name?: string;
    };
  } | null;
}

/**
 * Fetch all items for a submission with their dependencies
 * Now joins with relational tables instead of using JSONB
 */
export async function fetchSubmissionItems(submissionId: string): Promise<SubmissionItemWithDeps[]> {
  const { data, error } = await supabase
    .from('submission_items')
    .select(`
      *,
      park_submission:park_submissions!item_data_id(*),
      ride_submission:ride_submissions!item_data_id(*),
      photo_submission:photo_submissions!item_data_id(
        *,
        photo_items:photo_submission_items(*)
      )
    `)
    .eq('submission_id', submissionId)
    .order('order_index', { ascending: true });

  if (error) throw error;
  
  // Transform data to include relational data as item_data
  return (data || []).map(item => {
    let item_data: unknown;
    
    switch (item.item_type) {
      case 'park':
        item_data = (item as any).park_submission;
        break;
      case 'ride':
        item_data = (item as any).ride_submission;
        break;
      case 'photo':
        item_data = {
          ...(item as any).photo_submission,
          photos: (item as any).photo_submission?.photo_items || []
        };
        break;
      default:
        item_data = null;
    }
    
    return {
      ...item,
      item_data,
      status: item.status as 'pending' | 'approved' | 'rejected',
    };
  }) as SubmissionItemWithDeps[];
}

/**
 * Build dependency tree for submission items
 */
export function buildDependencyTree(items: SubmissionItemWithDeps[]): SubmissionItemWithDeps[] {
  const itemMap = new Map(items.map(item => [item.id, { ...item, dependencies: [] as SubmissionItemWithDeps[], dependents: [] as SubmissionItemWithDeps[] }]));

  // Build relationships
  items.forEach(item => {
    if (item.depends_on) {
      const parent = itemMap.get(item.depends_on);
      const child = itemMap.get(item.id);
      if (parent && child) {
        parent.dependents = parent.dependents || [];
        parent.dependents.push(child);
        child.dependencies = child.dependencies || [];
        child.dependencies.push(parent);
      }
    }
  });

  return Array.from(itemMap.values());
}

/**
 * Detect dependency conflicts for selective approval
 */
export async function detectDependencyConflicts(
  items: SubmissionItemWithDeps[],
  selectedItemIds: string[]
): Promise<DependencyConflict[]> {
  const conflicts: DependencyConflict[] = [];
  const selectedSet = new Set(selectedItemIds);

  for (const item of items) {
    // Check if parent is rejected but child is selected
    if (item.depends_on && selectedSet.has(item.id)) {
      const parent = items.find(i => i.id === item.depends_on);
      if (parent && (parent.status === 'rejected' || !selectedSet.has(parent.id))) {
        
        // Find existing entities that could be linked
        const suggestions: DependencyConflict['suggestions'] = [];
        
        // Suggest creating parent
        if (parent.status !== 'rejected') {
          const parentData = typeof parent.item_data === 'object' && parent.item_data !== null && !Array.isArray(parent.item_data)
            ? parent.item_data as Record<string, unknown>
            : {};
          const parentName = 'name' in parentData && typeof parentData.name === 'string' ? parentData.name : 'Unnamed';
          
          suggestions.push({
            action: 'create_parent',
            label: `Also approve ${parent.item_type}: ${parentName}`,
          });
        }

        // Suggest linking to existing entity
        if (parent.item_type === 'park') {
          const parentData = typeof parent.item_data === 'object' && parent.item_data !== null && !Array.isArray(parent.item_data)
            ? parent.item_data as Record<string, unknown>
            : {};
          const parentName = 'name' in parentData && typeof parentData.name === 'string' ? parentData.name : '';
          
          const { data: parks } = await supabase
            .from('parks')
            .select('id, name')
            .ilike('name', `%${parentName}%`)
            .limit(3);
          
          parks?.forEach(park => {
            suggestions.push({
              action: 'link_existing',
              label: `Link to existing park: ${park.name}`,
              entityId: park.id,
            });
          });
        }

        suggestions.push({
          action: 'escalate',
          label: 'Escalate to admin for resolution',
        });

        conflicts.push({
          itemId: item.id,
          type: 'missing_parent',
          message: `Cannot approve ${item.item_type} without its parent ${parent.item_type}`,
          suggestions,
        });
      }
    }
  }

  return conflicts;
}

/**
 * Update individual submission item status
 * Note: item_data and original_data are read-only (managed via relational tables)
 */
export async function updateSubmissionItem(
  itemId: string,
  updates: Partial<SubmissionItemWithDeps>
): Promise<void> {
  // Remove item_data and original_data from updates (managed via relational tables)
  const { item_data, original_data, ...cleanUpdates } = updates;
  
  const { error } = await supabase
    .from('submission_items')
    .update(cleanUpdates)
    .eq('id', itemId);

  if (error) throw error;
}

/**
 * Approve multiple items with dependency handling
 */
export async function approveSubmissionItems(
  items: SubmissionItemWithDeps[],
  userId: string
): Promise<void> {
  if (!userId) {
    throw new Error('User authentication required to approve items');
  }

  // Sort by dependency order (parents first)
  const sortedItems = topologicalSort(items);
  
  // Build dependency resolution map
  const dependencyMap = new Map<string, string>();
  
  for (const item of sortedItems) {
    let entityId: string | null = null;
    let isEdit = false;

    try {
      // Determine if this is an edit by checking for entity_id in item_data
      const itemData = typeof item.item_data === 'object' && item.item_data !== null && !Array.isArray(item.item_data)
        ? item.item_data as Record<string, unknown>
        : {};
      
      isEdit = !!(
        ('park_id' in itemData && itemData.park_id) || 
        ('ride_id' in itemData && itemData.ride_id) || 
        ('company_id' in itemData && itemData.company_id) ||
        ('ride_model_id' in itemData && itemData.ride_model_id)
      );

      // Create the entity based on type with dependency resolution
      // PASS sortedItems to enable correct index-based resolution
      switch (item.item_type) {
        case 'park':
          entityId = await createPark(item.item_data, dependencyMap, sortedItems);
          break;
        case 'ride':
          entityId = await createRide(item.item_data, dependencyMap, sortedItems);
          break;
        case 'manufacturer':
        case 'operator':
        case 'property_owner':
        case 'designer':
          entityId = await createCompany(item.item_data, item.item_type, dependencyMap, sortedItems);
          break;
        case 'ride_model':
          entityId = await createRideModel(item.item_data, dependencyMap, sortedItems);
          break;
        case 'photo':
          entityId = await approvePhotos(item.item_data, dependencyMap, userId, item.submission_id, sortedItems);
          break;
      }

      if (!entityId) {
        throw new Error(`Failed to create ${item.item_type}: no entity ID returned`);
      }

      // Update item status
      await updateSubmissionItem(item.id, {
        status: 'approved' as const,
        approved_entity_id: entityId,
      });

      // Create version history (skip for photo type)
      if (item.item_type !== 'photo') {
        await createVersionForApprovedItem(
          item.item_type,
          entityId,
          userId,
          item.submission_id,
          isEdit
        );
      }

      // Add to dependency map using item.id as key
      dependencyMap.set(item.id, entityId);

    } catch (error: unknown) {
      const errorMsg = getErrorMessage(error);
      logger.error('Error approving items', {
        action: 'approve_submission_items',
        error: errorMsg,
        userId,
        itemCount: items.length
      });
      
      // Update item with error status
      await updateSubmissionItem(item.id, {
        status: 'rejected' as const,
        rejection_reason: `Failed to create entity: ${errorMsg}`,
      });
      
      throw new Error(`Failed to approve ${item.item_type}: ${errorMsg}`);
    }
  }
}

/**
 * Create version history for approved submission item
 * 
 * NOTE: Versions are now created automatically via database triggers.
 * This function is no longer needed since the relational versioning system
 * handles version creation automatically when entities are inserted/updated.
 * 
 * The trigger `create_relational_version()` reads session variables set by
 * the edge function and creates versions in the appropriate `*_versions` table.
 */
async function createVersionForApprovedItem(
  itemType: string,
  entityId: string,
  userId: string,
  submissionId: string,
  isEdit: boolean
): Promise<void> {
  // No-op: Versions are created automatically by triggers
  // The edge function sets:
  // - app.current_user_id = original submitter
  // - app.submission_id = submission ID
  // Then the trigger creates the version automatically
  console.debug(
    `Version will be created automatically by trigger for ${itemType} ${entityId}`
  );
}

/**
 * Topological sort for dependency-ordered processing
 */
function topologicalSort(items: SubmissionItemWithDeps[]): SubmissionItemWithDeps[] {
  const sorted: SubmissionItemWithDeps[] = [];
  const visited = new Set<string>();
  const temp = new Set<string>();

  function visit(item: SubmissionItemWithDeps) {
    if (temp.has(item.id)) {
      throw new Error('Circular dependency detected');
    }
    if (visited.has(item.id)) return;

    temp.add(item.id);

    if (item.dependencies) {
      item.dependencies.forEach(dep => visit(dep));
    }

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

  items.forEach(item => {
    if (!visited.has(item.id)) {
      visit(item);
    }
  });

  return sorted;
}

/**
 * Extract image URLs from ImageAssignments structure
 */
function extractImageAssignments(images: any) {
  if (!images || !images.uploaded || !Array.isArray(images.uploaded)) {
    return {
      banner_image_url: null,
      banner_image_id: null,
      card_image_url: null,
      card_image_id: null,
    };
  }

  const bannerImage = images.banner_assignment !== null && images.banner_assignment !== undefined
    ? images.uploaded[images.banner_assignment]
    : null;
    
  const cardImage = images.card_assignment !== null && images.card_assignment !== undefined
    ? images.uploaded[images.card_assignment]
    : null;

  return {
    banner_image_url: bannerImage?.url || null,
    banner_image_id: bannerImage?.cloudflare_id || null,
    card_image_url: cardImage?.url || null,
    card_image_id: cardImage?.cloudflare_id || null,
  };
}

/**
 * Helper functions to create entities with dependency resolution
 */
async function createPark(data: any, dependencyMap: Map<string, string>, sortedItems: SubmissionItemWithDeps[]): Promise<string> {
  const { transformParkData, validateSubmissionData } = await import('./entityTransformers');
  const { ensureUniqueSlug } = await import('./slugUtils');

  // Check if this is an edit (has park_id)
  const isEdit = !!data.park_id;

  if (isEdit) {
    // Handle park edit
    const resolvedData = resolveDependencies(data, dependencyMap, sortedItems);
    
    // Resolve location_id if location data is provided
    let locationId = resolvedData.location_id;
    if (resolvedData.location && !locationId) {
      locationId = await resolveLocationId(resolvedData.location);
    }
    
    // Extract image assignments from ImageAssignments structure
    const imageData = extractImageAssignments(resolvedData.images);
    
    // Update the park
    const updateData: any = {
      name: resolvedData.name,
      slug: resolvedData.slug,
      description: resolvedData.description || null,
      park_type: resolvedData.park_type,
      status: resolvedData.status,
      opening_date: resolvedData.opening_date || null,
      closing_date: resolvedData.closing_date || null,
      website_url: resolvedData.website_url || null,
      phone: resolvedData.phone || null,
      email: resolvedData.email || null,
      operator_id: resolvedData.operator_id || null,
      property_owner_id: resolvedData.property_owner_id || null,
      location_id: locationId || null,
      ...imageData,
      updated_at: new Date().toISOString()
    };

    const { error } = await supabase
      .from('parks')
      .update(updateData)
      .eq('id', data.park_id);

  if (error) {
    logger.error('Error updating park', {
      action: 'update_park',
      parkId: data.park_id,
      error: error.message
    });
    throw new Error(`Database error: ${error.message}`);
  }

    return data.park_id;
  }

  // Handle park creation
  validateSubmissionData(data, 'Park');
  const resolvedData = resolveDependencies(data, dependencyMap, sortedItems);

  // Resolve location_id if location data is provided
  let locationId = resolvedData.location_id;
  if (resolvedData.location && !locationId) {
    locationId = await resolveLocationId(resolvedData.location);
  }

  // Ensure unique slug
  const uniqueSlug = await ensureUniqueSlug(resolvedData.slug, 'parks');
  resolvedData.slug = uniqueSlug;

  // Extract image assignments
  const imageData = extractImageAssignments(resolvedData.images);

  // Transform to database format
  const parkData = { 
    ...transformParkData(resolvedData), 
    ...imageData,
    location_id: locationId || null,
  };

  // Insert into database
  const { data: park, error } = await supabase
    .from('parks')
    .insert(parkData)
    .select('id')
    .single();

  if (error) {
    logger.error('Error creating park', {
      action: 'create_park',
      parkName: resolvedData.name,
      error: error.message
    });
    throw new Error(`Database error: ${error.message}`);
  }

  return park.id;
}

/**
 * Resolve location data to a location_id
 * 
 * SECURITY NOTE: Locations should go through moderation flow.
 * Current implementation allows moderators to create locations directly during approval.
 * This is acceptable as only moderators can call this function (via RLS on content_submissions).
 * 
 * For user-submitted locations in the future, they should be submitted as separate
 * submission_items with item_type='location' and go through the moderation queue.
 */
async function resolveLocationId(locationData: any): Promise<string | null> {
  if (!locationData || !locationData.latitude || !locationData.longitude) {
    return null;
  }

  // Check if location already exists by coordinates
  const { data: existingLocation } = await supabase
    .from('locations')
    .select('id')
    .eq('latitude', locationData.latitude)
    .eq('longitude', locationData.longitude)
    .maybeSingle();

  if (existingLocation) {
    return existingLocation.id;
  }

  // Create new location (moderator has permission via RLS)
  // FUTURE TODO: Change this to submission flow for user-submitted locations
  const { data: newLocation, error } = await supabase
    .from('locations')
    .insert({
      name: locationData.name,
      city: locationData.city || null,
      state_province: locationData.state_province || null,
      country: locationData.country,
      postal_code: locationData.postal_code || null,
      latitude: locationData.latitude,
      longitude: locationData.longitude,
      timezone: locationData.timezone || null,
    })
    .select('id')
    .single();

  if (error) {
    logger.error('Error creating location', {
      action: 'create_location',
      locationData,
      error: error.message
    });
    throw new Error(`Failed to create location: ${error.message}`);
  }

  return newLocation.id;
}

async function createRide(data: any, dependencyMap: Map<string, string>, sortedItems: SubmissionItemWithDeps[]): Promise<string> {
  const { transformRideData, validateSubmissionData } = await import('./entityTransformers');
  const { ensureUniqueSlug } = await import('./slugUtils');

  // Check if this is an edit (has ride_id)
  const isEdit = !!data.ride_id;

  if (isEdit) {
    // Handle ride edit
    const resolvedData = resolveDependencies(data, dependencyMap, sortedItems);
    
    // Extract image assignments from ImageAssignments structure
    const imageData = extractImageAssignments(resolvedData.images);
    
    // Update the ride
    const updateData: any = {
      name: resolvedData.name,
      slug: resolvedData.slug,
      description: resolvedData.description,
      category: resolvedData.category,
      ride_sub_type: resolvedData.ride_sub_type,
      status: resolvedData.status,
      opening_date: resolvedData.opening_date,
      closing_date: resolvedData.closing_date,
      height_requirement: resolvedData.height_requirement,
      age_requirement: resolvedData.age_requirement,
      capacity_per_hour: resolvedData.capacity_per_hour,
      duration_seconds: resolvedData.duration_seconds,
      max_speed_kmh: resolvedData.max_speed_kmh,
      max_height_meters: resolvedData.max_height_meters,
      length_meters: resolvedData.length_meters,
      inversions: resolvedData.inversions,
      coaster_type: resolvedData.coaster_type,
      seating_type: resolvedData.seating_type,
      intensity_level: resolvedData.intensity_level,
      drop_height_meters: resolvedData.drop_height_meters,
      max_g_force: resolvedData.max_g_force,
      manufacturer_id: resolvedData.manufacturer_id,
      ride_model_id: resolvedData.ride_model_id,
      ...imageData,
      updated_at: new Date().toISOString()
    };

    const { error } = await supabase
      .from('rides')
      .update(updateData)
      .eq('id', data.ride_id);

    if (error) {
      console.error('Error updating ride:', error);
      throw new Error(`Database error: ${error.message}`);
    }

    return data.ride_id;
  }

  // Handle ride creation
  validateSubmissionData(data, 'Ride');
  const resolvedData = resolveDependencies(data, dependencyMap, sortedItems);

  if (!resolvedData.park_id) {
    throw new Error('Ride must be associated with a park');
  }

  const uniqueSlug = await ensureUniqueSlug(resolvedData.slug, 'rides');
  resolvedData.slug = uniqueSlug;

  // Extract image assignments
  const imageData = extractImageAssignments(resolvedData.images);

  // Transform to database format
  const rideData = { ...transformRideData(resolvedData), ...imageData };

  const { data: ride, error } = await supabase
    .from('rides')
    .insert(rideData)
    .select('id')
    .single();

  if (error) {
    console.error('Error creating ride:', error);
    throw new Error(`Database error: ${error.message}`);
  }

  return ride.id;
}

async function createCompany(
  data: any,
  companyType: string,
  dependencyMap: Map<string, string>,
  sortedItems: SubmissionItemWithDeps[]
): Promise<string> {
  const { transformCompanyData, validateSubmissionData } = await import('./entityTransformers');
  const { ensureUniqueSlug } = await import('./slugUtils');

  // Check if this is an edit (has company_id)
  const isEdit = !!data.id;

  if (isEdit) {
    // Handle company edit
    const resolvedData = resolveDependencies(data, dependencyMap, sortedItems);
    
    // Extract image assignments from ImageAssignments structure
    const imageData = extractImageAssignments(resolvedData.images);
    
    // Update the company
    const updateData: any = {
      name: resolvedData.name,
      slug: resolvedData.slug,
      description: resolvedData.description || null,
      website_url: resolvedData.website_url || null,
      founded_year: resolvedData.founded_year || null,
      headquarters_location: resolvedData.headquarters_location || null,
      ...imageData,
      updated_at: new Date().toISOString()
    };

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

    if (error) {
      console.error('Error updating company:', error);
      throw new Error(`Database error: ${error.message}`);
    }

    return data.id;
  }

  // Handle company creation
  validateSubmissionData(data, 'Company');
  const resolvedData = resolveDependencies(data, dependencyMap, sortedItems);

  const uniqueSlug = await ensureUniqueSlug(resolvedData.slug, 'companies');
  resolvedData.slug = uniqueSlug;

  // Extract image assignments
  const imageData = extractImageAssignments(resolvedData.images);

  // Type guard for company type
  type ValidCompanyType = 'manufacturer' | 'designer' | 'operator' | 'property_owner';
  const validCompanyTypes: ValidCompanyType[] = ['manufacturer', 'designer', 'operator', 'property_owner'];
  
  if (!validCompanyTypes.includes(companyType as ValidCompanyType)) {
    throw new Error(`Invalid company type: ${companyType}`);
  }

  // Transform to database format
  const companyData = { ...transformCompanyData(resolvedData, companyType as ValidCompanyType), ...imageData };

  const { data: company, error } = await supabase
    .from('companies')
    .insert(companyData)
    .select('id')
    .single();

  if (error) {
    console.error('Error creating company:', error);
    throw new Error(`Database error: ${error.message}`);
  }

  return company.id;
}

async function createRideModel(data: any, dependencyMap: Map<string, string>, sortedItems: SubmissionItemWithDeps[]): Promise<string> {
  const { transformRideModelData, validateSubmissionData } = await import('./entityTransformers');
  const { ensureUniqueSlug } = await import('./slugUtils');

  // Check if this is an edit (has ride_model_id)
  const isEdit = !!data.ride_model_id;

  if (isEdit) {
    // Handle ride model edit
    const resolvedData = resolveDependencies(data, dependencyMap, sortedItems);
    
    // Extract image assignments from ImageAssignments structure
    const imageData = extractImageAssignments(resolvedData.images);
    
    // Update the ride model
    const updateData: any = {
      name: resolvedData.name,
      slug: resolvedData.slug,
      category: resolvedData.category,
      ride_type: resolvedData.ride_type || null,
      description: resolvedData.description || null,
      manufacturer_id: resolvedData.manufacturer_id,
      ...imageData,
      updated_at: new Date().toISOString()
    };

    const { error } = await supabase
      .from('ride_models')
      .update(updateData)
      .eq('id', data.ride_model_id);

    if (error) {
      console.error('Error updating ride model:', error);
      throw new Error(`Database error: ${error.message}`);
    }

    return data.ride_model_id;
  }

  // Handle ride model creation
  validateSubmissionData(data, 'Ride Model');
  const resolvedData = resolveDependencies(data, dependencyMap, sortedItems);

  // Validate manufacturer_id is present (required for ride models)
  if (!resolvedData.manufacturer_id) {
    throw new Error('Ride model must be associated with a manufacturer');
  }

  // Ensure unique slug
  const uniqueSlug = await ensureUniqueSlug(resolvedData.slug, 'ride_models');
  resolvedData.slug = uniqueSlug;

  // Extract image assignments
  const imageData = extractImageAssignments(resolvedData.images);

  // Transform to database format
  const modelData = { 
    ...transformRideModelData(resolvedData), 
    ...imageData
  };

  // Insert into database
  const { data: model, error } = await supabase
    .from('ride_models')
    .insert(modelData)
    .select('id')
    .single();

  if (error) {
    console.error('Error creating ride model:', error);
    throw new Error(`Database error: ${error.message}`);
  }

  return model.id;
}

async function approvePhotos(data: any, dependencyMap: Map<string, string>, userId: string, submissionId: string, sortedItems: SubmissionItemWithDeps[]): Promise<string> {
  // Photos are already uploaded to Cloudflare
  // Resolve dependencies for entity associations
  const resolvedData = resolveDependencies(data, dependencyMap, sortedItems);
  
  if (!resolvedData.photos || !Array.isArray(resolvedData.photos) || resolvedData.photos.length === 0) {
    throw new Error('No photos found in submission');
  }

  const { entity_id, context, park_id, ride_id, company_id } = resolvedData;
  
  // Determine entity_id and entity_type
  let finalEntityId = entity_id;
  let entityType = context;
  
  // Support legacy field names
  if (!finalEntityId) {
    if (park_id) {
      finalEntityId = park_id;
      entityType = 'park';
    } else if (ride_id) {
      finalEntityId = ride_id;
      entityType = 'ride';
    } else if (company_id) {
      finalEntityId = company_id;
      // Need to determine company type from database
    }
  }

  if (!finalEntityId || !entityType) {
    throw new Error('Missing entity_id or context in photo submission');
  }

  // Insert photos into the photos table
  const photosToInsert = resolvedData.photos.map((photo: any, index: number) => {
    // Extract CloudFlare image ID from URL if not provided
    // Supports both old imagedelivery.net and new cdn.thrillwiki.com URLs
    let cloudflareImageId = photo.cloudflare_image_id;
    if (!cloudflareImageId && photo.url) {
      cloudflareImageId = extractCloudflareImageId(photo.url);
      
      // Fallback: parse from URL structure
      if (!cloudflareImageId) {
        const urlParts = photo.url.split('/');
        cloudflareImageId = urlParts[urlParts.length - 2];
      }
    }

    return {
      cloudflare_image_id: cloudflareImageId,
      cloudflare_image_url: photo.url,
      entity_type: entityType,
      entity_id: finalEntityId,
      title: photo.title || resolvedData.title,
      caption: photo.caption,
      photographer_credit: photo.photographer_credit,
      date_taken: photo.date || photo.date_taken,
      order_index: photo.order !== undefined ? photo.order : index,
      is_featured: index === 0, // First photo is featured by default
      submission_id: submissionId,
      submitted_by: userId,
      approved_by: userId,
      approved_at: new Date().toISOString(),
    };
  });

  const { data: insertedPhotos, error } = await supabase
    .from('photos')
    .insert(photosToInsert)
    .select();

  if (error) {
    console.error('Error inserting photos:', error);
    throw new Error(`Database error: ${error.message}`);
  }

  // Update entity's featured image if this is the first photo
  if (insertedPhotos && insertedPhotos.length > 0) {
    const firstPhoto = insertedPhotos[0];
    await updateEntityFeaturedImage(
      entityType,
      finalEntityId,
      firstPhoto.cloudflare_image_url,
      firstPhoto.cloudflare_image_id
    );
  }

  // Return the first photo URL for backwards compatibility
  return resolvedData.photos[0].url;
}

/**
 * Update entity's featured image fields
 */
async function updateEntityFeaturedImage(
  entityType: string,
  entityId: string,
  imageUrl: string,
  imageId: string
): Promise<void> {
  try {
    // Update based on entity type
    if (entityType === 'park') {
      const { data: existingPark } = await supabase
        .from('parks')
        .select('card_image_url')
        .eq('id', entityId)
        .single();

      if (existingPark && !existingPark.card_image_url) {
        await supabase
          .from('parks')
          .update({
            card_image_url: imageUrl,
            card_image_id: imageId,
            updated_at: new Date().toISOString(),
          })
          .eq('id', entityId);
      }
    } else if (entityType === 'ride') {
      const { data: existingRide } = await supabase
        .from('rides')
        .select('card_image_url')
        .eq('id', entityId)
        .single();

      if (existingRide && !existingRide.card_image_url) {
        await supabase
          .from('rides')
          .update({
            card_image_url: imageUrl,
            card_image_id: imageId,
            updated_at: new Date().toISOString(),
          })
          .eq('id', entityId);
      }
    } else if (['manufacturer', 'operator', 'designer', 'property_owner'].includes(entityType)) {
      const { data: existingCompany } = await supabase
        .from('companies')
        .select('logo_url')
        .eq('id', entityId)
        .single();

      if (existingCompany && !existingCompany.logo_url) {
        await supabase
          .from('companies')
          .update({
            logo_url: imageUrl,
            updated_at: new Date().toISOString(),
          })
          .eq('id', entityId);
      }
    }
  } catch (error) {
    console.error(`Error updating ${entityType} featured image:`, error);
  }
}

/**
 * Resolve dependency references in item_data by looking up approved entity IDs
 * Replaces temporary references (_temp_*_ref) with actual database entity IDs
 * 
 * FIXED: Now uses sortedItems array for stable index-based resolution
 * instead of unreliable Array.from(dependencyMap.keys())[refIndex]
 */
function resolveDependencies(data: any, dependencyMap: Map<string, string>, sortedItems: SubmissionItemWithDeps[]): any {
  const resolved = { ...data };

  // List of foreign key fields that may need resolution
  const foreignKeys = [
    'park_id',
    'manufacturer_id',
    'designer_id',
    'operator_id',
    'property_owner_id',
    'ride_model_id',
    'location_id',
  ];

  // Resolve temporary references using sortedItems array (FIXED)
  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);
      }
    }
    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);
      }
    }
    delete resolved._temp_manufacturer_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);
      }
    }
    delete resolved._temp_designer_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);
      }
    }
    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);
      }
    }
    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);
      }
    }
    delete resolved._temp_ride_model_ref;
  }

  // Resolve each foreign key if it's a submission item ID
  for (const key of foreignKeys) {
    if (resolved[key] && dependencyMap.has(resolved[key])) {
      resolved[key] = dependencyMap.get(resolved[key]);
    }
  }

  return resolved;
}

/**
 * Reset rejected items back to pending status
 */
export async function resetRejectedItemsToPending(
  submissionId: string
): Promise<void> {
  // Reset rejected submission items to pending
  const { error: itemsError } = await supabase
    .from('submission_items')
    .update({
      status: 'pending' as const,
      rejection_reason: null,
      updated_at: new Date().toISOString()
    })
    .eq('submission_id', submissionId)
    .eq('status', 'rejected');

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

  // Reset parent submission to pending
  const { error: submissionError } = await supabase
    .from('content_submissions')
    .update({
      status: 'pending' as const,
      reviewed_at: null,
      reviewer_id: null,
      reviewer_notes: null,
      updated_at: new Date().toISOString()
    })
    .eq('id', submissionId)
    .in('status', ['rejected', 'partially_approved']);

  if (submissionError) {
    throw new Error(`Failed to reset submission: ${submissionError.message}`);
  }
}

/**
 * Reject multiple items with optional cascade to dependents
 */
export async function rejectSubmissionItems(
  items: SubmissionItemWithDeps[],
  reason: string,
  userId: string,
  cascade: boolean = true
): Promise<void> {
  if (!userId) {
    throw new Error('User authentication required to reject items');
  }

  if (!reason || !reason.trim()) {
    throw new Error('Rejection reason is required');
  }

  const itemsToReject = new Set<string>(items.map(i => i.id));

  // If cascading, collect all dependent items
  if (cascade) {
    for (const item of items) {
      await collectDependents(item, itemsToReject);
    }
  }

  // Update all items to rejected status
  const updates = Array.from(itemsToReject).map(async (itemId) => {
    const { error } = await supabase
      .from('submission_items')
      .update({
        status: 'rejected' as const,
        rejection_reason: reason,
        updated_at: new Date().toISOString(),
      })
      .eq('id', itemId);

    if (error) {
      console.error(`Error rejecting item ${itemId}:`, error);
      throw error;
    }
  });

  await Promise.all(updates);

  // Update parent submission status
  const submissionId = items[0]?.submission_id;
  if (submissionId) {
    await updateSubmissionStatusAfterRejection(submissionId);
  }
}

async function collectDependents(
  item: SubmissionItemWithDeps,
  rejectedSet: Set<string>
): Promise<void> {
  if (item.dependents && item.dependents.length > 0) {
    for (const dependent of item.dependents) {
      rejectedSet.add(dependent.id);
      await collectDependents(dependent, rejectedSet);
    }
  }
}

async function updateSubmissionStatusAfterRejection(submissionId: string): Promise<void> {
  // Get all items for this submission
  const { data: allItems, error: fetchError } = await supabase
    .from('submission_items')
    .select('status')
    .eq('submission_id', submissionId);

  if (fetchError) {
    console.error('Error fetching submission items:', fetchError);
    return;
  }

  if (!allItems || allItems.length === 0) return;

  const statuses = allItems.map(i => i.status);
  const allRejected = statuses.every(s => s === 'rejected');
  const allApproved = statuses.every(s => s === 'approved');
  const anyPending = statuses.some(s => s === 'pending');

  let newStatus: string;
  if (allRejected) {
    newStatus = 'rejected';
  } else if (allApproved) {
    newStatus = 'approved';
  } else if (anyPending) {
    newStatus = 'pending';
  } else {
    newStatus = 'partially_approved';
  }

  const { error: updateError } = await supabase
    .from('content_submissions')
    .update({
      status: newStatus,
      updated_at: new Date().toISOString(),
    })
    .eq('id', submissionId);

  if (updateError) {
    console.error('Error updating submission status:', updateError);
  }
}

/**
 * Edit a submission item - moderators edit directly, users auto-escalate
 */
export async function editSubmissionItem(
  itemId: string,
  newData: any,
  userId: string
): Promise<void> {
  if (!userId) {
    throw new Error('User authentication required to edit items');
  }

  // Get current item with relational data
  const { data: currentItem, error: fetchError } = await supabase
    .from('submission_items')
    .select(`
      *,
      submission:content_submissions!submission_id(user_id, status)
    `)
    .eq('id', itemId)
    .single();

  if (fetchError) throw fetchError;

  // Check if user has moderator/admin permissions
  const { data: userRoles } = await supabase
    .from('user_roles')
    .select('role')
    .eq('user_id', userId);

  const isModerator = userRoles?.some(r => 
    ['moderator', 'admin', 'superuser'].includes(r.role)
  );

  // Determine original action type - preserve submission intent
  const originalAction: 'create' | 'edit' | 'delete' = (currentItem.action_type as 'create' | 'edit' | 'delete') || 'create';

  if (isModerator) {
    // Track edit in edit history table
    const changes = {
      timestamp: new Date().toISOString(),
      editor: userId
    };

    // Moderators can edit directly - update relational table
    // Note: item_data and original_data columns have been removed
    // Updates now go directly to relational tables (park_submissions, ride_submissions, etc.)
    const { error: updateError } = await supabase
      .from('submission_items')
      .update({
        action_type: originalAction,
        updated_at: new Date().toISOString(),
      })
      .eq('id', itemId);

    if (updateError) throw updateError;

    // Phase 4: Record edit history
    const { error: historyError } = await supabase
      .from('item_edit_history')
      .insert({
        item_id: itemId,
        editor_id: userId,
        changes: changes,
      });

    if (historyError) {
      logger.error('Failed to record edit history', {
        itemId,
        editorId: userId,
        error: historyError.message,
      });
      // Don't fail the whole operation if history tracking fails
    }

    // CRITICAL: Create version history if this is an entity edit (not photo)
    // Only create version if this item has already been approved (has approved_entity_id)
    if (currentItem.item_type !== 'photo' && currentItem.approved_entity_id) {
      try {
        await createVersionForApprovedItem(
          currentItem.item_type,
          currentItem.approved_entity_id,
          userId,
          currentItem.submission_id,
          true // isEdit = true
        );
      } catch (versionError) {
        logger.error('Failed to create version for manual edit', {
          action: 'create_version_for_edit',
          itemType: currentItem.item_type,
          entityId: currentItem.approved_entity_id
        });
        // Don't fail the entire operation, just log the error
        // The edit itself is still saved, just without version history
      }
    }

    // Type guard for submission with user_id
    interface SubmissionWithUser {
      user_id: string;
      [key: string]: any;
    }

    // Log admin action
    await supabase
      .from('admin_audit_log')
      .insert({
        admin_user_id: userId,
        target_user_id: (currentItem.submission as SubmissionWithUser).user_id,
        action: 'edit_submission_item',
        details: {
          item_id: itemId,
          item_type: currentItem.item_type,
          changes: 'Item data updated with version history',
          version_created: !!(currentItem.approved_entity_id && currentItem.item_type !== 'photo'),
        },
      });
  } else {
    // Regular users: update submission items and auto-escalate
    // Note: item_data and original_data columns have been removed
    const { error: updateError } = await supabase
      .from('submission_items')
      .update({
        action_type: originalAction,
        updated_at: new Date().toISOString(),
      })
      .eq('id', itemId);

    if (updateError) throw updateError;

    // Auto-escalate the parent submission
    await escalateSubmission(
      currentItem.submission_id,
      `User requested edit to ${currentItem.item_type}`,
      userId
    );
  }
}

/**
 * Escalate submission for admin review
 */
export async function escalateSubmission(
  submissionId: string,
  reason: string,
  userId: string
): Promise<void> {
  if (!userId) {
    throw new Error('User authentication required to escalate submission');
  }

  // Fetch submission details for audit log
  const { data: submission } = await supabase
    .from('content_submissions')
    .select('user_id, submission_type')
    .eq('id', submissionId)
    .single();

  const { error } = await supabase
    .from('content_submissions')
    .update({
      status: 'pending' as const,
      escalation_reason: reason,
      escalated_by: userId,
      reviewer_notes: `Escalated: ${reason}`,
    })
    .eq('id', submissionId);

  if (error) throw error;

  // Log audit trail for escalation
  if (submission) {
    try {
      await supabase.rpc('log_admin_action', {
        _admin_user_id: userId,
        _target_user_id: submission.user_id,
        _action: 'submission_escalated',
        _details: {
          submission_id: submissionId,
          submission_type: submission.submission_type,
          escalation_reason: reason
        }
      });
    } catch (auditError) {
      logger.error('Failed to log escalation audit', { error: auditError });
    }
  }
}

/**
 * Phase 4: Fetch edit history for a submission item
 * Returns all edits with editor information
 */
export async function fetchEditHistory(itemId: string) {
  try {
    const { data, error } = await supabase
      .from('item_edit_history')
      .select(`
        id,
        changes,
        edited_at,
        editor:profiles!item_edit_history_editor_id_fkey (
          user_id,
          username,
          display_name,
          avatar_url
        )
      `)
      .eq('item_id', itemId)
      .order('edited_at', { ascending: false });

    if (error) throw error;

    return data || [];
  } catch (error: unknown) {
    logger.error('Error fetching edit history', {
      itemId,
      error: getErrorMessage(error),
    });
    return [];
  }
}

/**
 * Check if a submission has been modified since the client last loaded it
 * Used for optimistic locking to prevent concurrent edit conflicts
 */
export async function checkSubmissionConflict(
  submissionId: string,
  clientLastModified: string
): Promise<ConflictCheckResult> {
  try {
    const { data, error } = await supabase
      .from('content_submissions')
      .select(`
        last_modified_at,
        last_modified_by,
        profiles:last_modified_by (
          username,
          display_name,
          avatar_url
        )
      `)
      .eq('id', submissionId)
      .single();

    if (error) throw error;

    if (!data.last_modified_at) {
      return {
        hasConflict: false,
        clientVersion: { last_modified_at: clientLastModified },
      };
    }

    const serverTimestamp = new Date(data.last_modified_at).getTime();
    const clientTimestamp = new Date(clientLastModified).getTime();

    return {
      hasConflict: serverTimestamp > clientTimestamp,
      clientVersion: {
        last_modified_at: clientLastModified,
      },
      serverVersion: {
        last_modified_at: data.last_modified_at,
        last_modified_by: (data.last_modified_by ?? undefined) as string,
        modified_by_profile: data.profiles as any,
      },
    };
  } catch (error: unknown) {
    logger.error('Error checking submission conflict', {
      submissionId,
      error: getErrorMessage(error),
    });
    throw error;
  }
}

/**
 * Fetch recent versions of submission items for conflict resolution
 */
export async function fetchSubmissionVersions(
  submissionId: string,
  limit: number = 10
) {
  try {
    // Get all item IDs for this submission
    const { data: items, error: itemsError } = await supabase
      .from('submission_items')
      .select('id')
      .eq('submission_id', submissionId);

    if (itemsError) throw itemsError;
    if (!items || items.length === 0) return [];

    const itemIds = items.map(i => i.id);

    // Fetch edit history for all items
    const { data, error } = await supabase
      .from('item_edit_history')
      .select(`
        id,
        item_id,
        changes,
        edited_at,
        editor:profiles!item_edit_history_editor_id_fkey (
          user_id,
          username,
          display_name,
          avatar_url
        )
      `)
      .in('item_id', itemIds)
      .order('edited_at', { ascending: false })
      .limit(limit);

    if (error) throw error;

    return data || [];
  } catch (error: unknown) {
    logger.error('Error fetching submission versions', {
      submissionId,
      error: getErrorMessage(error),
    });
    return [];
  }
}
