import { useState, useCallback, useRef } from 'react';
import { WorkflowLog } from '../../components/UiComponents/Log/LogTypes';

interface OperationData {
  logs: Map<string, WorkflowLog>;
  parentId: string | null;
  expanded: boolean;
  latestProgress: number | null;
  latestStatus: string | null;
  operationName: string | null; // Stable name from first log
  latestMessage: string | null; // Latest status message that updates
  roundNumber: number | null; // Track which round this operation belongs to
}

interface RoundData {
  operations: Map<string, OperationData>;
  rootOperations: string[];
  expanded: boolean;
  isCompleted: boolean;
}

interface DashboardLogTree {
  operations: Map<string, OperationData>;
  rootOperations: string[];
  logExpandedStates: Map<string, boolean>;
  currentRound: number | null;
  rounds: Map<number, RoundData>;
}

export function useDashboardLogTree() {
  const [tree, setTree] = useState<DashboardLogTree>({
    operations: new Map(),
    rootOperations: [],
    logExpandedStates: new Map(),
    currentRound: null,
    rounds: new Map()
  });

  const treeRef = useRef<DashboardLogTree>(tree);
  treeRef.current = tree;

  const generateLogId = useCallback((log: WorkflowLog): string => {
    if (log.id) {
      return log.id;
    }
    return `log_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
  }, []);

  const processDashboardLogs = useCallback((logs: WorkflowLog[]) => {
    setTree(prevTree => {
      const newTree: DashboardLogTree = {
        operations: new Map(prevTree.operations),
        rootOperations: [...prevTree.rootOperations],
        logExpandedStates: new Map(prevTree.logExpandedStates),
        currentRound: prevTree.currentRound,
        rounds: new Map(prevTree.rounds)
      };

      // Process each log
      logs.forEach(log => {
        if (!log.operationId) {
          return; // Skip logs without operationId
        }

        const operationId = log.operationId;
        const logId = generateLogId(log);
        const logRoundNumber = (log as any).roundNumber as number | null | undefined;

        // Update current round tracking
        if (logRoundNumber !== null && logRoundNumber !== undefined) {
          if (newTree.currentRound === null || logRoundNumber > newTree.currentRound) {
            newTree.currentRound = logRoundNumber;
          }
        }

        // Get or create operation
        const existingOperation = newTree.operations.get(operationId);
        
        // Create new logs Map (copy existing logs if updating)
        const logsMap = existingOperation 
          ? new Map(existingOperation.logs)
          : new Map();
        
        // Store log (Map ensures uniqueness by logId)
        logsMap.set(logId, log);

        // Determine stable operation name (only set once, never change)
        // Always use formatted operationId as the stable name - don't use log messages
        // Log messages are status updates and should go in latestMessage, not operationName
        let operationName = existingOperation?.operationName || null;
        if (operationName === null) {
          // Remove UUIDs and timestamps from operationId before formatting
          // UUID pattern: 8-4-4-4-12 hex digits (e.g., "1e6d7b14-4f30-40e2-b7a6-748b63b6a7f5")
          // Also remove standalone long hex strings that might be timestamps or IDs
          let cleanedId = operationId
            .replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '') // Remove UUIDs
            .replace(/\b[0-9a-f]{32,}\b/gi, '') // Remove long hex strings (timestamps/IDs)
            .replace(/\s+/g, ' ') // Normalize whitespace
            .trim();
          
          // Format by splitting on dashes/underscores and capitalizing
          // This creates a stable, readable name like "Workflow Planning" from "workflow-planning"
          const formattedName = cleanedId
            .split(/[-_\s]+/)
            .filter(word => word.length > 0) // Remove empty strings
            .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
            .join(' ');
          operationName = formattedName || operationId.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '').trim();
        }

        // Update latest message (for status tag) - this updates with each poll
        const latestMessage = log.message || existingOperation?.latestMessage || null;

        // Update parentId if not set yet (from first log entry)
        const parentId = existingOperation?.parentId !== null && existingOperation?.parentId !== undefined
          ? existingOperation.parentId
          : (log.parentId !== undefined && log.parentId !== null ? log.parentId : null);

        // Update latest progress (use latest value)
        const latestProgress = log.progress !== undefined && log.progress !== null
          ? log.progress
          : existingOperation?.latestProgress ?? null;

        // Update latest status (use latest value)
        const latestStatus = log.status !== undefined && log.status !== null
          ? log.status
          : existingOperation?.latestStatus ?? null;
        
        // Get round number for this operation (from log or existing)
        const roundNumber = logRoundNumber !== null && logRoundNumber !== undefined
          ? logRoundNumber
          : existingOperation?.roundNumber ?? null;

        // Create new operation object to ensure React detects the change
        const operation: OperationData = {
          logs: logsMap,
          parentId,
          expanded: existingOperation?.expanded ?? false,
          latestProgress,
          latestStatus,
          operationName,
          latestMessage,
          roundNumber
        };

        newTree.operations.set(operationId, operation);
        
        // Add operation to its round
        if (roundNumber !== null) {
          if (!newTree.rounds.has(roundNumber)) {
            newTree.rounds.set(roundNumber, {
              operations: new Map(),
              rootOperations: [],
              expanded: true, // New rounds start expanded
              isCompleted: false
            });
          }
          const round = newTree.rounds.get(roundNumber)!;
          round.operations.set(operationId, operation);
        }
      });

      // Rebuild root operations list per round
      newTree.rounds.forEach((round, roundNumber) => {
        const rootOpsSet = new Set<string>();
        round.operations.forEach((op, opId) => {
          if (op.parentId === null) {
            rootOpsSet.add(opId);
          } else {
            // Check if parent is in a different round - then this is a root in THIS round
            const parentOp = newTree.operations.get(op.parentId);
            if (!parentOp || parentOp.roundNumber !== roundNumber) {
              rootOpsSet.add(opId);
            }
          }
        });
        
        // Sort by timestamp
        round.rootOperations = Array.from(rootOpsSet).sort((opIdA, opIdB) => {
          const opA = round.operations.get(opIdA);
          const opB = round.operations.get(opIdB);
          if (!opA || !opB) return 0;
          
          const logsA = Array.from(opA.logs.values());
          const logsB = Array.from(opB.logs.values());
          
          if (logsA.length === 0 && logsB.length === 0) return 0;
          if (logsA.length === 0) return 1;
          if (logsB.length === 0) return -1;
          
          const earliestA = Math.min(...logsA.map(log => log.timestamp || 0));
          const earliestB = Math.min(...logsB.map(log => log.timestamp || 0));
          
          return earliestA - earliestB;
        });
        
        // Update completion status
        const allOpsCompleted = Array.from(round.operations.values()).every(op => 
          op.latestStatus === 'completed' || op.latestStatus === 'success'
        );
        round.isCompleted = allOpsCompleted;
        
        // Auto-collapse completed rounds (except current)
        if (round.isCompleted && roundNumber !== newTree.currentRound) {
          round.expanded = false;
        }
      });

      // Rebuild global root operations list (operations without parentId)
      const rootOpsSet = new Set<string>();
      newTree.operations.forEach((op, opId) => {
        if (op.parentId === null) {
          rootOpsSet.add(opId);
        }
      });
      // Sort by timestamp of earliest log entry (chronological order)
      newTree.rootOperations = Array.from(rootOpsSet).sort((opIdA, opIdB) => {
        const opA = newTree.operations.get(opIdA);
        const opB = newTree.operations.get(opIdB);
        if (!opA || !opB) return 0;
        
        const logsA = Array.from(opA.logs.values());
        const logsB = Array.from(opB.logs.values());
        
        if (logsA.length === 0 && logsB.length === 0) return 0;
        if (logsA.length === 0) return 1;
        if (logsB.length === 0) return -1;
        
        const earliestA = Math.min(...logsA.map(log => log.timestamp || 0));
        const earliestB = Math.min(...logsB.map(log => log.timestamp || 0));
        
        return earliestA - earliestB;
      });

      return newTree;
    });
  }, [generateLogId]);

  const clearDashboard = useCallback((resetRound: boolean = false) => {
    setTree({
      operations: new Map(),
      rootOperations: [],
      logExpandedStates: new Map(),
      currentRound: resetRound ? null : treeRef.current.currentRound,
      rounds: new Map()
    });
  }, []);

  const toggleOperationExpanded = useCallback((operationId: string) => {
    setTree(prevTree => {
      const operation = prevTree.operations.get(operationId);
      if (!operation) {
        return prevTree;
      }

      const newTree: DashboardLogTree = {
        ...prevTree,
        operations: new Map(prevTree.operations)
      };

      const updatedOperation = {
        ...operation,
        expanded: !operation.expanded
      };

      newTree.operations.set(operationId, updatedOperation);

      return newTree;
    });
  }, []);

  const updateCurrentRound = useCallback((round: number | null) => {
    setTree(prevTree => {
      // Only update current round, keep all rounds data
      // Auto-collapse previous rounds when new round starts
      if (prevTree.currentRound !== null && round !== null && prevTree.currentRound !== round) {
        const newRounds = new Map(prevTree.rounds);
        
        // Collapse the old current round
        const oldRound = newRounds.get(prevTree.currentRound);
        if (oldRound) {
          newRounds.set(prevTree.currentRound, {
            ...oldRound,
            expanded: false
          });
        }
        
        return {
          ...prevTree,
          currentRound: round,
          rounds: newRounds
        };
      }

      return {
        ...prevTree,
        currentRound: round
      };
    });
  }, []);
  
  const toggleRoundExpanded = useCallback((roundNumber: number) => {
    setTree(prevTree => {
      const round = prevTree.rounds.get(roundNumber);
      if (!round) {
        return prevTree;
      }

      const newRounds = new Map(prevTree.rounds);
      newRounds.set(roundNumber, {
        ...round,
        expanded: !round.expanded
      });

      return {
        ...prevTree,
        rounds: newRounds
      };
    });
  }, []);

  const getChildOperations = useCallback((parentId: string | null): string[] => {
    const currentTree = treeRef.current;
    const childOps = Array.from(currentTree.operations.entries())
      .filter(([_, op]) => op.parentId === parentId)
      .map(([opId, op]) => ({ opId, op }));
    
    // Sort by timestamp of earliest log entry (chronological order)
    return childOps.sort((a, b) => {
      const logsA = Array.from(a.op.logs.values());
      const logsB = Array.from(b.op.logs.values());
      
      if (logsA.length === 0 && logsB.length === 0) return 0;
      if (logsA.length === 0) return 1; // Put operations without logs at the end
      if (logsB.length === 0) return -1;
      
      const earliestA = Math.min(...logsA.map(log => log.timestamp || 0));
      const earliestB = Math.min(...logsB.map(log => log.timestamp || 0));
      
      return earliestA - earliestB; // Ascending order (oldest first)
    }).map(({ opId }) => opId);
  }, []);

  return {
    tree,
    processDashboardLogs,
    clearDashboard,
    toggleOperationExpanded,
    toggleRoundExpanded,
    updateCurrentRound,
    getChildOperations
  };
}

