import { useCallback, useEffect, useRef, useState } from 'react';
import { backtestApiV2 } from '../services/backtestApiV2';
import type { Backtest, Run, RunResult, CreateBacktestRequest } from '../services/backtestApiV2';

interface UseBacktestV2Return {
  // State
  backtest: Backtest | null;
  runs: Run[];
  currentRun: Run | null;
  runResults: RunResult | null;
  isLoading: boolean;
  error: string | null;

  // Actions
  loadBacktest: (id: string) => Promise<void>;
  createBacktest: (request: CreateBacktestRequest) => Promise<Backtest>;
  updateBacktest: (id: string, updates: Partial<CreateBacktestRequest>) => Promise<void>;
  deleteBacktest: (id: string) => Promise<void>;
  
  createRun: (speedMultiplier?: number | null) => Promise<Run | undefined>;
  pauseRun: () => Promise<void>;
  resumeRun: () => Promise<void>;
  cancelRun: () => Promise<void>;
  updateRunSpeed: (speedMultiplier: number | null) => Promise<void>;
  selectRun: (runId: string | undefined) => Promise<void>;
}

export function useBacktestV2(): UseBacktestV2Return {
  const [backtest, setBacktest] = useState<Backtest | null>(null);
  const [runs, setRuns] = useState<Run[]>([]);
  const [currentRun, setCurrentRun] = useState<Run | null>(null);
  const [runResults, setRunResults] = useState<RunResult | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const pollingIntervalRef = useRef<NodeJS.Timeout | null>(null);

  // Load a specific backtest and its runs
  const loadBacktest = useCallback(async (id: string) => {
    setIsLoading(true);
    setError(null);
    
    try {
      const [loadedBacktest, loadedRuns] = await Promise.all([
        backtestApiV2.getBacktest(id),
        backtestApiV2.listRuns(id)
      ]);
      
      setBacktest(loadedBacktest);
      setRuns(loadedRuns);
      
      // Don't auto-select runs here - let the URL parameter drive the selection
    } catch (err) {
      console.error('Failed to load backtest:', err);
      setError(err instanceof Error ? err.message : 'Failed to load backtest');
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Create a new backtest
  const createBacktest = useCallback(async (request: CreateBacktestRequest): Promise<Backtest> => {
    setIsLoading(true);
    setError(null);
    
    try {
      const newBacktest = await backtestApiV2.createBacktest(request);
      setBacktest(newBacktest);
      setRuns([]);
      setCurrentRun(null);
      setRunResults(null);
      return newBacktest;
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to create backtest');
      throw err;
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Update backtest configuration
  const updateBacktest = useCallback(async (id: string, updates: Partial<CreateBacktestRequest>) => {
    setIsLoading(true);
    setError(null);
    
    try {
      const updatedBacktest = await backtestApiV2.updateBacktest(id, updates);
      setBacktest(updatedBacktest);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to update backtest');
      throw err;
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Delete backtest
  const deleteBacktest = useCallback(async (id: string) => {
    setIsLoading(true);
    setError(null);
    
    try {
      await backtestApiV2.deleteBacktest(id);
      setBacktest(null);
      setRuns([]);
      setCurrentRun(null);
      setRunResults(null);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to delete backtest');
      throw err;
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Create a new run
  const createRun = useCallback(async (speedMultiplier?: number | null) => {
    if (!backtest) return;
    
    setIsLoading(true);
    setError(null);
    setRunResults(null);
    
    try {
      // Pass speedMultiplier as-is, null means max speed
      const newRun = await backtestApiV2.createRun(backtest.id, { 
        speedMultiplier: speedMultiplier === undefined ? null : speedMultiplier
      });
      
      // Add to runs array first
      setRuns(prevRuns => [newRun, ...prevRuns]);
      
      // Clear previous results immediately
      setRunResults(null);
      
      // Set as current run
      setCurrentRun(newRun);
      
      // Small delay to ensure the run is properly created in the database
      await new Promise(resolve => setTimeout(resolve, 100));
      
      // Check if the run is completed (it might complete instantly)
      let finalRun = newRun;
      
      // If status is pending, check again after a short delay
      if (newRun.status === 'pending') {
        await new Promise(resolve => setTimeout(resolve, 500));
        try {
          finalRun = await backtestApiV2.getRun(newRun.id);
          // Update the current run with the latest status
          setCurrentRun(finalRun);
          setRuns(prevRuns => 
            prevRuns.map(r => r.id === finalRun.id ? finalRun : r)
          );
        } catch (err) {
          console.error('Failed to re-check run status:', err);
        }
      }
      
      // If the run is completed, load results
      if (finalRun.status === 'completed') {
        try {
          const results = await backtestApiV2.getRunResults(finalRun.id);
          setRunResults(results);
        } catch (err) {
          console.error('Failed to load new run results:', err);
        }
      } else {
        // Start monitoring the run
        startMonitoringRun(finalRun.id);
      }
      
      return newRun; // Return the created run
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to create run');
      throw err; // Re-throw so caller knows it failed
    } finally {
      setIsLoading(false);
    }
  }, [backtest]);

  // Run control actions
  const pauseRun = useCallback(async () => {
    if (!currentRun || currentRun.status !== 'running') return;
    
    try {
      await backtestApiV2.pauseRun(currentRun.id);
      setCurrentRun(prev => prev ? { ...prev, status: 'paused' } : null);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to pause run');
    }
  }, [currentRun]);

  const resumeRun = useCallback(async () => {
    if (!currentRun || currentRun.status !== 'paused') return;
    
    try {
      await backtestApiV2.resumeRun(currentRun.id);
      setCurrentRun(prev => prev ? { ...prev, status: 'running' } : null);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to resume run');
    }
  }, [currentRun]);

  const cancelRun = useCallback(async () => {
    if (!currentRun || (currentRun.status !== 'running' && currentRun.status !== 'paused')) return;
    
    try {
      await backtestApiV2.cancelRun(currentRun.id);
      setCurrentRun(prev => prev ? { ...prev, status: 'cancelled' } : null);
      stopMonitoringRun();
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to cancel run');
    }
  }, [currentRun]);

  const updateRunSpeed = useCallback(async (speedMultiplier: number | null) => {
    if (!currentRun) return;
    
    try {
      // Keep null as null for max speed (no limit)
      const apiSpeed = speedMultiplier;
      
      // Only call API if run is active
      if (currentRun.status === 'running' || currentRun.status === 'paused') {
        await backtestApiV2.updateRunSpeed(currentRun.id, apiSpeed);
      }
      
      // Always update local state so UI reflects the selection
      setCurrentRun(prev => prev ? { ...prev, speedMultiplier: apiSpeed } : null);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to update run speed');
    }
  }, [currentRun]);

  // Select a specific run
  const selectRun = useCallback(async (runId: string | undefined) => {
    if (!runId) {
      setCurrentRun(null);
      setRunResults(null);
      stopMonitoringRun();
      return;
    }
    
    let run = runs.find(r => r.id === runId);
    
    // If run not found in list, try to fetch it directly
    if (!run) {
      let attempts = 0;
      const maxAttempts = 3;
      
      while (attempts < maxAttempts) {
        try {
          run = await backtestApiV2.getRun(runId);
          
          // Add to runs list if not present
          setRuns(prevRuns => {
            const exists = prevRuns.some(r => r.id === run!.id);
            if (!exists) {
              return [run!, ...prevRuns];
            }
            return prevRuns;
          });
          
          break; // Success, exit loop
        } catch (err) {
          attempts++;
          if (attempts >= maxAttempts) {
            console.error('Failed to fetch run after all attempts:', runId, err);
            setError(`Failed to load run ${runId}`);
            return;
          }
          // Wait before retrying
          await new Promise(resolve => setTimeout(resolve, 1000));
        }
      }
    }
    
    setCurrentRun(run);
    setRunResults(null);
    
    if (run.status === 'completed') {
      try {
        const results = await backtestApiV2.getRunResults(run.id);
        setRunResults(results);
      } catch (err) {
        console.error('Failed to load run results:', err);
        setError(err instanceof Error ? err.message : 'Failed to load run results');
      }
    } else if (run.status === 'running' || run.status === 'paused' || run.status === 'pending') {
      startMonitoringRun(run.id);
    } else {
      console.warn('Run has unexpected status:', run.status);
    }
  }, [runs]);

  // Monitor run progress
  const startMonitoringRun = useCallback((runId: string) => {
    // Stop any existing monitoring
    stopMonitoringRun();
    
    // For pending runs, we need to poll until they start or complete
    // We'll check the run status by fetching it
    pollingIntervalRef.current = setInterval(async () => {
        try {
          const updatedRun = await backtestApiV2.getRun(runId);
          // Update the run in state
          setCurrentRun(updatedRun);
          setRuns(prevRuns => 
            prevRuns.map(r => r.id === updatedRun.id ? updatedRun : r)
          );
          
          // If run is no longer pending, handle the new status
          if (updatedRun.status === 'completed') {
            stopMonitoringRun();
            try {
              const results = await backtestApiV2.getRunResults(runId);
              setRunResults(results);
            } catch (err) {
              console.error('Failed to load completed run results:', err);
            }
          } else if (updatedRun.status === 'failed' || updatedRun.status === 'cancelled') {
            stopMonitoringRun();
          }
        } catch (err) {
          console.error('Failed to poll run status:', err);
        }
      }, 1000); // Poll every second
  }, []);

  const startPollingRun = (runId: string) => {
    pollingIntervalRef.current = setInterval(async () => {
      try {
        const updatedRun = await backtestApiV2.getRun(runId);
        handleRunUpdate(updatedRun);
        
        if (updatedRun.status === 'completed') {
          const results = await backtestApiV2.getRunResults(runId);
          setRunResults(results);
          stopMonitoringRun();
        } else if (updatedRun.status === 'failed' || updatedRun.status === 'cancelled') {
          stopMonitoringRun();
        }
      } catch (err) {
        console.error('Failed to poll run status:', err);
      }
    }, 1000); // Poll every second
  };

  const stopMonitoringRun = () => {
    if (pollingIntervalRef.current) {
      clearInterval(pollingIntervalRef.current);
      pollingIntervalRef.current = null;
    }
  };

  const handleRunUpdate = (update: Run) => {
    setCurrentRun(update);
    setRuns(prevRuns => 
      prevRuns.map(run => run.id === update.id ? update : run)
    );
  };

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      stopMonitoringRun();
    };
  }, []);

  return {
    backtest,
    runs,
    currentRun,
    runResults,
    isLoading,
    error,
    loadBacktest,
    createBacktest,
    updateBacktest,
    deleteBacktest,
    createRun,
    pauseRun,
    resumeRun,
    cancelRun,
    updateRunSpeed,
    selectRun,
  };
}