import { useState, useEffect, useCallback, useRef } from 'react';
import { useApiRequest } from '../useApi';
import {
  type Workflow,
  type WorkflowMessage,
  type WorkflowLog,
  type StartWorkflowRequest,
  fetchWorkflow as fetchWorkflowApi,
  fetchChatData
} from '../../api/workflowApi';
import { useWorkflowOperations } from './useWorkflowOperations';
import { sortMessages, sortLogs } from './playgroundUtils';
import { useWorkflowPolling } from './useWorkflowPolling';

interface UnifiedChatDataItem {
  type: 'message' | 'log' | 'stat';
  item: WorkflowMessage | WorkflowLog | any;
  createdAt: number;
}

export function useWorkflowLifecycle() {
  const [workflowId, setWorkflowId] = useState<string | null>(null);
  const [workflowStatus, setWorkflowStatus] = useState<string>('idle');
  const [currentRound, setCurrentRound] = useState<number | undefined>(undefined);
  const [messages, setMessages] = useState<WorkflowMessage[]>([]);
  const [logs, setLogs] = useState<WorkflowLog[]>([]);
  const [dashboardLogs, setDashboardLogs] = useState<WorkflowLog[]>([]);
  const [unifiedContentLogs, setUnifiedContentLogs] = useState<WorkflowLog[]>([]);
  const [statusChangedFromRunningAt, setStatusChangedFromRunningAt] = useState<number | null>(null);
  const [latestStats, setLatestStats] = useState<{ priceUsd?: number; processingTime?: number; bytesSent?: number; bytesReceived?: number } | null>(null);
  const prevStatusRef = useRef<string>('idle');
  const statusRef = useRef<string>('idle');
  const statusChangedFromRunningAtRef = useRef<number | null>(null);
  const lastRenderedTimestampRef = useRef<number | null>(null);
  // Track processed stat IDs to avoid double-counting
  const processedStatIdsRef = useRef<Set<string>>(new Set());
  // Track cumulative stats
  const cumulativeStatsRef = useRef({ priceUsd: 0, processingTime: 0, bytesSent: 0, bytesReceived: 0 });
  const { startWorkflow, stopWorkflow, startingWorkflow, stoppingWorkflows } = useWorkflowOperations();
  const { request } = useApiRequest();
  const pollingController = useWorkflowPolling();
  
  // Store polling controller methods in refs to avoid dependency issues
  const pollingControllerRef = useRef(pollingController);
  pollingControllerRef.current = pollingController;
  
  // Helper to update status and track transitions
  const updateWorkflowStatus = useCallback((newStatus: string) => {
    const prevStatus = prevStatusRef.current;
    prevStatusRef.current = newStatus;
    statusRef.current = newStatus;
    setWorkflowStatus(newStatus);
    
    // Track when status changes from 'running' to something else
    if (prevStatus === 'running' && newStatus !== 'running') {
      const timestamp = Date.now();
      setStatusChangedFromRunningAt(timestamp);
      statusChangedFromRunningAtRef.current = timestamp;
    } else if (newStatus === 'running') {
      setStatusChangedFromRunningAt(null);
      statusChangedFromRunningAtRef.current = null;
    }
  }, []);
  
  // Expose setWorkflowStatus for optimistic updates
  const setWorkflowStatusOptimistic = useCallback((status: string) => {
    updateWorkflowStatus(status);
  }, [updateWorkflowStatus]);

  // Convert backend log format to frontend format
  const convertLogToFrontendFormat = useCallback((log: any): WorkflowLog => {
    return {
      id: log.id,
      workflowId: log.workflowId || workflowId || '',
      message: log.message || '',
      type: log.type || 'info',
      timestamp: log.timestamp || log.createdAt || Date.now(),
      status: log.status || 'running',
      progress: log.progress !== undefined && log.progress !== null ? log.progress : undefined,
      performance: log.performance,
      operationId: log.operationId || null,
      parentId: log.parentId || null
    };
  }, [workflowId]);

  // Process unified chat data chronologically
  const processUnifiedChatData = useCallback((chatData: { messages: WorkflowMessage[]; logs: WorkflowLog[]; stats: any[] }) => {
    console.log('🔄 Processing unified chat data:', {
      messages: chatData.messages?.length || 0,
      logs: chatData.logs?.length || 0,
      stats: chatData.stats?.length || 0
    });
    
    // Build unified timeline of all items
    const timeline: UnifiedChatDataItem[] = [];

    // Add messages
    (chatData.messages || []).forEach((message: WorkflowMessage) => {
      timeline.push({
        type: 'message',
        item: message,
        createdAt: message.publishedAt || message.timestamp || Date.now()
      });
    });

    // Add logs
    (chatData.logs || []).forEach((log: any) => {
      timeline.push({
        type: 'log',
        item: log,
        createdAt: log.timestamp || log.createdAt || Date.now()
      });
    });

    // Add stats (if needed)
    (chatData.stats || []).forEach((stat: any) => {
      timeline.push({
        type: 'stat',
        item: stat,
        createdAt: stat.timestamp || stat.createdAt || Date.now()
      });
    });

    console.log('📋 Timeline created with', timeline.length, 'items');

    // Sort chronologically
    timeline.sort((a, b) => a.createdAt - b.createdAt);

    // Process items sequentially to maintain chronological order
    // Update lastRenderedTimestamp after processing all items (use latest timestamp)
    if (timeline.length > 0) {
      const latestTimestamp = timeline[timeline.length - 1].createdAt;
      lastRenderedTimestampRef.current = latestTimestamp;
    }

    // Use functional updates to avoid dependency on current state
    setMessages(prevMessages => {
      const newMessages: WorkflowMessage[] = [...prevMessages];
      let hasChanges = false;
      let messagesAdded = 0;
      let messagesUpdated = 0;
      
      timeline.forEach((item) => {
        if (item.type === 'message') {
          const message = item.item as WorkflowMessage;
          
          if (!message || !message.id) {
            console.warn('⚠️ Invalid message in timeline:', message);
            return;
          }
          
          // Check if message already exists
          const existingIndex = newMessages.findIndex(m => m.id === message.id);
          if (existingIndex >= 0) {
            // Always update existing message (don't compare, just update)
            newMessages[existingIndex] = message;
            hasChanges = true;
            messagesUpdated++;
          } else {
            newMessages.push(message);
            hasChanges = true;
            messagesAdded++;
          }
        }
      });

      console.log(`📨 Messages: ${messagesAdded} added, ${messagesUpdated} updated, total: ${newMessages.length}`);
      if (messagesAdded > 0 || messagesUpdated > 0) {
        console.log('📨 Sample messages:', newMessages.slice(0, 3).map(m => ({ id: m.id, message: m.message?.substring(0, 50) })));
      }

      // Always return sorted array if we processed any messages
      if (hasChanges || timeline.some(item => item.type === 'message')) {
        const sorted = [...newMessages].sort(sortMessages);
        console.log(`✅ Returning ${sorted.length} sorted messages`);
        return sorted;
      }

      console.log('⚠️ No changes detected, returning previous messages');
      return prevMessages;
    });

    setDashboardLogs(prevDashboardLogs => {
      const newDashboardLogs: WorkflowLog[] = [...prevDashboardLogs];
      let hasChanges = false;
      
      timeline.forEach((item) => {
        if (item.type === 'log') {
          const backendLog = item.item as any;
          const frontendLog = convertLogToFrontendFormat(backendLog);

          // Route logs based on operationId
          if (frontendLog.operationId) {
            // Logs WITH operationId → Dashboard
            const existingIndex = newDashboardLogs.findIndex(l => l.id === frontendLog.id);
            if (existingIndex >= 0) {
              // Check if log actually changed
              const existingLog = newDashboardLogs[existingIndex];
              if (JSON.stringify(existingLog) !== JSON.stringify(frontendLog)) {
                newDashboardLogs[existingIndex] = frontendLog;
                hasChanges = true;
              }
            } else {
              newDashboardLogs.push(frontendLog);
              hasChanges = true;
            }
          }
        }
      });

      // Only return new array if there are changes
      if (!hasChanges) {
        return prevDashboardLogs;
      }

      return [...newDashboardLogs].sort(sortLogs);
    });

    setUnifiedContentLogs(prevUnifiedContentLogs => {
      const newUnifiedContentLogs: WorkflowLog[] = [...prevUnifiedContentLogs];
      let hasChanges = false;
      
      timeline.forEach((item) => {
        if (item.type === 'log') {
          const backendLog = item.item as any;
          const frontendLog = convertLogToFrontendFormat(backendLog);

          // Route logs based on operationId
          if (!frontendLog.operationId) {
            // Logs WITHOUT operationId → Unified Content Area
            const existingIndex = newUnifiedContentLogs.findIndex(l => l.id === frontendLog.id);
            if (existingIndex >= 0) {
              // Check if log actually changed
              const existingLog = newUnifiedContentLogs[existingIndex];
              if (JSON.stringify(existingLog) !== JSON.stringify(frontendLog)) {
                newUnifiedContentLogs[existingIndex] = frontendLog;
                hasChanges = true;
              }
            } else {
              newUnifiedContentLogs.push(frontendLog);
              hasChanges = true;
            }
          }
        }
      });

      // Only return new array if there are changes
      if (!hasChanges) {
        return prevUnifiedContentLogs;
      }

      return [...newUnifiedContentLogs].sort(sortLogs);
    });

    // Update combined logs for backward compatibility (using functional update)
    setLogs(prevLogs => {
      const allLogs: WorkflowLog[] = [...prevLogs];
      
      timeline.forEach((item) => {
        if (item.type === 'log') {
          const backendLog = item.item as any;
          const frontendLog = convertLogToFrontendFormat(backendLog);
          const existingIndex = allLogs.findIndex(l => l.id === frontendLog.id);
          if (existingIndex >= 0) {
            allLogs[existingIndex] = frontendLog;
          } else {
            allLogs.push(frontendLog);
          }
        }
      });

      return [...allLogs].sort(sortLogs);
    });

    // Process stats - aggregate only NEW stat entries (avoid double-counting)
    const statsItems = timeline.filter(item => item.type === 'stat');
    if (statsItems.length > 0) {
      let hasNewStats = false;
      
      statsItems.forEach(statItem => {
        const statData = statItem.item || statItem;
        const statId = statData?.id || statItem.id;
        
        // Skip if already processed
        if (statId && processedStatIdsRef.current.has(statId)) {
          return;
        }
        
        if (statData) {
          hasNewStats = true;
          
          // Mark as processed
          if (statId) {
            processedStatIdsRef.current.add(statId);
          }
          
          // Add to cumulative stats
          if (statData.priceUsd !== undefined && statData.priceUsd !== null) {
            cumulativeStatsRef.current.priceUsd += statData.priceUsd;
          }
          if (statData.processingTime !== undefined && statData.processingTime !== null) {
            cumulativeStatsRef.current.processingTime += statData.processingTime;
          }
          if (statData.bytesSent !== undefined && statData.bytesSent !== null) {
            cumulativeStatsRef.current.bytesSent += statData.bytesSent;
          }
          if (statData.bytesReceived !== undefined && statData.bytesReceived !== null) {
            cumulativeStatsRef.current.bytesReceived += statData.bytesReceived;
          }
        }
      });
      
      // Update state with cumulative totals
      if (hasNewStats || (cumulativeStatsRef.current.bytesSent > 0 || cumulativeStatsRef.current.bytesReceived > 0 || 
          cumulativeStatsRef.current.processingTime > 0 || cumulativeStatsRef.current.priceUsd > 0)) {
        setLatestStats({
          priceUsd: cumulativeStatsRef.current.priceUsd,
          processingTime: cumulativeStatsRef.current.processingTime,
          bytesSent: cumulativeStatsRef.current.bytesSent,
          bytesReceived: cumulativeStatsRef.current.bytesReceived
        });
      }
    }
  }, [convertLogToFrontendFormat]);

  // Poll workflow data using unified chat data endpoint
  const pollWorkflowData = useCallback(async (id: string) => {
    try {
      // Determine afterTimestamp for incremental polling
      const afterTimestamp = lastRenderedTimestampRef.current || undefined;

      // Fetch workflow status
      const workflowData = await fetchWorkflowApi(request, id).catch(() => null);
      
      if (workflowData) {
        const status = workflowData.status || 'idle';
        const round = workflowData.currentRound !== undefined ? workflowData.currentRound : undefined;
        
        updateWorkflowStatus(status);
        setCurrentRound(round);
      }

      // Fetch unified chat data
      const chatData = await fetchChatData(request, id, afterTimestamp);
      
      console.log('📊 Processed chat data:', {
        messagesCount: chatData.messages?.length || 0,
        logsCount: chatData.logs?.length || 0,
        statsCount: chatData.stats?.length || 0,
        afterTimestamp: afterTimestamp
      });
      
      // If we got empty results and we're using afterTimestamp, the backend might be filtering incorrectly
      // Reset timestamp to null so next poll fetches all items (but only if we have existing data)
      const hasNoNewData = (chatData.messages?.length || 0) === 0 && 
                           (chatData.logs?.length || 0) === 0 && 
                           (chatData.stats?.length || 0) === 0;
      
      // Only reset if we're using afterTimestamp and got empty results
      // This handles cases where backend filtering might miss items due to timestamp precision issues
      if (hasNoNewData && afterTimestamp !== undefined && lastRenderedTimestampRef.current !== null) {
        console.warn('⚠️ Got empty results with afterTimestamp, resetting timestamp for next poll');
        // Don't reset immediately - let this poll complete, next poll will fetch all
        lastRenderedTimestampRef.current = null;
      }
      
      // Process unified chat data
      processUnifiedChatData(chatData);

      // Determine if polling should continue
      const currentStatus = statusRef.current;
      
      // Stop polling immediately for failed or stopped workflows
      // For completed workflows, allow grace period (handled by useEffect)
      if (currentStatus === 'failed' || currentStatus === 'stopped') {
        pollingControllerRef.current.stopPolling();
        return;
      }

      // Continue polling for 'running' status
      // For 'completed' status, continue if within grace period (handled by useEffect)
      // Polling will be stopped by the useEffect when grace period expires or status changes to failed/stopped
    } catch (error: any) {
      // Handle rate limiting (429 errors)
      if (error?.status === 429 || error?.response?.status === 429) {
        throw error; // Let polling controller handle rate limit backoff
      }
      console.error('Error polling workflow data:', error);
      // Don't throw for other errors - allow polling to continue with backoff
    }
  }, [request, updateWorkflowStatus, processUnifiedChatData]);

  // Load initial workflow data (non-polling)
  const _loadWorkflowData = useCallback(async (id: string) => {
    try {
      const workflowData = await fetchWorkflowApi(request, id).catch(() => null);
      
      if (!workflowData) {
        setMessages([]);
        setLogs([]);
        setDashboardLogs([]);
        setUnifiedContentLogs([]);
        setLatestStats(null);
        // Reset stats tracking
        processedStatIdsRef.current.clear();
        cumulativeStatsRef.current = { priceUsd: 0, processingTime: 0, bytesSent: 0, bytesReceived: 0 };
        return;
      }
      
      const messagesData = Array.isArray(workflowData.messages) ? workflowData.messages : [];
      const logsData = Array.isArray(workflowData.logs) ? workflowData.logs : [];
      const status = workflowData.status || 'idle';
      const round = workflowData.currentRound !== undefined ? workflowData.currentRound : undefined;
      
      updateWorkflowStatus(status);
      setCurrentRound(round);
      
      // Always fetch unified chat data to get all messages and logs
      // Reset lastRenderedTimestamp to fetch all historical data
      lastRenderedTimestampRef.current = null;
      try {
        const chatData = await fetchChatData(request, id, undefined);
        console.log('📥 loadWorkflowData: Fetched unified chat data:', {
          messagesCount: chatData.messages?.length || 0,
          logsCount: chatData.logs?.length || 0
        });
        processUnifiedChatData(chatData);
      } catch (error) {
        console.warn('⚠️ Failed to fetch unified chat data, falling back to workflowData:', error);
        // Fallback to workflowData if unified chat data fails
        if (messagesData.length > 0) {
          setMessages([...messagesData].sort(sortMessages));
        }
        
        // Process logs and separate by operationId
        const dashboardLogsList: WorkflowLog[] = [];
        const unifiedContentLogsList: WorkflowLog[] = [];
        
        logsData.forEach((log: any) => {
          const frontendLog = convertLogToFrontendFormat(log);
          if (frontendLog.operationId) {
            dashboardLogsList.push(frontendLog);
          } else {
            unifiedContentLogsList.push(frontendLog);
          }
        });

        setDashboardLogs(dashboardLogsList.sort(sortLogs));
        setUnifiedContentLogs(unifiedContentLogsList.sort(sortLogs));
        setLogs([...dashboardLogsList, ...unifiedContentLogsList].sort(sortLogs));
      }
    } catch (error) {
      console.error('Error loading workflow data:', error);
    }
  }, [request, updateWorkflowStatus, convertLogToFrontendFormat, processUnifiedChatData]);
  void _loadWorkflowData; // Intentionally unused, reserved for future use

  // Set up polling when workflow is running
  useEffect(() => {
    if (!workflowId) {
      // Only clear state if not already cleared to avoid unnecessary updates
      setMessages(prev => prev.length > 0 ? [] : prev);
      setLogs(prev => prev.length > 0 ? [] : prev);
      setDashboardLogs(prev => prev.length > 0 ? [] : prev);
      setUnifiedContentLogs(prev => prev.length > 0 ? [] : prev);
      setLatestStats(null);
      // Reset stats tracking
      processedStatIdsRef.current.clear();
      cumulativeStatsRef.current = { priceUsd: 0, processingTime: 0, bytesSent: 0, bytesReceived: 0 };
      setCurrentRound(prev => prev !== undefined ? undefined : prev);
      if (statusChangedFromRunningAt !== null) {
        setStatusChangedFromRunningAt(null);
        statusChangedFromRunningAtRef.current = null;
      }
      lastRenderedTimestampRef.current = null;
      pollingControllerRef.current.stopPolling();
      return;
    }

    // Continue polling if:
    // 1. Workflow is currently running, OR
    // 2. Workflow just completed (within last 10 seconds) - grace period to catch final messages
    // Stop polling for failed or stopped workflows immediately
    // Use ref for statusChangedFromRunningAt to get latest value (state updates are async)
    const changedAtRef = statusChangedFromRunningAtRef.current;
    const shouldPoll = workflowStatus === 'running' || 
      (workflowStatus === 'completed' && changedAtRef !== null && Date.now() - changedAtRef < 10000);

    if (shouldPoll) {
      // Reset lastRenderedTimestamp for first poll (fetch all historical data)
      if (lastRenderedTimestampRef.current === null) {
        lastRenderedTimestampRef.current = null; // null means fetch all
      }

      // Start polling
      pollingControllerRef.current.startPolling(workflowId, pollWorkflowData);
    } else {
      // Stop polling for failed, stopped, or completed (after grace period) workflows
      pollingControllerRef.current.stopPolling();
      // Clear the status change timestamp when we stop polling (only if not already null)
      if (statusChangedFromRunningAt !== null) {
        setStatusChangedFromRunningAt(null);
        statusChangedFromRunningAtRef.current = null;
      }
    }

    return () => {
      pollingControllerRef.current.stopPolling();
    };
  }, [workflowStatus, workflowId, pollWorkflowData]);
  
  const handleStartWorkflow = useCallback(async (
    workflowData: StartWorkflowRequest,
    options?: { workflowId?: string; workflowMode?: 'Dynamic' | 'Automation' }
  ) => {
    try {
      const result = await startWorkflow(workflowData, options);
      
      if (result.success && result.data) {
        const workflow = result.data as Workflow;
        setWorkflowId(workflow.id);
        updateWorkflowStatus(workflow.status || 'running');
        // Reset lastRenderedTimestamp for new workflow
        lastRenderedTimestampRef.current = null;
        return { success: true, data: result.data };
      } else {
        return { success: false, error: result.error || 'Failed to start workflow' };
      }
    } catch (error: any) {
      return { success: false, error: error.message || 'Failed to start workflow' };
    }
  }, [startWorkflow, updateWorkflowStatus]);
  
  const handleStopWorkflow = useCallback(async () => {
    if (!workflowId) {
      return { success: false, error: 'No workflow to stop' };
    }
    
    try {
      const result = await stopWorkflow(workflowId);
      
      if (result.success) {
        updateWorkflowStatus('stopped');
        return { success: true };
      } else {
        return { success: false, error: result.error || 'Failed to stop workflow' };
      }
    } catch (error: any) {
      return { success: false, error: error.message || 'Failed to stop workflow' };
    }
  }, [workflowId, stopWorkflow, updateWorkflowStatus]);
  
  const resetWorkflow = useCallback(() => {
    setWorkflowId(null);
    prevStatusRef.current = 'idle';
    statusRef.current = 'idle';
    updateWorkflowStatus('idle');
    setCurrentRound(undefined);
    setLatestStats(null);
    // Reset stats tracking
    processedStatIdsRef.current.clear();
    cumulativeStatsRef.current = { priceUsd: 0, processingTime: 0, bytesSent: 0, bytesReceived: 0 };
    setStatusChangedFromRunningAt(null);
    statusChangedFromRunningAtRef.current = null;
    lastRenderedTimestampRef.current = null;
    pollingControllerRef.current.stopPolling();
  }, [updateWorkflowStatus]);
  
  const selectWorkflow = useCallback(async (workflowIdToSelect: string) => {
    try {
      setWorkflowId(workflowIdToSelect);
      // Reset lastRenderedTimestamp and stats for new workflow selection
      lastRenderedTimestampRef.current = null;
      processedStatIdsRef.current.clear();
      cumulativeStatsRef.current = { priceUsd: 0, processingTime: 0, bytesSent: 0, bytesReceived: 0 };
      
      const workflowData = await fetchWorkflowApi(request, workflowIdToSelect).catch(() => null);
      
      if (!workflowData) {
        setMessages([]);
        setLogs([]);
        setDashboardLogs([]);
        setUnifiedContentLogs([]);
        setLatestStats(null);
        updateWorkflowStatus('idle');
        return;
      }
      
      const messagesData = Array.isArray(workflowData.messages) ? workflowData.messages : [];
      const logsData = Array.isArray(workflowData.logs) ? workflowData.logs : [];
      const status = workflowData.status || 'idle';
      const round = workflowData.currentRound !== undefined ? workflowData.currentRound : undefined;
      
      updateWorkflowStatus(status);
      setCurrentRound(round);
      
      // Always fetch unified chat data to get all messages and logs (regardless of status)
      // This ensures completed workflows also show their logs
      try {
        const chatData = await fetchChatData(request, workflowIdToSelect, undefined);
        console.log('📥 selectWorkflow: Fetched unified chat data:', {
          messagesCount: chatData.messages?.length || 0,
          logsCount: chatData.logs?.length || 0,
          status
        });
        processUnifiedChatData(chatData);
      } catch (error) {
        console.warn('⚠️ Failed to fetch unified chat data, falling back to workflowData:', error);
        // Fallback to workflowData if unified chat data fails
        if (messagesData.length > 0) {
          setMessages([...messagesData].sort(sortMessages));
        }
        
        // Process logs and separate by operationId
        const dashboardLogsList: WorkflowLog[] = [];
        const unifiedContentLogsList: WorkflowLog[] = [];
        
        logsData.forEach((log: any) => {
          const frontendLog = convertLogToFrontendFormat(log);
          if (frontendLog.operationId) {
            dashboardLogsList.push(frontendLog);
          } else {
            unifiedContentLogsList.push(frontendLog);
          }
        });

        setDashboardLogs(dashboardLogsList.sort(sortLogs));
        setUnifiedContentLogs(unifiedContentLogsList.sort(sortLogs));
        setLogs([...dashboardLogsList, ...unifiedContentLogsList].sort(sortLogs));
      }
      
      // If workflow is running, polling will start automatically via useEffect
    } catch (error) {
      console.error('Error selecting workflow:', error);
    }
  }, [request, updateWorkflowStatus, convertLogToFrontendFormat, processUnifiedChatData]);
  
  const isRunning = workflowStatus === 'running';
  const isStopping = workflowId ? stoppingWorkflows.has(workflowId) : false;
  
  return {
    workflowId,
    workflowStatus,
    currentRound,
    isRunning,
    isStopping,
    startingWorkflow,
    messages,
    logs,
    dashboardLogs,
    unifiedContentLogs,
    latestStats,
    startWorkflow: handleStartWorkflow,
    stopWorkflow: handleStopWorkflow,
    resetWorkflow,
    selectWorkflow,
    setWorkflowStatusOptimistic
  };
}
