'use client';

import { useRouter } from 'next/navigation';
import {
  ReactNode,
  createContext,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
} from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { ModalTypes } from '@/components/modal/constants/ModalTypes';
import { toast } from '@/components/toast/Toast';
import { useModalContext } from '@/context/ModalContext';
import { useApiClient } from '@/lib/apiClient';
import type { components } from '@/lib/gen';

// Use generated API types
type Application = components['schemas']['ApplicationResponse'];
type MarketplaceProject = components['schemas']['ProjectResponse'];
type MediaReference = components['schemas']['MediaResponse'];
type Message = components['schemas']['MessageResponse'];
type Review = components['schemas']['MediaResponse']; // Using MediaResponse for now, need to find correct Review type

type _AttachedMedia = {
  id: string;
  type: 'audio' | 'image';
  url: string;
  name: string;
};

interface MarketplaceProjectContextType {
  // Project state
  project: MarketplaceProject | null;
  isLoading: boolean;
  error: string | null;
  isCreator: boolean;

  // Messages state
  messages: Message[];
  messagesByMedia: Record<
    string,
    { media: MediaReference; messages: Message[] }
  >;
  isLoadingMessages: boolean;
  isSendingMessage: boolean;

  // Media state
  mediaReferences: MediaReference[];
  isLoadingMedia: boolean;

  // Applications state
  applications: Application[];
  isLoadingApplications: boolean;
  userApplication: Application | null;
  isCheckingApplication: boolean;

  // Reviews state
  reviews: Review[];
  isLoadingReviews: boolean;
  isSubmittingReview: boolean;

  // UI state
  selectedTimeRange: { start: number; end: number } | null;
  selectedMediaFilter: 'all' | 'source' | 'reference' | 'submission';
  isAlertDismissed: boolean;
  viewMode: 'media' | 'chat';
  setViewMode: (mode: 'media' | 'chat') => void;
  activeMediaId: string | null;
  setActiveMediaId: (mediaId: string | null) => void;
  filterByMediaId: string | null;
  setFilterByMediaId: (mediaId: string | null) => void;

  // Project editing state
  isEditingTitle: boolean;
  isEditingDescription: boolean;
  isSavingProject: boolean;
  isPublishing: boolean;
  editTitle: string;
  editDescription: string;

  // UI state
  showClipboardSuccess: boolean;
  showApplicationModal: boolean;

  // Media comment editing state
  editingMediaComment: string | null;
  editMediaComment: string;

  // Message editing state
  editingMessageId: string | null;
  editMessageContent: string;

  // File upload state
  attachedMedia: Array<{
    id: string;
    type: 'audio' | 'image';
    url: string;
    name: string;
  }>;
  setAttachedMedia: (
    media: Array<{
      id: string;
      type: 'audio' | 'image';
      url: string;
      name: string;
    }>
  ) => void;

  // Time ranges state
  timeRanges: MediaReference[];
  isLoadingTimeRanges: boolean;
  trackTimeRanges: Record<string, { start: number; end: number } | null>;
  setTrackTimeRanges: (
    ranges: Record<string, { start: number; end: number } | null>
  ) => void;
  waveformSelection: { start: number; end?: number } | null;
  setWaveformSelection: (
    selection: { start: number; end?: number } | null
  ) => void;
  handleTimeRangeClick: (
    timeRange: { start: number; end: number },
    mediaId?: string
  ) => void;
  playAudioAtTimeRange: (
    timeRange: { start: number; end: number },
    mediaId: string
  ) => void;

  // Current playing time range state
  currentPlayingTimeRange: {
    start: number;
    end: number;
    mediaId: string;
  } | null;
  setCurrentPlayingTimeRange: (
    timeRange: { start: number; end: number; mediaId: string } | null
  ) => void;

  // Comments state
  collapsedComments: Set<string>;
  setCollapsedComments: (comments: Set<string>) => void;

  // Filtered data
  filteredMessagesByMedia: Record<
    string,
    { media: MediaReference; messages: Message[] }
  >;

  // Active projects count (for fulfillers)
  activeProjectsCount: number | null;
  activeProjectLimit: number;
  canApply: boolean;
  isLoadingActiveProjectsCount: boolean;

  // External dependencies
  projectId: string;
  apiClient: ReturnType<typeof useApiClient>;
  currentUser: { id: number } | null;
  setMessages: (messages: Message[]) => void;
  setMessagesByMedia: (
    messagesByMedia: Record<
      string,
      { media: MediaReference; messages: Message[] }
    >
  ) => void;

  // Actions
  fetchProject: () => Promise<MarketplaceProject | null>;
  fetchMessages: (silent?: boolean) => Promise<Message[]>;
  fetchMediaReferences: () => Promise<MediaReference[]>;
  fetchApplications: (silent?: boolean) => Promise<void>;
  fetchReviews: (silent?: boolean) => Promise<void>;
  checkUserApplication: () => Promise<void>;
  fetchTimeRanges: () => Promise<MediaReference[]>;

  // Message actions
  handleSendMessage: (
    content: string,
    mediaId?: string,
    timeRange?: { start: number; end: number } | null,
    parentMessageId?: string
  ) => Promise<void>;
  handleEditMessage: (
    messageId: string,
    content: string,
    timeRange?: { start: number; end: number } | null
  ) => Promise<void>;
  handleDeleteMessage: (messageId: string) => Promise<void>;

  // Project actions
  handleSaveTitle: (title: string) => Promise<void>;
  handleSaveDescription: (description: string) => Promise<void>;
  handleSaveDeadline: (deadline: string) => Promise<void>;
  handleDeleteProject: () => Promise<void>;
  handleUnpublishProject: () => Promise<void>;
  handlePublishProject: () => Promise<void>;
  handleEditTitle: () => void;
  handleCancelTitleEdit: () => void;
  handleEditDescription: () => void;
  handleCancelDescriptionEdit: () => void;

  // UI actions
  setShowClipboardSuccess: (show: boolean) => void;
  setShowApplicationModal: (show: boolean) => void;
  setEditDescription: (description: string) => void;

  // Media actions
  handleDeleteMedia: (mediaId: string) => Promise<void>;
  handleSaveMediaTitle: (mediaId: string, title: string) => Promise<void>;
  handleSaveMediaDescription: (
    mediaId: string,
    description: string
  ) => Promise<void>;
  handleAcceptSubmission: (
    submissionId: string,
    closeProject?: boolean
  ) => Promise<void>;
  handleRejectSubmission: (
    submissionId: string,
    message?: string
  ) => Promise<void>;
  handleEditMediaComment: (mediaId: string, comment: string) => Promise<void>;
  handleSendTrackMessage: (mediaId: string) => Promise<void>;
  organizeMessagesByMedia: (
    messages: Message[],
    mediaRefs: MediaReference[]
  ) => Record<string, { media: MediaReference; messages: Message[] }>;

  // Application actions
  handleSubmitApplication: (
    message: string,
    portfolioUrl?: string,
    estimatedDays?: number
  ) => Promise<void>;
  handleAcceptApplication: (applicationId: string) => Promise<void>;
  handleRejectApplication: (applicationId: string) => Promise<void>;
  handleWithdrawApplication: (applicationId: string) => Promise<void>;

  // File upload actions
  handleFileUpload: (e: React.ChangeEvent<HTMLInputElement>) => void;
  handleFileDrop: (e: React.DragEvent<HTMLDivElement>) => void;
  removeAttachedMedia: (id: string) => void;

  // Review actions
  handleSubmitReview: (
    rating: number,
    feedback: string,
    _targetUserId: number,
    _targetUserRole: string
  ) => Promise<void>;

  // Share action
  handleShareProject: () => void;

  // UI actions
  setSelectedTimeRange: (
    timeRange: { start: number; end: number } | null
  ) => void;
  setSelectedMediaFilter: (
    filter: 'all' | 'source' | 'reference' | 'submission'
  ) => void;
  setIsAlertDismissed: (dismissed: boolean) => void;
  setEditingMessageId: (messageId: string | null) => void;
  setEditMessageContent: (content: string) => void;
  setEditingMediaComment: (mediaId: string | null) => void;
  setEditMediaComment: (comment: string) => void;
  setIsEditingTitle: (editing: boolean) => void;
  setEditTitle: (title: string) => void;
}

const MarketplaceProjectContext = createContext<
  MarketplaceProjectContextType | undefined
>(undefined);

export const useMarketplaceProject = () => {
  const context = useContext(MarketplaceProjectContext);
  if (!context) {
    throw new Error(
      'useMarketplaceProject must be used within a MarketplaceProjectProvider'
    );
  }
  return context;
};

interface MarketplaceProjectProviderProps {
  children: ReactNode;
  projectId: string;
}

export default function MarketplaceProjectProvider({
  children,
  projectId,
}: MarketplaceProjectProviderProps) {
  const router = useRouter();
  const { session } = useStores();
  const apiClient = useApiClient();
  const { openModalWithData } = useModalContext();

  // Project state
  const [project, setProject] = useState<MarketplaceProject | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  // Messages state
  const [messages, setMessages] = useState<Message[]>([]);
  const [messagesByMedia, setMessagesByMedia] = useState<
    Record<string, { media: MediaReference; messages: Message[] }>
  >({});
  const [isLoadingMessages, setIsLoadingMessages] = useState(false);
  const [isSendingMessage, setIsSendingMessage] = useState(false);

  // Media state
  const [mediaReferences, setMediaReferences] = useState<MediaReference[]>([]);
  const [isLoadingMedia, setIsLoadingMedia] = useState(false);

  // Applications state
  const [applications, setApplications] = useState<Application[]>([]);
  const [isLoadingApplications, setIsLoadingApplications] = useState(false);
  const [userApplication, setUserApplication] = useState<Application | null>(
    null
  );
  const [isCheckingApplication, setIsCheckingApplication] = useState(false);

  // Reviews state
  const [reviews, setReviews] = useState<Review[]>([]);
  const [isLoadingReviews, setIsLoadingReviews] = useState(false);
  const [isSubmittingReview, setIsSubmittingReview] = useState(false);

  // UI state
  const [selectedTimeRange, setSelectedTimeRange] = useState<{
    start: number;
    end: number;
  } | null>(null);
  const [selectedMediaFilter, setSelectedMediaFilter] = useState<
    'all' | 'source' | 'reference' | 'submission'
  >('all');
  const [isAlertDismissed, setIsAlertDismissed] = useState(false);
  const [viewMode, setViewModeState] = useState<'media' | 'chat'>(() => {
    // Load view mode from localStorage, default to 'chat' (timeline view)
    if (typeof window !== 'undefined') {
      const saved = localStorage.getItem('marketplace-view-mode');
      return (saved === 'media' ? 'media' : 'chat') as 'media' | 'chat';
    }
    return 'chat';
  });

  // Wrapper to persist view mode to localStorage
  const setViewMode = useCallback((mode: 'media' | 'chat') => {
    setViewModeState(mode);
    if (typeof window !== 'undefined') {
      localStorage.setItem('marketplace-view-mode', mode);
    }
  }, []);

  // Project editing state
  const [isEditingTitle, setIsEditingTitle] = useState(false);
  const [isEditingDescription, setIsEditingDescription] = useState(false);
  const [isSavingProject, setIsSavingProject] = useState(false);
  const [isPublishing, setIsPublishing] = useState(false);
  const [editTitle, setEditTitle] = useState('');
  const [editDescription, setEditDescription] = useState('');

  // UI state
  const [showClipboardSuccess, setShowClipboardSuccess] = useState(false);
  const [showApplicationModal, setShowApplicationModal] = useState(false);

  // Media comment editing state
  const [editingMediaComment, setEditingMediaComment] = useState<string | null>(
    null
  );
  const [editMediaComment, setEditMediaComment] = useState('');

  // Message editing state
  const [editingMessageId, setEditingMessageId] = useState<string | null>(null);
  const [editMessageContent, setEditMessageContent] = useState('');

  // File upload state
  const [attachedMedia, setAttachedMedia] = useState<
    Array<{ id: string; type: 'audio' | 'image'; url: string; name: string }>
  >([]);

  // Time ranges state
  const [timeRanges, setTimeRanges] = useState<MediaReference[]>([]);
  const [isLoadingTimeRanges, setIsLoadingTimeRanges] = useState(false);
  const [trackTimeRanges, setTrackTimeRanges] = useState<
    Record<string, { start: number; end: number } | null>
  >({});
  const [waveformSelection, setWaveformSelection] = useState<{
    start: number;
    end?: number;
  } | null>(null);

  // Comments state
  const [collapsedComments, setCollapsedComments] = useState<Set<string>>(
    new Set()
  );

  // Current playing time range state
  const [currentPlayingTimeRange, setCurrentPlayingTimeRange] = useState<{
    start: number;
    end: number;
    mediaId: string;
  } | null>(null);

  // Current user state
  const [currentUser, setCurrentUser] = useState<{ id: number } | null>(null);

  // Active projects count state (for fulfillers)
  const [activeProjectsCount, setActiveProjectsCount] = useState<number | null>(
    null
  );
  const [activeProjectLimit, setActiveProjectLimit] = useState<number>(1);
  const [isLoadingActiveProjectsCount, setIsLoadingActiveProjectsCount] =
    useState(false);

  // Active media and filter state
  const [activeMediaId, setActiveMediaId] = useState<string | null>(null);
  const [filterByMediaId, setFilterByMediaId] = useState<string | null>(null);

  // Polling refs
  const pollIntervalRef = useRef<NodeJS.Timeout | null>(null);

  // Derived state
  const isCreator = project?.is_creator || false;

  // Get current user from session
  useEffect(() => {
    if (session?.sessionIsLoaded) {
      if (session?.userId) {
        // Try to parse as number first
        let userId = parseInt(session.userId);
        if (isNaN(userId)) {
          // If parsing as number fails, try to extract number from string
          const match = session.userId.toString().match(/\d+/);
          if (match) {
            userId = parseInt(match[0]);
          }
        }

        if (!isNaN(userId)) {
          setCurrentUser({ id: userId });
        } else {
          setCurrentUser({ id: 0 });
        }
      } else {
        setCurrentUser({ id: 0 });
      }
    }
  }, [session?.sessionIsLoaded, session?.userId]);

  // Filter messages by selected media type
  const filteredMessagesByMedia = Object.entries(messagesByMedia).reduce(
    (acc, [mediaId, { media, messages: mediaMessages }]) => {
      // Apply media type filter
      if (selectedMediaFilter !== 'all') {
        if (
          selectedMediaFilter === 'source' &&
          media.reference_type !== 'source'
        )
          return acc;
        if (
          selectedMediaFilter === 'reference' &&
          media.reference_type !== 'reference'
        )
          return acc;
        if (
          selectedMediaFilter === 'submission' &&
          media.reference_type !== 'submission'
        )
          return acc;
      }

      acc[mediaId] = { media, messages: mediaMessages };
      return acc;
    },
    {} as Record<string, { media: MediaReference; messages: Message[] }>
  );

  // Fetch project data
  const fetchProject =
    useCallback(async (): Promise<MarketplaceProject | null> => {
      try {
        setIsLoading(true);
        setError(null);

        const response = await apiClient.GET(
          '/api/marketplace/projects/{project_id}/',
          {
            params: { path: { project_id: projectId } },
          }
        );

        if (response.data) {
          setProject(response.data);
          return response.data;
        } else {
          setError('Project not found');
          return null;
        }
      } catch (err) {
        console.error('Failed to fetch project:', err);
        setError('Failed to load project');
        toast({
          title: 'Failed to load project',
          description: 'Please try refreshing the page',
          status: 'error',
          duration: 5000,
          isClosable: true,
        });
        return null;
      } finally {
        setIsLoading(false);
      }
    }, [apiClient, projectId]);

  // Fetch messages
  const fetchMessages = useCallback(
    async (silent = false): Promise<Message[]> => {
      try {
        if (!silent) setIsLoadingMessages(true);

        const response = await apiClient.GET(
          '/api/marketplace/projects/{project_id}/messages/',
          {
            params: { path: { project_id: projectId } },
          }
        );

        if (response.data) {
          setMessages(response.data);
          return response.data; // Return the data for use in other functions
        }
        return [];
      } catch (err) {
        console.error('Failed to fetch messages:', err);
        return [];
      } finally {
        if (!silent) setIsLoadingMessages(false);
      }
    },
    [apiClient, projectId]
  );

  // Fetch media references
  const fetchMediaReferences = useCallback(async (): Promise<
    MediaReference[]
  > => {
    try {
      setIsLoadingMedia(true);

      const response = await apiClient.GET(
        '/api/marketplace/projects/{project_id}/media/',
        {
          params: { path: { project_id: projectId } },
        }
      );

      if (response.data) {
        const mediaData = Array.isArray(response.data)
          ? response.data
          : (response.data as { results?: MediaReference[] }).results || [];
        setMediaReferences(mediaData);
        return mediaData; // Return the data for use in other functions
      }
      return [];
    } catch (err) {
      console.error('Failed to fetch media references:', err);
      return [];
    } finally {
      setIsLoadingMedia(false);
    }
  }, [apiClient, projectId]);

  // Fetch applications
  const fetchApplications = useCallback(
    async (silent = false) => {
      try {
        if (!silent) setIsLoadingApplications(true);

        const response = await apiClient.GET(
          '/api/marketplace/projects/{project_id}/applications/',
          {
            params: { path: { project_id: projectId } },
          }
        );

        if (response.data) {
          const applicationsData = Array.isArray(response.data)
            ? response.data
            : (response.data as { applications?: Application[] })
                .applications || [];
          setApplications(applicationsData);

          // Find user's application
          const userApp = applicationsData.find(
            (app: Application) => app.fulfiller_id === session.user?.id
          );
          setUserApplication(userApp || null);
        }
      } catch (err) {
        console.error('Failed to fetch applications:', err);
      } finally {
        if (!silent) setIsLoadingApplications(false);
      }
    },
    [apiClient, projectId, session.user?.id]
  );

  // Fetch reviews
  const fetchReviews = useCallback(
    async (silent = false) => {
      try {
        if (!silent) setIsLoadingReviews(true);

        const response = await apiClient.GET(
          '/api/marketplace/projects/{project_id}/reviews/',
          {
            params: { path: { project_id: projectId } },
          }
        );

        if (response.data) {
          // Reviews endpoint returns never (not implemented), so we'll handle it gracefully
          setReviews([]);
        }
      } catch (err) {
        console.error('Failed to fetch reviews:', err);
      } finally {
        if (!silent) setIsLoadingReviews(false);
      }
    },
    [apiClient, projectId]
  );

  // Check user application
  const checkUserApplicationRef = useRef(false);
  const checkUserApplication = useCallback(async () => {
    // Prevent multiple simultaneous checks
    if (checkUserApplicationRef.current) {
      return;
    }

    try {
      checkUserApplicationRef.current = true;
      setIsCheckingApplication(true);
      const response = await apiClient.GET('/api/marketplace/applications/my/');

      if (response.data) {
        const applicationsData =
          (response.data as { applications?: Application[] }).applications ||
          [];
        const userApp = applicationsData.find(
          (app: Application) => app.project_id === projectId
        );
        setUserApplication(userApp || null);
      }
    } catch (err) {
      console.error('Failed to check user application:', err);
    } finally {
      setIsCheckingApplication(false);
      checkUserApplicationRef.current = false;
    }
  }, [apiClient, projectId]);

  // Fetch time ranges
  const fetchTimeRanges = useCallback(async (): Promise<MediaReference[]> => {
    try {
      setIsLoadingTimeRanges(true);
      const response = await apiClient.GET(
        '/api/marketplace/projects/{project_id}/media/',
        {
          params: { path: { project_id: projectId } },
        }
      );

      if (response.data) {
        const mediaData = Array.isArray(response.data)
          ? response.data
          : (response.data as { results?: MediaReference[] }).results || [];
        setTimeRanges(mediaData);
        return mediaData;
      }
      return [];
    } catch (err) {
      console.error('Failed to fetch time ranges:', err);
      return [];
    } finally {
      setIsLoadingTimeRanges(false);
    }
  }, [apiClient, projectId]);

  // Send message
  const handleSendMessage = async (
    content: string,
    mediaId?: string,
    timeRange?: { start: number; end: number } | null,
    parentMessageId?: string
  ) => {
    try {
      setIsSendingMessage(true);

      const response = await apiClient.POST(
        '/api/marketplace/projects/{project_id}/messages/',
        {
          params: { path: { project_id: projectId } },
          body: {
            content,
            media_reference_id: mediaId,
            time_range: timeRange,
            parent_message_id: parentMessageId,
          },
        }
      );

      if (response.data) {
        // Refresh messages and media references to update messagesByMedia
        const [fetchedMessages, mediaRefs] = await Promise.all([
          fetchMessages(true),
          fetchMediaReferences(),
        ]);

        // Organize messages by media reference
        const organized = organizeMessagesByMedia(fetchedMessages, mediaRefs);
        setMessagesByMedia(organized);
      }
    } catch (err) {
      console.error('Failed to send message:', err);
      toast({
        title: 'Failed to send message',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    } finally {
      setIsSendingMessage(false);
    }
  };

  // Edit message
  const handleEditMessage = async (
    messageId: string,
    content: string,
    timeRange?: { start: number; end: number } | null
  ) => {
    try {
      const body: {
        content: string;
        time_range?: { start: number; end: number } | null;
      } = {
        content,
      };
      // If timeRange is explicitly null, send null to remove it
      // If timeRange is undefined, don't include it (no change)
      // If timeRange is provided, send it to update
      if (timeRange !== undefined) {
        body.time_range = timeRange;
      }

      await apiClient.PUT(
        '/api/marketplace/projects/{project_id}/messages/{message_id}/',
        {
          params: { path: { project_id: projectId, message_id: messageId } },
          body,
        }
      );

      // Refresh messages and media references to update messagesByMedia
      const [fetchedMessages, mediaRefs] = await Promise.all([
        fetchMessages(true),
        fetchMediaReferences(),
      ]);

      // Organize messages by media reference
      const organized = organizeMessagesByMedia(fetchedMessages, mediaRefs);
      setMessagesByMedia(organized);
    } catch (err) {
      console.error('Failed to edit message:', err);
      toast({
        title: 'Failed to edit message',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Delete message
  const handleDeleteMessage = async (messageId: string) => {
    try {
      const response = await apiClient.DELETE(
        '/api/marketplace/projects/{project_id}/messages/{message_id}/',
        {
          params: { path: { project_id: projectId, message_id: messageId } },
        }
      );

      // Check if the response indicates an error
      if (
        response.error ||
        (response.response && response.response.status >= 400)
      ) {
        throw new Error(
          (response as { error?: { message?: string } }).error?.message ||
            `HTTP ${(response as { response?: { status?: number; statusText?: string } }).response?.status}: ${(response as { response?: { status?: number; statusText?: string } }).response?.statusText}`
        );
      }

      // Refresh messages and media references to update messagesByMedia
      const [fetchedMessages, mediaRefs] = await Promise.all([
        fetchMessages(true),
        fetchMediaReferences(),
      ]);

      // Update the main messages state
      setMessages(fetchedMessages);

      // Organize messages by media reference
      const organized = organizeMessagesByMedia(fetchedMessages, mediaRefs);
      setMessagesByMedia(organized);
    } catch (err) {
      console.error('Failed to delete message:', err);
      toast({
        title: 'Failed to delete message',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Save title
  const handleSaveTitle = async (title: string) => {
    if (!title.trim()) return;

    try {
      setIsSavingProject(true);

      await apiClient.PUT('/api/marketplace/projects/{project_id}/', {
        params: { path: { project_id: projectId } },
        body: { title: title.trim() },
      });

      setProject((prev) => (prev ? { ...prev, title: title.trim() } : null));
      setIsEditingTitle(false);
      setEditTitle('');
    } catch (err) {
      console.error('Failed to save title:', err);
      toast({
        title: 'Failed to save title',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    } finally {
      setIsSavingProject(false);
    }
  };

  // Save description
  const handleSaveDescription = async (description: string) => {
    try {
      setIsSavingProject(true);

      await apiClient.PUT('/api/marketplace/projects/{project_id}/', {
        params: { path: { project_id: projectId } },
        body: { description },
      });

      setProject((prev) => (prev ? { ...prev, description } : null));

      // Exit edit mode after successful save
      setIsEditingDescription(false);
    } catch (err) {
      console.error('Failed to save description:', err);
      toast({
        title: 'Failed to save description',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    } finally {
      setIsSavingProject(false);
    }
  };

  // Save deadline
  const handleSaveDeadline = async (deadline: string) => {
    try {
      setIsSavingProject(true);

      await apiClient.PUT('/api/marketplace/projects/{project_id}/', {
        params: { path: { project_id: projectId } },
        body: { deadline },
      });

      setProject((prev) => (prev ? { ...prev, deadline } : null));
    } catch (err) {
      console.error('Failed to save deadline:', err);
      toast({
        title: 'Failed to save deadline',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
      throw err; // Re-throw so component can handle it
    } finally {
      setIsSavingProject(false);
    }
  };

  // Delete project
  const handleDeleteProject = async () => {
    try {
      await apiClient.DELETE('/api/marketplace/projects/{project_id}/', {
        params: { path: { project_id: projectId } },
      });

      router.push('/marketplace');
    } catch (err) {
      console.error('Failed to delete project:', err);
      toast({
        title: 'Failed to delete project',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Unpublish project
  const handleUnpublishProject = async () => {
    try {
      await apiClient.POST(
        '/api/marketplace/projects/{project_id}/unpublish/',
        {
          params: { path: { project_id: projectId } },
        }
      );

      // Refresh project data
      await fetchProject();
    } catch (err) {
      console.error('Failed to unpublish project:', err);
      toast({
        title: 'Failed to unpublish project',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Publish project
  const handlePublishProject = async () => {
    try {
      setIsPublishing(true);
      await apiClient.POST('/api/marketplace/projects/{project_id}/publish/', {
        params: { path: { project_id: projectId } },
      });

      // Refresh project data
      await fetchProject();

      // Also update the project state immediately to reflect the change
      if (project) {
        setProject({
          ...project,
          status: 'OPEN',
          published_at: new Date().toISOString(),
        });
      }
    } catch (err) {
      console.error('Failed to publish project:', err);
      toast({
        title: 'Failed to publish project',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    } finally {
      setIsPublishing(false);
    }
  };

  // Delete media
  const handleDeleteMedia = async (mediaId: string) => {
    try {
      await apiClient.DELETE(
        '/api/marketplace/projects/{project_id}/media/{media_id}/',
        {
          params: { path: { project_id: projectId, media_id: mediaId } },
        }
      );

      // Refresh media references
      await fetchMediaReferences();
    } catch (err) {
      console.error('Failed to delete media:', err);
      toast({
        title: 'Failed to delete media',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Save media title
  const handleSaveMediaTitle = async (mediaId: string, title: string) => {
    if (!title.trim()) return;

    try {
      await apiClient.PATCH(
        '/api/marketplace/projects/{project_id}/media/{media_id}/',
        {
          params: {
            path: { project_id: projectId, media_id: mediaId },
            query: { title },
          },
        }
      );

      // Refresh media references to show the updated title
      await fetchMediaReferences();
    } catch (err) {
      console.error('Failed to save media title:', err);
      toast({
        title: 'Failed to save media title',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Save media description
  const handleSaveMediaDescription = async (
    mediaId: string,
    description: string
  ) => {
    try {
      await apiClient.PATCH(
        '/api/marketplace/projects/{project_id}/media/{media_id}/',
        {
          params: {
            path: { project_id: projectId, media_id: mediaId },
            query: { description: description || null },
          },
        }
      );

      // Refresh media references to show the updated description
      await fetchMediaReferences();
    } catch (err) {
      console.error('Failed to save media description:', err);
      toast({
        title: 'Failed to save media description',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Accept submission
  const handleAcceptSubmission = async (
    submissionId: string,
    closeProject: boolean = true
  ) => {
    try {
      const response = await apiClient.POST(
        '/api/marketplace/projects/{project_id}/submissions/{submission_id}/accept/',
        {
          params: {
            path: { project_id: projectId, submission_id: submissionId },
            query: { close_project: closeProject },
          },
        }
      );

      if ((response as { error?: unknown }).error) {
        throw new Error('Failed to accept submission');
      }

      if (
        !(response as { response?: { status?: number } }).response ||
        (response as { response?: { status?: number } }).response!.status! >=
          400
      ) {
        throw new Error('Failed to accept submission');
      }

      // Refresh both media references and project to show updated status
      const [, updatedProject] = await Promise.all([
        fetchMediaReferences(),
        fetchProject(),
      ]);

      // Refresh active projects count if project was completed
      if (updatedProject?.status === 'COMPLETED') {
        fetchActiveProjectsCount();
      }

      // After successful acceptance, open review modal for the fulfiller
      // The project should now be COMPLETED
      if (
        updatedProject?.fulfiller_id &&
        updatedProject?.status === 'COMPLETED'
      ) {
        // Use setTimeout to ensure the UI has updated before showing modal
        setTimeout(() => {
          openModalWithData(ModalTypes.MARKETPLACE_PARTICIPANT_REVIEWS, {
            userId: Number(updatedProject.fulfiller_id),
            userName:
              updatedProject.fulfiller_display_name ||
              updatedProject.fulfiller_handle ||
              'Fulfiller',
            userRole: 'fulfiller' as const,
            userAvatarUrl: updatedProject.fulfiller_avatar_url,
            userHandle: updatedProject.fulfiller_handle || undefined,
            projectId: updatedProject.id,
            projectStatus: updatedProject.status,
            canLeaveReview: true, // Creator can always review fulfiller after completion
          });
        }, 300);
      }
    } catch (err) {
      console.error('Failed to accept submission:', err);
      toast({
        title: 'Failed to accept submission',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Reject submission
  const handleRejectSubmission = async (
    submissionId: string,
    message?: string
  ) => {
    try {
      const response = await apiClient.POST(
        '/api/marketplace/projects/{project_id}/submissions/{submission_id}/reject/',
        {
          params: {
            path: { project_id: projectId, submission_id: submissionId },
          },
        }
      );

      if ((response as { error?: unknown }).error) {
        throw new Error('Failed to reject submission');
      }

      if (
        !(response as { response?: { status?: number } }).response ||
        (response as { response?: { status?: number } }).response!.status! >=
          400
      ) {
        throw new Error('Failed to reject submission');
      }

      // If a message was provided, send it attached to the submission
      if (message) {
        await apiClient.POST(
          '/api/marketplace/projects/{project_id}/messages/',
          {
            params: { path: { project_id: projectId } },
            body: {
              content: message,
              media_reference_id: submissionId,
            },
          }
        );
      }

      // Refresh media references, project, and messages to show updated status and new message
      await Promise.all([
        fetchMediaReferences(),
        fetchProject(),
        message ? fetchMessages(true) : Promise.resolve([]),
      ]);
    } catch (err) {
      console.error('Failed to reject submission:', err);
      toast({
        title: 'Failed to reject submission',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Edit media comment
  const handleEditMediaComment = async (mediaId: string, comment: string) => {
    try {
      await apiClient.PATCH(
        '/api/marketplace/projects/{project_id}/media/{media_id}/comment/',
        {
          params: { path: { project_id: projectId, media_id: mediaId } },
          body: { comment },
        }
      );

      // Refresh media references
      await fetchMediaReferences();
    } catch (err) {
      console.error('Failed to edit media comment:', err);
      toast({
        title: 'Failed to edit media comment',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Submit review
  const handleSubmitReview = async (
    rating: number,
    feedback: string,
    _targetUserId: number,
    _targetUserRole: string
  ) => {
    try {
      setIsSubmittingReview(true);

      await apiClient.POST('/api/marketplace/projects/{project_id}/reviews/', {
        params: {
          path: { project_id: projectId },
          query: { rating, feedback },
        },
      });

      // Refresh reviews
      await fetchReviews(true);
    } catch (err) {
      console.error('Failed to submit review:', err);
      toast({
        title: 'Failed to submit review',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    } finally {
      setIsSubmittingReview(false);
    }
  };

  // Submit application
  const handleSubmitApplication = async (
    message: string,
    portfolioUrl?: string,
    estimatedDays?: number
  ) => {
    try {
      await apiClient.POST('/api/marketplace/projects/{project_id}/apply/', {
        params: { path: { project_id: projectId } },
        body: {
          message: message.trim(),
          portfolio_url: portfolioUrl?.trim() || undefined,
          estimated_completion_days: estimatedDays || undefined,
        },
      });

      // Refresh applications to show the new application
      await fetchApplications(true);
    } catch (err) {
      console.error('Failed to submit application:', err);
      toast({
        title: 'Failed to submit application',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
      throw err; // Re-throw so the modal can handle the error
    }
  };

  // Accept application
  const handleAcceptApplication = async (applicationId: string) => {
    try {
      const response = await apiClient.POST(
        '/api/marketplace/applications/{application_id}/accept/',
        {
          params: { path: { application_id: applicationId } },
        }
      );

      if ((response as { error?: unknown }).error) {
        throw new Error('Failed to accept application');
      }

      // Refresh both applications and project to update status
      await Promise.all([fetchApplications(true), fetchProject()]);

      // Refresh active projects count since fulfiller was assigned
      fetchActiveProjectsCount();
    } catch (err) {
      console.error('Failed to accept application:', err);
      toast({
        title: 'Failed to accept application',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Reject application
  const handleRejectApplication = async (applicationId: string) => {
    try {
      const response = await apiClient.POST(
        '/api/marketplace/applications/{application_id}/reject/',
        {
          params: { path: { application_id: applicationId } },
        }
      );

      if ((response as { error?: unknown }).error) {
        throw new Error('Failed to reject application');
      }

      // Refresh applications
      await fetchApplications(true);
    } catch (err) {
      console.error('Failed to reject application:', err);
      toast({
        title: 'Failed to reject application',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Withdraw application
  const handleWithdrawApplication = async (applicationId: string) => {
    try {
      const response = await apiClient.POST(
        '/api/marketplace/applications/{application_id}/withdraw/',
        {
          params: { path: { application_id: applicationId } },
        }
      );

      if ((response as { error?: { message?: string } }).error) {
        throw new Error(
          (response as { error?: { message?: string } }).error!.message ||
            'Failed to withdraw application'
        );
      }

      // Clear application status since it's deleted
      setUserApplication(null);
      // Refresh applications
      await fetchApplications(true);
    } catch (err) {
      console.error('Failed to withdraw application:', err);
      toast({
        title: 'Failed to withdraw application',
        description: 'Please try again',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Share project
  const handleShareProject = () => {
    if (navigator.share) {
      navigator.share({
        title: project?.title || 'Marketplace Project',
        text: project?.description || '',
        url: window.location.href,
      });
    } else {
      navigator.clipboard.writeText(window.location.href);
    }
  };

  // Handle edit title
  const handleEditTitle = () => {
    setIsEditingTitle(true);
    setEditTitle(project?.title || '');
  };

  // Handle cancel title edit
  const handleCancelTitleEdit = () => {
    setIsEditingTitle(false);
    setEditTitle('');
  };

  // Handle edit description
  const handleEditDescription = () => {
    setIsEditingDescription(true);
    setEditDescription(project?.description || '');
  };

  // Handle cancel description edit
  const handleCancelDescriptionEdit = () => {
    setIsEditingDescription(false);
    setEditDescription('');
  };

  // Handle time range click - seek to the time range
  const handleTimeRangeClick = (
    timeRange: { start: number; end: number },
    mediaId?: string
  ) => {
    // Don't set waveform selection to avoid white highlight
    // setWaveformSelection(timeRange);

    if (mediaId) {
      // Use the provided mediaId
      playAudioAtTimeRange(timeRange, mediaId);
    } else {
      // Fallback: find the first audio media reference to play
      const audioMedia = Object.values(filteredMessagesByMedia).find(
        ({ media }) => media.media_type === 'audio'
      );

      if (audioMedia) {
        playAudioAtTimeRange(timeRange, audioMedia.media.id);
      }
    }
  };

  // Play audio at specific time range
  const playAudioAtTimeRange = (
    timeRange: { start: number; end: number },
    mediaId: string
  ) => {
    // First, pause all currently playing audio
    const pauseEvent = new CustomEvent('pauseAllAudio');
    window.dispatchEvent(pauseEvent);

    // Then play the requested audio
    // This will be handled by the waveform component
    // We'll dispatch a custom event that the waveform can listen to
    const event = new CustomEvent('playTimeRange', {
      detail: { timeRange, mediaId },
    });
    window.dispatchEvent(event);
  };

  // File upload handlers
  const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if (!files) return;

    const remainingSlots = 5 - attachedMedia.length;
    const filesToAdd = Array.from(files).slice(0, remainingSlots);

    filesToAdd.forEach((file) => {
      const url = URL.createObjectURL(file);
      const type = file.type.startsWith('audio/')
        ? ('audio' as const)
        : ('image' as const);
      const newMedia = {
        id: Math.random().toString(36).substr(2, 9),
        type,
        url,
        name: file.name,
      };
      setAttachedMedia((prev) => [...prev, newMedia]);
    });
  };

  const handleFileDrop = (e: React.DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    const files = e.dataTransfer.files;
    if (!files) return;

    const remainingSlots = 5 - attachedMedia.length;
    const filesToAdd = Array.from(files).slice(0, remainingSlots);

    filesToAdd.forEach((file) => {
      const url = URL.createObjectURL(file);
      const type = file.type.startsWith('audio/')
        ? ('audio' as const)
        : ('image' as const);
      const newMedia = {
        id: Math.random().toString(36).substr(2, 9),
        type,
        url,
        name: file.name,
      };
      setAttachedMedia((prev) => [...prev, newMedia]);
    });
  };

  const removeAttachedMedia = (id: string) => {
    setAttachedMedia((prev) => prev.filter((media) => media.id !== id));
  };

  // Organize messages by media
  const organizeMessagesByMedia = (
    messages: Message[],
    mediaRefs: MediaReference[]
  ) => {
    const organized: Record<
      string,
      { media: MediaReference; messages: Message[] }
    > = {};

    // First pass: organize messages that directly reference media
    mediaRefs.forEach((media) => {
      organized[media.id] = {
        media,
        messages: messages.filter((msg) =>
          (
            msg as { media_references?: Array<{ id: string }> }
          ).media_references?.some((mr: { id: string }) => mr.id === media.id)
        ),
      };
    });

    // Second pass: include nested comments (messages with parent_message_id)
    // that point to messages already in a media group
    messages.forEach((msg) => {
      const parentId = (msg as { parent_message_id?: string })
        .parent_message_id;
      if (!parentId) return; // Skip messages without parents

      // Find which media group contains the parent message
      for (const [mediaId, { messages: mediaMessages }] of Object.entries(
        organized
      )) {
        const parentExists = mediaMessages.some((m) => m.id === parentId);
        if (parentExists) {
          // Add this nested comment to the same media group as its parent
          // Check if it's not already included
          if (!organized[mediaId].messages.some((m) => m.id === msg.id)) {
            organized[mediaId].messages.push(msg);
          }
          break; // Found the parent, no need to check other media groups
        }
      }
    });

    // Third pass: if a nested comment has its own media reference,
    // also add it to that media group (so it appears under both parent's and its own media)
    messages.forEach((msg) => {
      const parentId = (msg as { parent_message_id?: string })
        .parent_message_id;
      if (!parentId) return; // Skip messages without parents (they're already in first pass)

      // Check if this message has its own media reference
      const msgMediaRefs = (msg as { media_references?: Array<{ id: string }> })
        .media_references;
      if (!msgMediaRefs || msgMediaRefs.length === 0) return;

      // Add this message to each of its own media references
      msgMediaRefs.forEach((mr: { id: string }) => {
        const mediaId = mr.id;
        if (organized[mediaId]) {
          // Check if it's not already included
          if (!organized[mediaId].messages.some((m) => m.id === msg.id)) {
            organized[mediaId].messages.push(msg);
          }
        }
      });
    });

    // Fourth pass: group messages without media references under the source track
    const sourceMedia = mediaRefs.find((m) => m.reference_type === 'source');
    if (sourceMedia) {
      // Ensure source media entry exists (it should from first pass, but be defensive)
      if (!organized[sourceMedia.id]) {
        organized[sourceMedia.id] = {
          media: sourceMedia,
          messages: [],
        };
      }

      // Helper function to check if a message has no media references
      const hasNoMediaRefs = (msg: Message) => {
        const msgMediaRefs = (
          msg as { media_references?: Array<{ id: string }> }
        ).media_references;
        return !msgMediaRefs || msgMediaRefs.length === 0;
      };

      // Helper function to check if a message is already included
      const isAlreadyIncluded = (msgId: string) => {
        return Object.values(organized).some(({ messages: msgs }) =>
          msgs.some((m) => m.id === msgId)
        );
      };

      // First, add root-level messages without media references
      messages.forEach((msg) => {
        const parentId = (msg as { parent_message_id?: string })
          .parent_message_id;
        if (parentId) return; // Skip replies for now

        if (hasNoMediaRefs(msg) && !isAlreadyIncluded(msg.id)) {
          organized[sourceMedia.id].messages.push(msg);
        }
      });

      // Then, add reply messages without media references whose parent is in source track
      // This needs to be done iteratively to handle nested replies
      let addedNewMessages = true;
      while (addedNewMessages) {
        addedNewMessages = false;
        messages.forEach((msg) => {
          const parentId = (msg as { parent_message_id?: string })
            .parent_message_id;
          if (!parentId) return; // Skip root messages

          if (hasNoMediaRefs(msg) && !isAlreadyIncluded(msg.id)) {
            // Check if parent is in source track
            const parentInSource = organized[sourceMedia.id].messages.some(
              (m) => m.id === parentId
            );
            if (parentInSource) {
              organized[sourceMedia.id].messages.push(msg);
              addedNewMessages = true;
            }
          }
        });
      }
    }

    return organized;
  };

  // Handle track message sending
  const handleSendTrackMessage = async (mediaId: string) => {
    if (!editMediaComment.trim()) return;

    try {
      // Get the time range for this specific track
      const trackTimeRange = trackTimeRanges[mediaId];

      // Send the message with the media reference
      const messageResponse = await apiClient.POST(
        '/api/marketplace/projects/{project_id}/messages/',
        {
          params: { path: { project_id: projectId } },
          body: {
            content: editMediaComment.trim(),
            media_reference_id: mediaId,
            // Attach the track-specific time range if available
            time_range: trackTimeRange || undefined,
          },
        }
      );

      if (messageResponse.data) {
        // Add the new message to the local state
        setMessages((prev) => [...prev, messageResponse.data]);

        // Update messagesByMedia to immediately show the new message
        setMessagesByMedia((prev) => {
          const newMessage = messageResponse.data;
          const trackMediaId = mediaId; // The media reference ID for this track

          // If this media already exists in messagesByMedia, add the message to it
          if (prev[trackMediaId]) {
            return {
              ...prev,
              [trackMediaId]: {
                ...prev[trackMediaId],
                messages: [...prev[trackMediaId].messages, newMessage],
              },
            };
          }

          // If this is a new media reference, we need to get its details
          // For now, we'll just add it to the general section
          return {
            ...prev,
            general: {
              ...prev.general,
              messages: [...prev.general.messages, newMessage],
            },
          };
        });

        // Clear the form
        setEditMediaComment('');
        setEditingMediaComment(null);
      }
    } catch (err) {
      console.error('Error sending track message:', err);
      const errorMessage =
        err instanceof Error ? err.message : 'Failed to send message';
      toast({
        title: 'Failed to send message',
        description: errorMessage,
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  // Initial data fetch
  useEffect(() => {
    if (projectId) {
      fetchProject();
    }
  }, [projectId, fetchProject]);

  // Fetch related data when project loads
  useEffect(() => {
    if (project) {
      fetchMessages();
      fetchMediaReferences();
      fetchApplications();
      fetchReviews();
      fetchTimeRanges();
    }
  }, [
    project,
    fetchMessages,
    fetchMediaReferences,
    fetchApplications,
    fetchReviews,
    fetchTimeRanges,
  ]);

  // Check user application only once per project
  const hasCheckedApplicationRef = useRef<string | null>(null);
  useEffect(() => {
    if (project && hasCheckedApplicationRef.current !== project.id) {
      hasCheckedApplicationRef.current = project.id;
      checkUserApplication();
    }
  }, [project, checkUserApplication]);

  // Fetch active projects count for fulfillers
  const fetchActiveProjectsCount = useCallback(async () => {
    if (!currentUser?.id) {
      return;
    }

    try {
      setIsLoadingActiveProjectsCount(true);
      const response: {
        data?: {
          active_projects_count: number;
          active_project_limit: number;
          can_apply: boolean;
        };
      } = (await apiClient.GET(
        '/api/marketplace/users/me/active-projects-count/'
      )) as any;

      if (response.data) {
        setActiveProjectsCount(response.data.active_projects_count);
        setActiveProjectLimit(response.data.active_project_limit);
      }
    } catch (err) {
      console.error('Failed to fetch active projects count:', err);
      // Don't show error toast, just set defaults
      setActiveProjectsCount(null);
      setActiveProjectLimit(1);
    } finally {
      setIsLoadingActiveProjectsCount(false);
    }
  }, [apiClient, currentUser?.id]);

  // Fetch active projects count when user is available
  useEffect(() => {
    if (currentUser?.id) {
      fetchActiveProjectsCount();
    }
  }, [currentUser?.id, fetchActiveProjectsCount]);

  // Update messages by media when messages or media change
  useEffect(() => {
    setMessagesByMedia(organizeMessagesByMedia(messages, mediaReferences));
  }, [messages, mediaReferences]);

  // Polling for updates
  useEffect(() => {
    if (!project) {
      return;
    }

    // Poll for updates when project is active (all states except completed)
    const shouldPoll = project.status !== 'COMPLETED';

    if (shouldPoll) {
      const pollForUpdates = async () => {
        try {
          // Fetch messages (silent = true to avoid loading states)
          await fetchMessages(true);

          // Only poll applications if project is still open
          if (project.status === 'OPEN') {
            await fetchApplications(true);
          }

          // Poll media references to detect new submissions/references
          if (project.status !== 'COMPLETED') {
            await fetchMediaReferences();
          }

          // Always poll reviews
          await fetchReviews(true);
        } catch (error) {
          console.error('Polling error:', error);
        }
      };

      // Do an immediate poll on mount
      pollForUpdates();

      // Poll every 5 seconds
      pollIntervalRef.current = setInterval(pollForUpdates, 5000);

      return () => {
        if (pollIntervalRef.current) {
          clearInterval(pollIntervalRef.current);
          pollIntervalRef.current = null;
        }
      };
    }
  }, [
    project,
    fetchMessages,
    fetchApplications,
    fetchReviews,
    fetchMediaReferences,
  ]);

  const value: MarketplaceProjectContextType = {
    // Project state
    project,
    isLoading,
    error,
    isCreator,

    // Messages state
    messages,
    messagesByMedia,
    isLoadingMessages,
    isSendingMessage,

    // Media state
    mediaReferences,
    isLoadingMedia,

    // Applications state
    applications,
    isLoadingApplications,
    userApplication,
    isCheckingApplication,

    // Reviews state
    reviews,
    isLoadingReviews,
    isSubmittingReview,

    // UI state
    selectedTimeRange,
    selectedMediaFilter,
    isAlertDismissed,
    viewMode,
    activeMediaId,
    setActiveMediaId,
    filterByMediaId,
    setFilterByMediaId,

    // Project editing state
    isEditingTitle,
    isEditingDescription,
    isSavingProject,
    isPublishing,
    editTitle,
    editDescription,

    // UI state
    showClipboardSuccess,
    showApplicationModal,

    // Media comment editing state
    editingMediaComment,
    editMediaComment,

    // Message editing state
    editingMessageId,
    editMessageContent,

    // File upload state
    attachedMedia,
    setAttachedMedia,

    // Time ranges state
    timeRanges,
    isLoadingTimeRanges,
    trackTimeRanges,
    setTrackTimeRanges,
    waveformSelection,
    setWaveformSelection,
    handleTimeRangeClick,
    playAudioAtTimeRange,

    // Comments state
    collapsedComments,
    setCollapsedComments,

    // Current playing time range state
    currentPlayingTimeRange,
    setCurrentPlayingTimeRange,

    // Filtered data
    filteredMessagesByMedia,

    // Active projects count
    activeProjectsCount,
    activeProjectLimit,
    canApply:
      activeProjectsCount !== null
        ? activeProjectsCount < activeProjectLimit
        : true, // Default to true if not loaded yet
    isLoadingActiveProjectsCount,

    // External dependencies
    projectId,
    apiClient,
    currentUser,
    setMessages,
    setMessagesByMedia,

    // Actions
    fetchProject,
    fetchMessages,
    fetchMediaReferences,
    fetchApplications,
    fetchReviews,
    checkUserApplication,
    fetchTimeRanges,

    // Message actions
    handleSendMessage,
    handleEditMessage,
    handleDeleteMessage,

    // Project actions
    handleSaveTitle,
    handleSaveDescription,
    handleSaveDeadline,
    handleDeleteProject,
    handleUnpublishProject,
    handlePublishProject,
    handleEditTitle,
    handleCancelTitleEdit,
    handleEditDescription,
    handleCancelDescriptionEdit,

    // UI actions
    setShowClipboardSuccess,
    setShowApplicationModal,
    setEditDescription,

    // Media actions
    handleDeleteMedia,
    handleSaveMediaTitle,
    handleSaveMediaDescription,
    handleAcceptSubmission,
    handleRejectSubmission,
    handleEditMediaComment,
    handleSendTrackMessage,
    organizeMessagesByMedia,

    // Application actions
    handleSubmitApplication,
    handleAcceptApplication,
    handleRejectApplication,
    handleWithdrawApplication,

    // File upload actions
    handleFileUpload,
    handleFileDrop,
    removeAttachedMedia,

    // Review actions
    handleSubmitReview,

    // Share action
    handleShareProject,

    // UI actions
    setSelectedTimeRange,
    setSelectedMediaFilter,
    setIsAlertDismissed,
    setViewMode,
    setEditingMessageId,
    setEditMessageContent,
    setEditingMediaComment,
    setEditMediaComment,
    setIsEditingTitle,
    setEditTitle,
  };

  return (
    <MarketplaceProjectContext.Provider value={value}>
      {children}
    </MarketplaceProjectContext.Provider>
  );
}
