import React, {useCallback, useState} from 'react'
import {useQuery} from '@tanstack/react-query'
import {useForm} from 'react-hook-form'

import useApiUrlStore from '../../app/store/apiStore'
import useBacktestStore from '../../app/store/backtestStore'
import {
    AccountClient,
    type Backtest,
    BacktestClient,
    DataClient,
    type IndicatorRequest,
    IndicatorType,
    LightBacktestResponse,
    MoneyManagementClient,
    type MoneyManagementRequest,
    type RunBacktestRequest,
    ScenarioClient,
    type ScenarioRequest,
    SignalType,
    Ticker,
    Timeframe,
    type TradingBotConfigRequest,
} from '../../generated/ManagingApi'
import {Toast} from '../../components/mollecules'
import BacktestTable from '../../components/organism/Backtest/backtestTable'
import IndicatorsComparison from '../../components/organism/Charts/IndicatorsComparison'
import useTheme from '../../hooks/useTheme'
import Fitness3DPlot from '../../components/organism/Charts/Fitness3DPlot'
import TPvsSLvsPnL3DPlot from '../../components/organism/Charts/TPvsSLvsPnL3DPlot'

// ============================================================================
// CONFIGURABLE PARAMETERS - ADJUST THESE TO CUSTOMIZE THE GENETIC ALGORITHM
// ============================================================================

// Genetic Algorithm Settings
const GENETIC_CONFIG = {
  populationSize: 10,        // Number of individuals per generation
  generations: 5,            // Number of generations to evolve
  mutationRate: 0.3,         // Probability of mutation (0.0 - 1.0)
  crossoverRate: 0.7,        // Probability of crossover (0.0 - 1.0)
  maxIndicators: 4,          // Maximum number of indicators per scenario
  minIndicators: 1,          // Minimum number of indicators per scenario
  phase1Generations: 3,      // Generations for single indicator testing
  phase2Generations: 2,      // Generations for 2-indicator combinations
  phase3Generations: 2,      // Generations for 3-indicator combinations
  phase4Generations: 2,      // Generations for 4-indicator combinations
}

// Trading Parameters Ranges (in whole number percentages)
const PARAMETER_RANGES = {
  stopLoss: { min: 2, max: 15 },                // 2% - 15%
  takeProfit: { min: 0.9, max: 4 },             // 0.9% - 4% (default)
  leverage: { min: 1, max: 10 },                 // 1x - 10x
  cooldownPeriod: { min: 5, max: 25 },          // 5 - 25 candles
  maxLossStreak: { min: 0, max: 4 },            // 0 - 4 losses
  maxPositionTimeHours: { min: 1, max: 48 },    // 1 - 48 hours
}

// Indicator Parameters Ranges (based on CustomScenario defaults)
const INDICATOR_RANGES = {
  period: { min: 10, max: 20 },                 // 10 - 20 periods (default: 14)
  fastPeriods: { min: 8, max: 18 },             // 8 - 18 periods (default: 12)
  slowPeriods: { min: 20, max: 35 },            // 20 - 35 periods (default: 26)
  signalPeriods: { min: 7, max: 12 },           // 7 - 12 periods (default: 9)
  multiplier: { min: 2.0, max: 4.5 },           // 2.0 - 4.5 (default: 3.0)
  stochPeriods: { min: 10, max: 20 },           // 10 - 20 periods (default: 14)
  smoothPeriods: { min: 2, max: 5 },            // 2 - 5 periods (default: 3)
  cyclePeriods: { min: 8, max: 15 },            // 8 - 15 periods (default: 10)
}

// Default indicator values (from CustomScenario)
const DEFAULT_INDICATOR_VALUES = {
  period: 14,
  fastPeriods: 12,
  slowPeriods: 26,
  signalPeriods: 9,
  multiplier: 3.0,
  stochPeriods: 14,
  smoothPeriods: 3,
  cyclePeriods: 10,
}

// Loopback Period Configuration
const LOOPBACK_CONFIG = {
  singleIndicator: 1,                           // 1 candle for single indicator
  multipleIndicators: { min: 5, max: 15 },      // 5-15 candles for multiple indicators
}

// Available Indicator Types (excluding Composite as it's a special case)
const ALL_INDICATORS = [
  IndicatorType.RsiDivergence,
  IndicatorType.RsiDivergenceConfirm,
  IndicatorType.MacdCross,
  IndicatorType.EmaCross,
  IndicatorType.ThreeWhiteSoldiers,
  IndicatorType.SuperTrend,
  IndicatorType.ChandelierExit,
  IndicatorType.EmaTrend,
  IndicatorType.StochRsiTrend,
  IndicatorType.Stc,
  IndicatorType.StDev,
  IndicatorType.LaggingStc,
  IndicatorType.SuperTrendCrossEma,
  IndicatorType.DualEmaCross,
]

// Indicator type to parameter mapping
const INDICATOR_PARAM_MAPPING = {
  [IndicatorType.RsiDivergence]: ['period'],
  [IndicatorType.RsiDivergenceConfirm]: ['period'],
  [IndicatorType.EmaCross]: ['period'],
  [IndicatorType.EmaTrend]: ['period'],
  [IndicatorType.StDev]: ['period'],
  [IndicatorType.ThreeWhiteSoldiers]: ['period'],
  [IndicatorType.MacdCross]: ['fastPeriods', 'slowPeriods', 'signalPeriods'],
  [IndicatorType.DualEmaCross]: ['fastPeriods', 'slowPeriods'],
  [IndicatorType.SuperTrend]: ['period', 'multiplier'],
  [IndicatorType.SuperTrendCrossEma]: ['period', 'multiplier'],
  [IndicatorType.ChandelierExit]: ['period', 'multiplier'],
  [IndicatorType.StochRsiTrend]: ['period', 'stochPeriods', 'signalPeriods', 'smoothPeriods'],
  [IndicatorType.Stc]: ['cyclePeriods', 'fastPeriods', 'slowPeriods'],
  [IndicatorType.LaggingStc]: ['cyclePeriods', 'fastPeriods', 'slowPeriods'],
}

// ============================================================================
// END CONFIGURABLE PARAMETERS
// ============================================================================

// Form Interface
interface GeneticFormData {
  ticker: Ticker
  timeframe: Timeframe
  startDate: string
  endDate: string
  balance: number
  populationSize: number
  generations: number
  mutationRate: number
  selectionMethod: 'tournament' | 'roulette' | 'fitness-weighted'
  elitismPercentage: number
  maxTakeProfit: number
}

// Genetic Individual Interface
interface GeneticIndividual {
  // Money Management Parameters
  stopLoss: number
  takeProfit: number
  leverage: number
  
  // Trading Bot Parameters
  cooldownPeriod: number
  maxLossStreak: number
  maxPositionTimeHours: number
  flipOnlyWhenInProfit: boolean
  closeEarlyWhenProfitable: boolean
  
  // Synth API Parameters (always false for performance)
  useSynthApi: boolean
  useForPositionSizing: boolean
  useForSignalFiltering: boolean
  useForDynamicStopLoss: boolean
  
  // Indicator Configuration
  indicators: GeneticIndicator[]
}

// Genetic Indicator Interface
interface GeneticIndicator {
  type: IndicatorType
  period?: number
  fastPeriods?: number
  slowPeriods?: number
  signalPeriods?: number
  multiplier?: number
  stochPeriods?: number
  smoothPeriods?: number
  cyclePeriods?: number
}

const BacktestGenetic: React.FC = () => {
  const {apiUrl} = useApiUrlStore()
  const {addBacktest} = useBacktestStore()
  const theme = useTheme().themeProperty()
  
  // API Clients
  const backtestClient = new BacktestClient({}, apiUrl)
  const scenarioClient = new ScenarioClient({}, apiUrl)
  const moneyManagementClient = new MoneyManagementClient({}, apiUrl)
  const accountClient = new AccountClient({}, apiUrl)
  const dataClient = new DataClient({}, apiUrl)
  
  // State
  const [isRunning, setIsRunning] = useState(false)
  const [loading, setLoading] = useState(false)
  const [results, setResults] = useState<any[]>([])
  const [currentGeneration, setCurrentGeneration] = useState(0)
  const [currentIndividual, setCurrentIndividual] = useState(0)
  const [bestFitness, setBestFitness] = useState(0)
  const [showResults, setShowResults] = useState(false)
  const [totalIndividuals, setTotalIndividuals] = useState(0)
  const [backtestTimes, setBacktestTimes] = useState<number[]>([])
  const [startTime, setStartTime] = useState<Date | null>(null)
  const [estimatedCompletion, setEstimatedCompletion] = useState<string>('')
  const [currentPhase, setCurrentPhase] = useState<'phase1' | 'phase2' | 'phase3' | 'phase4'>('phase1')
  const [bestSingleIndicators, setBestSingleIndicators] = useState<Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}>>([])
  const [eligibleIndicators, setEligibleIndicators] = useState<IndicatorType[]>(ALL_INDICATORS)
  const [removedIndicators, setRemovedIndicators] = useState<Set<IndicatorType>>(new Set())
  
  // Calculate default date range (2 weeks ago to now)
  const getDefaultDateRange = () => {
    const endDate = new Date()
    const startDate = new Date()
    startDate.setDate(startDate.getDate() - 14) // 2 weeks ago
    return {
      start: startDate.toISOString().split('T')[0],
      end: endDate.toISOString().split('T')[0]
    }
  }
  
  // React Hook Form
  const {register, handleSubmit, watch, setValue, formState: {errors}} = useForm<GeneticFormData>({
    defaultValues: {
      ticker: Ticker.BTC,
      timeframe: Timeframe.FifteenMinutes,
      startDate: getDefaultDateRange().start,
      endDate: getDefaultDateRange().end,
      balance: 10000,
      populationSize: GENETIC_CONFIG.populationSize,
      generations: GENETIC_CONFIG.generations,
      mutationRate: GENETIC_CONFIG.mutationRate,
      selectionMethod: 'tournament',
      elitismPercentage: 15,
      maxTakeProfit: 4,
    }
  })
  
  // Watch form values
  const formValues = watch()
  
  // Genetic Algorithm Configuration (from form values)
  const geneticConfig = {
    populationSize: formValues.populationSize || GENETIC_CONFIG.populationSize,
    generations: formValues.generations || GENETIC_CONFIG.generations,
    mutationRate: formValues.mutationRate || GENETIC_CONFIG.mutationRate,
    crossoverRate: GENETIC_CONFIG.crossoverRate,
  }
  
  // Fetch accounts
  const {data: accounts} = useQuery({
    queryKey: ['accounts'],
    queryFn: () => accountClient.account_GetAccounts(),
  })
  
  // Helper function to get random value within range
  const getRandomInRange = (range: { min: number; max: number }): number => {
    return Math.random() * (range.max - range.min) + range.min
  }
  
  // Helper function to get random integer within range
  const getRandomIntInRange = (range: { min: number; max: number }): number => {
    return Math.floor(Math.random() * (range.max - range.min + 1)) + range.min
  }
  
  // Helper function to format time duration
  const formatTimeDuration = (milliseconds: number): string => {
    const seconds = Math.floor(milliseconds / 1000)
    const minutes = Math.floor(seconds / 60)
    const hours = Math.floor(minutes / 60)
    
    if (hours > 0) {
      return `${hours}h ${minutes % 60}m`
    } else if (minutes > 0) {
      return `${minutes}m ${seconds % 60}s`
    } else {
      return `${seconds}s`
    }
  }
  
  // Helper function to calculate estimated completion time
  const calculateEstimatedCompletion = (processedCount: number, totalCount: number, averageTimeMs: number): string => {
    if (processedCount === 0 || averageTimeMs === 0) return 'Calculating...'
    
    const remainingCount = totalCount - processedCount
    const estimatedTimeMs = remainingCount * averageTimeMs
    const estimatedCompletionTime = new Date(Date.now() + estimatedTimeMs)
    
    return estimatedCompletionTime.toLocaleTimeString()
  }
  
  // Helper function to get dynamic max take profit value
  const getMaxTakeProfit = (): number => {
    return Math.min(formValues.maxTakeProfit || 4, PARAMETER_RANGES.takeProfit.max)
  }
  
  // Calculate total number of backtests that will be run
  const calculateTotalBacktests = (): number => {
    const populationSize = formValues.populationSize || GENETIC_CONFIG.populationSize
    const totalGenerations = formValues.generations || GENETIC_CONFIG.generations
    
    // Each phase uses the full generation count
    const phase1Generations = totalGenerations
    const phase2Generations = totalGenerations
    const phase3Generations = totalGenerations
    const phase4Generations = totalGenerations
    
    // For Phase 1, we test each eligible indicator with the full population size for each generation
    const phase1Backtests = phase1Generations * populationSize * eligibleIndicators.length
    
    // For subsequent phases, we use combinations of the best indicators from Phase 1
    const maxBestIndicators = Math.min(8, eligibleIndicators.length)
    
    // Calculate combinations for each phase
    const phase2Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) / 2) // C(n,2)
    const phase3Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) / 6) // C(n,3)
    const phase4Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) * (maxBestIndicators - 3) / 24) // C(n,4)
    
    const phase2Backtests = phase2Generations * phase2Combinations
    const phase3Backtests = phase3Generations * phase3Combinations
    const phase4Backtests = phase4Generations * phase4Combinations
    
    return phase1Backtests + phase2Backtests + phase3Backtests + phase4Backtests
  }
  
  // Helper function to calculate progress accurately
  const calculateProgress = () => {
    if (totalIndividuals === 0) return { completed: 0, total: 1, percentage: 0 }
    
    const populationSize = geneticConfig.populationSize
    const totalGenerations = formValues.generations || GENETIC_CONFIG.generations
    
    let completedBacktests = 0
    let totalBacktests = 0
    
    // Phase 1: Each generation tests populationSize * eligibleIndicators.length
    const phase1Backtests = totalGenerations * populationSize * eligibleIndicators.length
    totalBacktests += phase1Backtests
    
    if (currentPhase === 'phase1') {
      completedBacktests = (currentGeneration - 1) * populationSize + currentIndividual
    } else {
      completedBacktests += phase1Backtests
      
      // Phase 2: Use best indicators (max 8)
      const maxBestIndicators = Math.min(8, eligibleIndicators.length)
      const phase2Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) / 2)
      const phase2Backtests = totalGenerations * phase2Combinations
      totalBacktests += phase2Backtests
      
      if (currentPhase === 'phase2') {
        completedBacktests += (currentGeneration - totalGenerations - 1) * populationSize + currentIndividual
      } else {
        completedBacktests += phase2Backtests
        
        // Phase 3: 3-indicator combinations
        const phase3Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) / 6)
        const phase3Backtests = totalGenerations * phase3Combinations
        totalBacktests += phase3Backtests
        
        if (currentPhase === 'phase3') {
          completedBacktests += (currentGeneration - totalGenerations * 2 - 1) * populationSize + currentIndividual
        } else {
          completedBacktests += phase3Backtests
          
          // Phase 4: 4-indicator combinations
          const phase4Combinations = Math.min(populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) * (maxBestIndicators - 3) / 24)
          const phase4Backtests = totalGenerations * phase4Combinations
          totalBacktests += phase4Backtests
          
          if (currentPhase === 'phase4') {
            completedBacktests += (currentGeneration - totalGenerations * 3 - 1) * populationSize + currentIndividual
          }
        }
      }
    }
    
    return {
      completed: Math.min(completedBacktests, totalBacktests),
      total: totalBacktests,
      percentage: Math.round((completedBacktests / totalBacktests) * 100)
    }
  }
  
  // Create a random indicator with bias towards CustomScenario defaults
  const createRandomIndicator = (eligibleIndicators: IndicatorType[]): GeneticIndicator => {
    const type = eligibleIndicators[Math.floor(Math.random() * eligibleIndicators.length)]
    
    const indicator: GeneticIndicator = { type }
    
    // Add parameters based on indicator type using the mapping
    const requiredParams = INDICATOR_PARAM_MAPPING[type as keyof typeof INDICATOR_PARAM_MAPPING] || []
    
    requiredParams.forEach((param: string) => {
      // 70% chance to use default value, 30% chance to use random value in range
      const useDefault = Math.random() < 0.7
      
      switch (param) {
        case 'period':
          indicator.period = useDefault ? DEFAULT_INDICATOR_VALUES.period : getRandomIntInRange(INDICATOR_RANGES.period)
          break
        case 'fastPeriods':
          indicator.fastPeriods = useDefault ? DEFAULT_INDICATOR_VALUES.fastPeriods : getRandomIntInRange(INDICATOR_RANGES.fastPeriods)
          break
        case 'slowPeriods':
          indicator.slowPeriods = useDefault ? DEFAULT_INDICATOR_VALUES.slowPeriods : getRandomIntInRange(INDICATOR_RANGES.slowPeriods)
          break
        case 'signalPeriods':
          indicator.signalPeriods = useDefault ? DEFAULT_INDICATOR_VALUES.signalPeriods : getRandomIntInRange(INDICATOR_RANGES.signalPeriods)
          break
        case 'multiplier':
          indicator.multiplier = useDefault ? DEFAULT_INDICATOR_VALUES.multiplier : getRandomInRange(INDICATOR_RANGES.multiplier)
          break
        case 'stochPeriods':
          indicator.stochPeriods = useDefault ? DEFAULT_INDICATOR_VALUES.stochPeriods : getRandomIntInRange(INDICATOR_RANGES.stochPeriods)
          break
        case 'smoothPeriods':
          indicator.smoothPeriods = useDefault ? DEFAULT_INDICATOR_VALUES.smoothPeriods : getRandomIntInRange(INDICATOR_RANGES.smoothPeriods)
          break
        case 'cyclePeriods':
          indicator.cyclePeriods = useDefault ? DEFAULT_INDICATOR_VALUES.cyclePeriods : getRandomIntInRange(INDICATOR_RANGES.cyclePeriods)
          break
      }
    })
    
    return indicator
  }
  
  // Create combinations of indicators from best single indicators
  const createIndicatorCombinations = (bestIndicators: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}>, targetSize: number): GeneticIndicator[][] => {
    const combinations: GeneticIndicator[][] = []
    const indicatorTypes = bestIndicators.map(item => item.individual.indicators[0])
    
    // Generate combinations of specific size without duplicates
    const generateCombinations = (arr: GeneticIndicator[], size: number, start: number = 0, current: GeneticIndicator[] = []): void => {
      if (current.length === size) {
        // Check for duplicate indicator types
        const types = current.map(ind => ind.type)
        const uniqueTypes = new Set(types)
        if (uniqueTypes.size === types.length) {
          combinations.push([...current])
        }
        return
      }
      
      for (let i = start; i < arr.length; i++) {
        current.push(arr[i])
        generateCombinations(arr, size, i + 1, current)
        current.pop()
      }
    }
    
    generateCombinations(indicatorTypes, targetSize)
    return combinations
  }
  
  // Function to ensure no duplicate indicators in a scenario
  const ensureNoDuplicateIndicators = (indicators: GeneticIndicator[]): GeneticIndicator[] => {
    const seen = new Set<IndicatorType>()
    const uniqueIndicators: GeneticIndicator[] = []
    
    for (const indicator of indicators) {
      if (!seen.has(indicator.type)) {
        seen.add(indicator.type)
        uniqueIndicators.push(indicator)
      }
    }
    
    return uniqueIndicators
  }
  
  // Create a population for Phase 1 that ensures every eligible indicator is tested
  const createPhase1Population = (populationSize: number, eligibleIndicators: IndicatorType[]): GeneticIndividual[] => {
    const population: GeneticIndividual[] = []
    
    // Create a full population where each individual uses a different eligible indicator
    // If we have more population slots than indicators, we cycle through indicators
    for (let i = 0; i < populationSize; i++) {
      const indicatorType = eligibleIndicators[i % eligibleIndicators.length]
      const indicator = createRandomIndicator([indicatorType]) // Force this specific indicator type
      
      // Generate stop loss first
      const minTP = 0.9;
      const maxTP = getMaxTakeProfit();
      const tp = getRandomInRange({ min: minTP, max: maxTP });
      const minSL = 0.5;
      const maxSL = Math.min(tp / 1.1, tp - 0.1);
      const sl = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
      
      const individual: GeneticIndividual = {
        // Money Management Parameters
        stopLoss: sl,
        takeProfit: tp,
        leverage: 1,
        
        // Trading Bot Parameters
        cooldownPeriod: getRandomIntInRange(PARAMETER_RANGES.cooldownPeriod),
        maxLossStreak: getRandomIntInRange(PARAMETER_RANGES.maxLossStreak),
        maxPositionTimeHours: 0, // Always 0 to prevent early position cutting
        flipOnlyWhenInProfit: Math.random() > 0.5,
        closeEarlyWhenProfitable: Math.random() > 0.5,
        
        // Synth API Parameters (always false for performance)
        useSynthApi: false,
        useForPositionSizing: false,
        useForSignalFiltering: false,
        useForDynamicStopLoss: false,
        
        // Indicator Configuration
        indicators: [indicator],
      }
      
      population.push(individual)
    }
    
    return population
  }
  
  // Create a random individual with positive risk-reward ratio
  const createIndividual = (phase: 'phase1' | 'phase2' | 'phase3' | 'phase4' = 'phase1', bestIndicators: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}> = [], currentEligibleIndicators: IndicatorType[] = eligibleIndicators): GeneticIndividual => {
    // Generate stop loss first
    const minTP = 0.9;
    const maxTP = getMaxTakeProfit();
    const tp = getRandomInRange({ min: minTP, max: maxTP });
    const minSL = 0.5;
    const maxSL = Math.min(tp / 1.1, tp - 0.1);
    const sl = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
    
    let indicators: GeneticIndicator[]
    
    if (phase === 'phase1') {
      // Phase 1: Single indicators only
      indicators = [createRandomIndicator(currentEligibleIndicators)]
    } else {
      // Phase 2, 3, 4: Use combinations of best indicators with specific sizes
      const targetSize = phase === 'phase2' ? 2 : phase === 'phase3' ? 3 : 4
      
      if (bestIndicators.length > 0) {
        const combinations = createIndicatorCombinations(bestIndicators, targetSize)
        if (combinations.length > 0) {
          // Randomly select a combination
          const randomCombination = combinations[Math.floor(Math.random() * combinations.length)]
          indicators = randomCombination
        } else {
          // Fallback to random indicators of target size
          const randomIndicators = []
          const usedTypes = new Set<IndicatorType>()
          
          while (randomIndicators.length < targetSize && usedTypes.size < currentEligibleIndicators.length) {
            const randomIndicator = createRandomIndicator(currentEligibleIndicators)
            if (!usedTypes.has(randomIndicator.type)) {
              usedTypes.add(randomIndicator.type)
              randomIndicators.push(randomIndicator)
            }
          }
          
          indicators = randomIndicators
        }
      } else {
        // Fallback to random indicators of target size
        const randomIndicators = []
        const usedTypes = new Set<IndicatorType>()
        
        while (randomIndicators.length < targetSize && usedTypes.size < currentEligibleIndicators.length) {
          const randomIndicator = createRandomIndicator(currentEligibleIndicators)
          if (!usedTypes.has(randomIndicator.type)) {
            usedTypes.add(randomIndicator.type)
            randomIndicators.push(randomIndicator)
          }
        }
        
        indicators = randomIndicators
      }
    }
    
    // Ensure no duplicate indicators in the final scenario
    const uniqueIndicators = ensureNoDuplicateIndicators(indicators)
    
    return {
      // Money Management Parameters
      stopLoss: sl,
      takeProfit: tp,
      leverage: 1,
      
      // Trading Bot Parameters
      cooldownPeriod: getRandomIntInRange(PARAMETER_RANGES.cooldownPeriod),
      maxLossStreak: getRandomIntInRange(PARAMETER_RANGES.maxLossStreak),
      maxPositionTimeHours: 0, // Always 0 to prevent early position cutting
      flipOnlyWhenInProfit: Math.random() > 0.5,
      closeEarlyWhenProfitable: Math.random() > 0.5,
      
      // Synth API Parameters (always false for performance)
      useSynthApi: false,
      useForPositionSizing: false,
      useForSignalFiltering: false,
      useForDynamicStopLoss: false,
      
      // Indicator Configuration
      indicators: uniqueIndicators,
    }
  }
  
  // Validate risk-reward ratio and minimum take profit
  const validateRiskRewardRatio = (stopLoss: number, takeProfit: number): boolean => {
    const minTakeProfitForRR = stopLoss * 1.1 // At least 10% better than 1:1
    const minTakeProfitAbsolute = 0.9 // Minimum 0.9%
    const minTakeProfit = Math.max(minTakeProfitForRR, minTakeProfitAbsolute)
    return takeProfit >= minTakeProfit
  }
  
  // Function to remove bad indicators from eligible list
  const removeBadIndicators = (fitnessResults: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}>) => {
    const badIndicators = new Set<IndicatorType>()
    
    // Calculate the maximum possible fitness score for reference
    // PnL Score (30%) + Win Rate Score (20%) + Risk-Reward Score (20%) + Consistency Score (10%) + Risk-Reward Bonus (10%) + Drawdown Score (10%)
    // Maximum theoretical fitness: 0.3 + 0.2 + 0.4 + 0.1 + 0.02 + 0.1 = 1.12
    const maxPossibleFitness = 1.12
    
    // Set threshold to 10% of maximum possible fitness (0.112)
    const badIndicatorThreshold = maxPossibleFitness * 0.1
    
    // Find indicators with very poor performance
    fitnessResults.forEach(result => {
      if (result.fitness < badIndicatorThreshold && result.individual.indicators.length === 1) {
        const indicatorType = result.individual.indicators[0].type
        badIndicators.add(indicatorType)
      }
    })
    
    // Remove bad indicators from eligible list
    if (badIndicators.size > 0) {
      setEligibleIndicators(prev => prev.filter(indicator => !badIndicators.has(indicator)))
      setRemovedIndicators(prev => new Set([...prev, ...badIndicators]))
      
      // Log removed indicators with fitness scores for transparency
      const removedWithScores = fitnessResults
        .filter(result => badIndicators.has(result.individual.indicators[0]?.type))
        .map(result => `${result.individual.indicators[0]?.type} (fitness: ${result.fitness.toFixed(3)})`)
      
      console.log(`Removed bad indicators (threshold: ${badIndicatorThreshold.toFixed(3)}): ${removedWithScores.join(', ')}`)
    }
  }
  
  // Calculate fitness score based on backtest results
  const calculateFitnessScore = (backtest: Backtest, individual: GeneticIndividual): number => {
    if (!backtest.statistics) return 0
    
    const stats = backtest.statistics
    
    // Multi-objective fitness function
    const pnlScore = Math.max(0, (stats.totalPnL || 0) / 1000) // Normalize PnL
    const winRateScore = (backtest.winRate || 0) / 100 // Normalize win rate
    const riskRewardScore = Math.min(2, (stats.winningTrades || 0) / Math.max(1, Math.abs(stats.loosingTrades || 1)))
    const consistencyScore = 1 - Math.abs((stats.totalPnL || 0) - (backtest.finalPnl || 0)) / Math.max(1, Math.abs(stats.totalPnL || 1))
    
    // Risk-reward ratio bonus (encourage better R:R ratios)
    const riskRewardRatio = individual.takeProfit / individual.stopLoss
    const riskRewardBonus = Math.min(0.2, (riskRewardRatio - 1.1) * 0.1) // Bonus for R:R > 1.1
    
    // Drawdown score (normalized to 0-1, where lower drawdown is better)
    // maxDrawdownPc is in percentage, so we normalize it
    const maxDrawdownPc = Math.abs(stats.maxDrawdownPc || 0) // Use absolute value and default to 0
    const drawdownScore = Math.max(0, 1 - (maxDrawdownPc / 50)) // Normalize: 0% drawdown = 1.0, 50% drawdown = 0.0
    
    // Weighted combination with drawdown consideration
    const fitness = 
      pnlScore * 0.3 + 
      winRateScore * 0.2 + 
      riskRewardScore * 0.2 + 
      consistencyScore * 0.1 +
      riskRewardBonus * 0.1 +
      drawdownScore * 0.1 // Add drawdown score with a weight of 0.1
    
    return Math.max(0, fitness)
  }
  
  // Convert genetic indicator to IndicatorRequest
  const convertToIndicatorRequest = (indicator: GeneticIndicator, index: number): IndicatorRequest => {
    const baseRequest: IndicatorRequest = {
      name: `Genetic_${indicator.type}_${index}_${Date.now()}`,
      type: indicator.type,
      signalType: SignalType.Signal,
      minimumHistory: 14,
    }
    
    // Add parameters based on indicator type
    if (indicator.period !== undefined) baseRequest.period = indicator.period
    if (indicator.fastPeriods !== undefined) baseRequest.fastPeriods = indicator.fastPeriods
    if (indicator.slowPeriods !== undefined) baseRequest.slowPeriods = indicator.slowPeriods
    if (indicator.signalPeriods !== undefined) baseRequest.signalPeriods = indicator.signalPeriods
    if (indicator.multiplier !== undefined) baseRequest.multiplier = indicator.multiplier
    if (indicator.stochPeriods !== undefined) baseRequest.stochPeriods = indicator.stochPeriods
    if (indicator.smoothPeriods !== undefined) baseRequest.smoothPeriods = indicator.smoothPeriods
    if (indicator.cyclePeriods !== undefined) baseRequest.cyclePeriods = indicator.cyclePeriods
    
    return baseRequest
  }
  
  // Selection functions for genetic algorithm
  const selectParentByTournament = (
    fitnessResults: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}>,
    tournamentSize: number
  ): GeneticIndividual => {
    let bestIndividual = fitnessResults[0].individual
    let bestFitness = fitnessResults[0].fitness
    
    for (let i = 0; i < tournamentSize; i++) {
      const randomIndex = Math.floor(Math.random() * fitnessResults.length)
      const candidate = fitnessResults[randomIndex]
      
      if (candidate.fitness > bestFitness) {
        bestFitness = candidate.fitness
        bestIndividual = candidate.individual
      }
    }
    
    return bestIndividual
  }
  
  const selectParentByRouletteWheel = (
    fitnessResults: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}>
  ): GeneticIndividual => {
    // Calculate total fitness
    const totalFitness = fitnessResults.reduce((sum, result) => sum + Math.max(0, result.fitness), 0)
    
    if (totalFitness <= 0) {
      // If all fitness scores are negative or zero, use tournament selection as fallback
      return selectParentByTournament(fitnessResults, 3)
    }
    
    // Generate random number between 0 and total fitness
    const randomValue = Math.random() * totalFitness
    
    // Find the individual corresponding to this random value
    let cumulativeFitness = 0
    for (const result of fitnessResults) {
      cumulativeFitness += Math.max(0, result.fitness)
      if (cumulativeFitness >= randomValue) {
        return result.individual
      }
    }
    
    // Fallback to best individual
    return fitnessResults[0].individual
  }
  
  const selectParentByFitnessWeighted = (
    fitnessResults: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}>
  ): GeneticIndividual => {
    // Normalize fitness scores to positive values and calculate selection probabilities
    const minFitness = Math.min(...fitnessResults.map(r => r.fitness))
    const normalizedFitness = fitnessResults.map(result => ({
      ...result,
      normalizedFitness: result.fitness - minFitness + 1 // Add 1 to avoid zero probabilities
    }))
    
    const totalNormalizedFitness = normalizedFitness.reduce((sum, result) => sum + result.normalizedFitness, 0)
    
    // Calculate selection probabilities
    const selectionProbabilities = normalizedFitness.map(result => ({
      ...result,
      probability: result.normalizedFitness / totalNormalizedFitness
    }))
    
    // Sort by probability (best first)
    selectionProbabilities.sort((a, b) => b.probability - a.probability)
    
    // Use roulette wheel selection with normalized probabilities
    const randomValue = Math.random()
    let cumulativeProbability = 0
    
    for (const result of selectionProbabilities) {
      cumulativeProbability += result.probability
      if (cumulativeProbability >= randomValue) {
        return result.individual
      }
    }
    
    // Fallback to best individual
    return selectionProbabilities[0].individual
  }

  const selectParent = (
    fitnessResults: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}>
  ): GeneticIndividual => {
    switch (formValues.selectionMethod) {
      case 'roulette':
        return selectParentByRouletteWheel(fitnessResults)
      case 'fitness-weighted':
        return selectParentByFitnessWeighted(fitnessResults)
      case 'tournament':
      default:
        return selectParentByTournament(fitnessResults, 3)
    }
  }
  
  // Run backtest for an individual
  const runBacktestForIndividual = useCallback(async (individual: GeneticIndividual): Promise<Backtest> => {
    // Create money management (values are already in percentage format)
    const moneyManagement: MoneyManagementRequest = {
      name: `Genetic_${Date.now()}`,
      timeframe: formValues.timeframe,
      stopLoss: individual.stopLoss,
      takeProfit: individual.takeProfit,
      leverage: individual.leverage,
    }
    
    // Create indicators
    const indicators: IndicatorRequest[] = individual.indicators.map((indicator, index) => 
      convertToIndicatorRequest(indicator, index)
    )
    
    // Calculate loopback period based on number of indicators
    const loopbackPeriod = individual.indicators.length === 1 
      ? LOOPBACK_CONFIG.singleIndicator 
      : getRandomIntInRange(LOOPBACK_CONFIG.multipleIndicators)
    
    // Create scenario
    const scenario: ScenarioRequest = {
      name: `Genetic_Scenario_${Date.now()}`,
      indicators: indicators,
      loopbackPeriod: loopbackPeriod,
    }
    
    // Create trading bot config
    const config: TradingBotConfigRequest = {
      accountName: accounts?.[0]?.name || 'default',
      ticker: formValues.ticker,
      timeframe: formValues.timeframe,
      isForWatchingOnly: false,
      botTradingBalance: formValues.balance,
      name: `Genetic_Bot_${Date.now()}`,
      flipPosition: false,
      cooldownPeriod: individual.cooldownPeriod,
      maxLossStreak: individual.maxLossStreak,
      scenario: scenario,
      moneyManagement: moneyManagement,
      maxPositionTimeHours: individual.maxPositionTimeHours,
      flipOnlyWhenInProfit: individual.flipOnlyWhenInProfit,
      closeEarlyWhenProfitable: individual.closeEarlyWhenProfitable,
      useSynthApi: individual.useSynthApi,
      useForPositionSizing: individual.useForPositionSizing,
      useForSignalFiltering: individual.useForSignalFiltering,
      useForDynamicStopLoss: individual.useForDynamicStopLoss,
    }
    
    // Run backtest
    const request: RunBacktestRequest = {
      config: config,
      startDate: new Date(formValues.startDate),
      endDate: new Date(formValues.endDate),
      save: false,
    }
    
    const backtest = await backtestClient.backtest_Run(request)
    addBacktest(backtest)
    
    return backtest
  }, [formValues.timeframe, formValues.ticker, formValues.balance, formValues.startDate, formValues.endDate, accounts, backtestClient, addBacktest])
  
  // Two-phase genetic algorithm implementation with real-time updates
  const runGeneticAlgorithm = useCallback(async () => {
    setIsRunning(true)
    setLoading(true)
    setResults([])
    setCurrentGeneration(0)
    setCurrentIndividual(0)
    setBestFitness(0)
    setShowResults(true) // Show results immediately for real-time updates
    setBacktestTimes([])
    setStartTime(new Date())
    setEstimatedCompletion('Calculating...')
    setCurrentPhase('phase1')
    setBestSingleIndicators([])
    setEligibleIndicators(ALL_INDICATORS)
    setRemovedIndicators(new Set())
    
    // Use the full user-specified generation count for each phase
    const totalGenerations = formValues.generations || GENETIC_CONFIG.generations
    
    // Each phase uses the full generation count
    const phase1Generations = totalGenerations
    const phase2Generations = totalGenerations
    const phase3Generations = totalGenerations
    const phase4Generations = totalGenerations
    
    // For Phase 1, we test each eligible indicator with the full population size
    const phase1Individuals = phase1Generations * geneticConfig.populationSize * eligibleIndicators.length
    
    // For subsequent phases, we use combinations of the best indicators from Phase 1
    // We'll assume we keep the top 8 indicators (as per the current logic)
    const maxBestIndicators = Math.min(8, eligibleIndicators.length)
    
    // Calculate combinations for each phase
    const phase2Combinations = Math.min(geneticConfig.populationSize, maxBestIndicators * (maxBestIndicators - 1) / 2) // C(n,2)
    const phase3Combinations = Math.min(geneticConfig.populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) / 6) // C(n,3)
    const phase4Combinations = Math.min(geneticConfig.populationSize, maxBestIndicators * (maxBestIndicators - 1) * (maxBestIndicators - 2) * (maxBestIndicators - 3) / 24) // C(n,4)
    
    const phase2Individuals = phase2Generations * phase2Combinations
    const phase3Individuals = phase3Generations * phase3Combinations
    const phase4Individuals = phase4Generations * phase4Combinations
    const totalIndividuals = phase1Individuals + phase2Individuals + phase3Individuals + phase4Individuals
    setTotalIndividuals(totalIndividuals)
    
    const t = new Toast('Starting two-phase genetic algorithm optimization...')
    
    try {
      let processedIndividuals = 0
      let bestFitnessScore = 0
      let bestIndividual: GeneticIndividual | null = null
      
      // Phase 1: Test single indicators
      t.update('info', 'Phase 1: Testing single indicators...')
      setCurrentPhase('phase1')
      
      let population = createPhase1Population(geneticConfig.populationSize, eligibleIndicators)
      const phase1Results: Array<{individual: GeneticIndividual, backtest: Backtest | null, fitness: number}> = []
      
      for (let generation = 0; generation < phase1Generations; generation++) {
        setCurrentGeneration(generation + 1)
        
        // Evaluate fitness for individuals one by one for real-time updates
        const fitnessResults = []
        
        for (let i = 0; i < population.length; i++) {
          setCurrentIndividual(i + 1)
          const individual = population[i]
          
          try {
            const backtestStartTime = Date.now()
            const backtest = await runBacktestForIndividual(individual)
            const backtestEndTime = Date.now()
            const backtestDuration = backtestEndTime - backtestStartTime
            
            const fitness = calculateFitnessScore(backtest, individual)
            const result = {individual, backtest, fitness}
            fitnessResults.push(result)
            phase1Results.push(result)
            
            processedIndividuals++
            
            // Track backtest times for average calculation
            setBacktestTimes(prev => {
              const newTimes = [...prev, backtestDuration]
              return newTimes.slice(-10)
            })
            
            // Update results in real-time
            setResults(prev => {
              const newResults = [...prev, {
                individual: result.individual,
                backtest: result.backtest,
                fitness: result.fitness,
                generation: generation + 1,
                phase: 'phase1',
              }]
              return newResults
            })
            
            // Update best fitness in real-time
            if (fitness > bestFitnessScore) {
              bestFitnessScore = fitness
              bestIndividual = individual
              setBestFitness(bestFitnessScore)
            }
            
            // Calculate and update estimated completion time
            const currentTimes = backtestTimes.length > 0 ? backtestTimes : [backtestDuration]
            const averageTimeMs = currentTimes.reduce((sum, time) => sum + time, 0) / currentTimes.length
            const estimatedCompletionTime = calculateEstimatedCompletion(processedIndividuals, totalIndividuals, averageTimeMs)
            setEstimatedCompletion(estimatedCompletionTime)
            
            // Update progress toast
            const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
            const avgTimeFormatted = formatTimeDuration(averageTimeMs)
            const testedIndicators = new Set(fitnessResults.map(r => r.individual.indicators[0]?.type)).size
            t.update('info', `Phase 1 - Progress: ${progress}% - Gen ${generation + 1}/${phase1Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Tested: ${testedIndicators}/${eligibleIndicators.length} indicators - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
            
          } catch (error) {
            console.error('Fitness calculation error:', error)
            fitnessResults.push({individual, backtest: null, fitness: 0})
            processedIndividuals++
          }
        }
        
        // Sort by fitness and create next generation
        fitnessResults.sort((a, b) => b.fitness - a.fitness)
        
        // Store best single indicators for subsequent phases
        if (generation === phase1Generations - 1) {
          const topSingleIndicators = fitnessResults
            .filter(result => result.fitness > 0)
            .slice(0, Math.min(8, fitnessResults.length)) // Keep top 8 single indicators
          setBestSingleIndicators(topSingleIndicators)
          
          // Remove bad indicators after phase 1
          removeBadIndicators(fitnessResults)
        }
        
        // For Phase 1, create a new population for the next generation
        // Each generation tests every eligible indicator with the full population size
        population = createPhase1Population(geneticConfig.populationSize, eligibleIndicators)
      }
      
      // Phase 2: Test 2-indicator combinations
      t.update('info', 'Phase 2: Testing 2-indicator combinations...')
      setCurrentPhase('phase2')
      
      const bestIndicators = bestSingleIndicators
      population = Array.from({length: geneticConfig.populationSize}, () => createIndividual('phase2', bestIndicators))
      
      for (let generation = 0; generation < phase2Generations; generation++) {
        setCurrentGeneration(phase1Generations + generation + 1)
        
        const fitnessResults = []
        
        for (let i = 0; i < population.length; i++) {
          setCurrentIndividual(i + 1)
          const individual = population[i]
          
          try {
            const backtestStartTime = Date.now()
            const backtest = await runBacktestForIndividual(individual)
            const backtestEndTime = Date.now()
            const backtestDuration = backtestEndTime - backtestStartTime
            
            const fitness = calculateFitnessScore(backtest, individual)
            const result = {individual, backtest, fitness}
            fitnessResults.push(result)
            
            processedIndividuals++
            
            // Track backtest times for average calculation
            setBacktestTimes(prev => {
              const newTimes = [...prev, backtestDuration]
              return newTimes.slice(-10)
            })
            
            // Update results in real-time
            setResults(prev => {
              const newResults = [...prev, {
                individual: result.individual,
                backtest: result.backtest,
                fitness: result.fitness,
                generation: phase1Generations + generation + 1,
                phase: 'phase2',
              }]
              return newResults
            })
            
            // Update best fitness in real-time
            if (fitness > bestFitnessScore) {
              bestFitnessScore = fitness
              bestIndividual = individual
              setBestFitness(bestFitnessScore)
            }
            
            // Calculate and update estimated completion time
            const currentTimes = backtestTimes.length > 0 ? backtestTimes : [backtestDuration]
            const averageTimeMs = currentTimes.reduce((sum, time) => sum + time, 0) / currentTimes.length
            const estimatedCompletionTime = calculateEstimatedCompletion(processedIndividuals, totalIndividuals, averageTimeMs)
            setEstimatedCompletion(estimatedCompletionTime)
            
            // Update progress toast
            const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
            const avgTimeFormatted = formatTimeDuration(averageTimeMs)
            t.update('info', `Phase 2 - Progress: ${progress}% - Gen ${generation + 1}/${phase2Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
            
          } catch (error) {
            console.error('Fitness calculation error:', error)
            fitnessResults.push({individual, backtest: null, fitness: 0})
            processedIndividuals++
          }
        }
        
        // Sort by fitness and create next generation
        fitnessResults.sort((a, b) => b.fitness - a.fitness)
        
        // Create next generation for phase 2
        const newPopulation = []
        const eliteCount = Math.max(1, Math.floor(geneticConfig.populationSize * (formValues.elitismPercentage || 15) / 100))
        for (let i = 0; i < eliteCount; i++) {
          newPopulation.push(fitnessResults[i].individual)
        }
        
        while (newPopulation.length < geneticConfig.populationSize) {
          const parent1 = selectParent(fitnessResults)
          const parent2 = selectParent(fitnessResults)
          
          const child = {...parent1}
          if (Math.random() < geneticConfig.crossoverRate) {
            child.stopLoss = parent2.stopLoss
            child.takeProfit = parent2.takeProfit
            child.leverage = 1
            child.cooldownPeriod = parent2.cooldownPeriod
            child.indicators = parent2.indicators
            
            // Validate risk-reward ratio after crossover
            const minTakeProfit = child.stopLoss * 1.1
            if (child.takeProfit < minTakeProfit) {
              child.takeProfit = minTakeProfit + getRandomInRange({ 
                min: 0, 
                max: getMaxTakeProfit() - minTakeProfit 
              })
            }
          }
          
          // Mutation
          if (Math.random() < geneticConfig.mutationRate) {
            // Mutate stop loss
            child.stopLoss = getRandomInRange(PARAMETER_RANGES.stopLoss);
            // Adjust take profit to maintain minimum R:R ratio and respect max TP
            const minTP = 0.9;
            const maxTP = getMaxTakeProfit();
            child.takeProfit = getRandomInRange({ min: minTP, max: maxTP });
            const minSL = 0.5;
            const maxSL = Math.min(child.takeProfit / 1.1, child.takeProfit - 0.1);
            child.stopLoss = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
          }
          if (Math.random() < geneticConfig.mutationRate) {
            // Mutate take profit while respecting minimum R:R ratio and max TP
            const minTP = 0.9;
            const maxTP = getMaxTakeProfit();
            child.takeProfit = getRandomInRange({ min: minTP, max: maxTP });
            const minSL = 0.5;
            const maxSL = Math.min(child.takeProfit / 1.1, child.takeProfit - 0.1);
            child.stopLoss = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
          }
          if (Math.random() < geneticConfig.mutationRate) {
            // 50% chance to create new combination, 50% chance to slightly modify existing
            if (Math.random() < 0.5) {
              // Create new 2-indicator combination
              const combinations = createIndicatorCombinations(bestIndicators, 2)
              if (combinations.length > 0) {
                const randomCombination = combinations[Math.floor(Math.random() * combinations.length)]
                child.indicators = randomCombination
              }
            } else {
              // Slight modification of existing indicator parameters
              const modifiedIndicators = child.indicators.map(indicator => {
                const modifiedIndicator = {...indicator}
                
                // Randomly modify one parameter with small variation
                const paramKeys = Object.keys(modifiedIndicator).filter(key => 
                  key !== 'type' && typeof modifiedIndicator[key as keyof GeneticIndicator] === 'number'
                ) as Array<keyof GeneticIndicator>
                
                if (paramKeys.length > 0 && Math.random() < 0.3) { // 30% chance to modify each indicator
                  const randomParam = paramKeys[Math.floor(Math.random() * paramKeys.length)]
                  const currentValue = modifiedIndicator[randomParam] as number
                  const defaultValue = DEFAULT_INDICATOR_VALUES[randomParam as keyof typeof DEFAULT_INDICATOR_VALUES]
                  
                  // Add small random variation (±20% of the difference from default)
                  const variation = (currentValue - defaultValue) * 0.2 * (Math.random() - 0.5)
                  const newValue = Math.max(1, Math.round(currentValue + variation))
                  
                  ;(modifiedIndicator as any)[randomParam] = newValue
                }
                
                return modifiedIndicator
              })
              
              child.indicators = modifiedIndicators
            }
          }
          
          // Enforce RR >= 1.1:1
          if (child.takeProfit < child.stopLoss * 1.1) {
            child.takeProfit = Math.min(getMaxTakeProfit(), child.stopLoss * 1.1);
          }
          if (child.stopLoss > child.takeProfit / 1.1) {
            child.stopLoss = Math.max(PARAMETER_RANGES.stopLoss.min, child.takeProfit / 1.1);
          }
          
          newPopulation.push(child)
        }
        
        population = newPopulation
      }
      
      // Phase 3: Test 3-indicator combinations
      t.update('info', 'Phase 3: Testing 3-indicator combinations...')
      setCurrentPhase('phase3')
      
      population = Array.from({length: geneticConfig.populationSize}, () => createIndividual('phase3', bestIndicators))
      
      for (let generation = 0; generation < phase3Generations; generation++) {
        setCurrentGeneration(phase1Generations + phase2Generations + generation + 1)
        
        const fitnessResults = []
        
        for (let i = 0; i < population.length; i++) {
          setCurrentIndividual(i + 1)
          const individual = population[i]
          
          try {
            const backtestStartTime = Date.now()
            const backtest = await runBacktestForIndividual(individual)
            const backtestEndTime = Date.now()
            const backtestDuration = backtestEndTime - backtestStartTime
            
            const fitness = calculateFitnessScore(backtest, individual)
            const result = {individual, backtest, fitness}
            fitnessResults.push(result)
            
            processedIndividuals++
            
            // Track backtest times for average calculation
            setBacktestTimes(prev => {
              const newTimes = [...prev, backtestDuration]
              return newTimes.slice(-10)
            })
            
            // Update results in real-time
            setResults(prev => {
              const newResults = [...prev, {
                individual: result.individual,
                backtest: result.backtest,
                fitness: result.fitness,
                generation: phase1Generations + phase2Generations + generation + 1,
                phase: 'phase3',
              }]
              return newResults
            })
            
            // Update best fitness in real-time
            if (fitness > bestFitnessScore) {
              bestFitnessScore = fitness
              bestIndividual = individual
              setBestFitness(bestFitnessScore)
            }
            
            // Calculate and update estimated completion time
            const currentTimes = backtestTimes.length > 0 ? backtestTimes : [backtestDuration]
            const averageTimeMs = currentTimes.reduce((sum, time) => sum + time, 0) / currentTimes.length
            const estimatedCompletionTime = calculateEstimatedCompletion(processedIndividuals, totalIndividuals, averageTimeMs)
            setEstimatedCompletion(estimatedCompletionTime)
            
            // Update progress toast
            const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
            const avgTimeFormatted = formatTimeDuration(averageTimeMs)
            t.update('info', `Phase 3 - Progress: ${progress}% - Gen ${generation + 1}/${phase3Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
            
          } catch (error) {
            console.error('Fitness calculation error:', error)
            fitnessResults.push({individual, backtest: null, fitness: 0})
            processedIndividuals++
          }
        }
        
        // Sort by fitness and create next generation
        fitnessResults.sort((a, b) => b.fitness - a.fitness)
        
        // Create next generation for phase 3
        const newPopulation = []
        const eliteCount = Math.max(1, Math.floor(geneticConfig.populationSize * (formValues.elitismPercentage || 15) / 100))
        for (let i = 0; i < eliteCount; i++) {
          newPopulation.push(fitnessResults[i].individual)
        }
        
        while (newPopulation.length < geneticConfig.populationSize) {
          const parent1 = selectParent(fitnessResults)
          const parent2 = selectParent(fitnessResults)
          
          const child = {...parent1}
          if (Math.random() < geneticConfig.crossoverRate) {
            child.stopLoss = parent2.stopLoss
            child.takeProfit = parent2.takeProfit
            child.leverage = 1
            child.cooldownPeriod = parent2.cooldownPeriod
            child.indicators = parent2.indicators
            
            // Validate risk-reward ratio after crossover
            const minTakeProfit = child.stopLoss * 1.1
            if (child.takeProfit < minTakeProfit) {
              child.takeProfit = minTakeProfit + getRandomInRange({ 
                min: 0, 
                max: getMaxTakeProfit() - minTakeProfit 
              })
            }
          }
          
          // Mutation
          if (Math.random() < geneticConfig.mutationRate) {
            // Mutate stop loss
            child.stopLoss = getRandomInRange(PARAMETER_RANGES.stopLoss);
            // Adjust take profit to maintain minimum R:R ratio and respect max TP
            const minTP = 0.9;
            const maxTP = getMaxTakeProfit();
            child.takeProfit = getRandomInRange({ min: minTP, max: maxTP });
            const minSL = 0.5;
            const maxSL = Math.min(child.takeProfit / 1.1, child.takeProfit - 0.1);
            child.stopLoss = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
          }
          if (Math.random() < geneticConfig.mutationRate) {
            // Mutate take profit while respecting minimum R:R ratio and max TP
            const minTP = 0.9;
            const maxTP = getMaxTakeProfit();
            child.takeProfit = getRandomInRange({ min: minTP, max: maxTP });
            const minSL = 0.5;
            const maxSL = Math.min(child.takeProfit / 1.1, child.takeProfit - 0.1);
            child.stopLoss = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
          }
          if (Math.random() < geneticConfig.mutationRate) {
            // 50% chance to create new combination, 50% chance to slightly modify existing
            if (Math.random() < 0.5) {
              // Create new 3-indicator combination
              const combinations = createIndicatorCombinations(bestIndicators, 3)
              if (combinations.length > 0) {
                const randomCombination = combinations[Math.floor(Math.random() * combinations.length)]
                child.indicators = randomCombination
              }
            } else {
              // Slight modification of existing indicator parameters
              const modifiedIndicators = child.indicators.map(indicator => {
                const modifiedIndicator = {...indicator}
                
                // Randomly modify one parameter with small variation
                const paramKeys = Object.keys(modifiedIndicator).filter(key => 
                  key !== 'type' && typeof modifiedIndicator[key as keyof GeneticIndicator] === 'number'
                ) as Array<keyof GeneticIndicator>
                
                if (paramKeys.length > 0 && Math.random() < 0.3) { // 30% chance to modify each indicator
                  const randomParam = paramKeys[Math.floor(Math.random() * paramKeys.length)]
                  const currentValue = modifiedIndicator[randomParam] as number
                  const defaultValue = DEFAULT_INDICATOR_VALUES[randomParam as keyof typeof DEFAULT_INDICATOR_VALUES]
                  
                  // Add small random variation (±20% of the difference from default)
                  const variation = (currentValue - defaultValue) * 0.2 * (Math.random() - 0.5)
                  const newValue = Math.max(1, Math.round(currentValue + variation))
                  
                  ;(modifiedIndicator as any)[randomParam] = newValue
                }
                
                return modifiedIndicator
              })
              
              child.indicators = modifiedIndicators
            }
          }
          
          // Enforce RR >= 1.1:1
          if (child.takeProfit < child.stopLoss * 1.1) {
            child.takeProfit = Math.min(getMaxTakeProfit(), child.stopLoss * 1.1);
          }
          if (child.stopLoss > child.takeProfit / 1.1) {
            child.stopLoss = Math.max(PARAMETER_RANGES.stopLoss.min, child.takeProfit / 1.1);
          }
          
          newPopulation.push(child)
        }
        
        population = newPopulation
      }
      
      // Phase 4: Test 4-indicator combinations
      t.update('info', 'Phase 4: Testing 4-indicator combinations...')
      setCurrentPhase('phase4')
      
      population = Array.from({length: geneticConfig.populationSize}, () => createIndividual('phase4', bestIndicators))
      
      for (let generation = 0; generation < phase4Generations; generation++) {
        setCurrentGeneration(phase1Generations + phase2Generations + phase3Generations + generation + 1)
        
        const fitnessResults = []
        
        for (let i = 0; i < population.length; i++) {
          setCurrentIndividual(i + 1)
          const individual = population[i]
          
          try {
            const backtestStartTime = Date.now()
            const backtest = await runBacktestForIndividual(individual)
            const backtestEndTime = Date.now()
            const backtestDuration = backtestEndTime - backtestStartTime
            
            const fitness = calculateFitnessScore(backtest, individual)
            const result = {individual, backtest, fitness}
            fitnessResults.push(result)
            
            processedIndividuals++
            
            // Track backtest times for average calculation
            setBacktestTimes(prev => {
              const newTimes = [...prev, backtestDuration]
              return newTimes.slice(-10)
            })
            
            // Update results in real-time
            setResults(prev => {
              const newResults = [...prev, {
                individual: result.individual,
                backtest: result.backtest,
                fitness: result.fitness,
                generation: phase1Generations + phase2Generations + phase3Generations + generation + 1,
                phase: 'phase4',
              }]
              return newResults
            })
            
            // Update best fitness in real-time
            if (fitness > bestFitnessScore) {
              bestFitnessScore = fitness
              bestIndividual = individual
              setBestFitness(bestFitnessScore)
            }
            
            // Calculate and update estimated completion time
            const currentTimes = backtestTimes.length > 0 ? backtestTimes : [backtestDuration]
            const averageTimeMs = currentTimes.reduce((sum, time) => sum + time, 0) / currentTimes.length
            const estimatedCompletionTime = calculateEstimatedCompletion(processedIndividuals, totalIndividuals, averageTimeMs)
            setEstimatedCompletion(estimatedCompletionTime)
            
            // Update progress toast
            const progress = (processedIndividuals / totalIndividuals * 100).toFixed(1)
            const avgTimeFormatted = formatTimeDuration(averageTimeMs)
            t.update('info', `Phase 4 - Progress: ${progress}% - Gen ${generation + 1}/${phase4Generations} - Individual ${i + 1}/${geneticConfig.populationSize} - Best: ${bestFitnessScore.toFixed(2)} - Avg: ${avgTimeFormatted} - ETA: ${estimatedCompletionTime}`)
            
          } catch (error) {
            console.error('Fitness calculation error:', error)
            fitnessResults.push({individual, backtest: null, fitness: 0})
            processedIndividuals++
          }
        }
        
        // Sort by fitness and create next generation
        fitnessResults.sort((a, b) => b.fitness - a.fitness)
        
        // Create next generation for phase 4
        const newPopulation = []
        const eliteCount = Math.max(1, Math.floor(geneticConfig.populationSize * (formValues.elitismPercentage || 15) / 100))
        for (let i = 0; i < eliteCount; i++) {
          newPopulation.push(fitnessResults[i].individual)
        }
        
        while (newPopulation.length < geneticConfig.populationSize) {
          const parent1 = selectParent(fitnessResults)
          const parent2 = selectParent(fitnessResults)
          
          const child = {...parent1}
          if (Math.random() < geneticConfig.crossoverRate) {
            child.stopLoss = parent2.stopLoss
            child.takeProfit = parent2.takeProfit
            child.leverage = 1
            child.cooldownPeriod = parent2.cooldownPeriod
            child.indicators = parent2.indicators
            
            // Validate risk-reward ratio after crossover
            const minTakeProfit = child.stopLoss * 1.1
            if (child.takeProfit < minTakeProfit) {
              child.takeProfit = minTakeProfit + getRandomInRange({ 
                min: 0, 
                max: getMaxTakeProfit() - minTakeProfit 
              })
            }
          }
          
          // Mutation
          if (Math.random() < geneticConfig.mutationRate) {
            // Mutate stop loss
            child.stopLoss = getRandomInRange(PARAMETER_RANGES.stopLoss);
            // Adjust take profit to maintain minimum R:R ratio and respect max TP
            const minTP = 0.9;
            const maxTP = getMaxTakeProfit();
            child.takeProfit = getRandomInRange({ min: minTP, max: maxTP });
            const minSL = 0.5;
            const maxSL = Math.min(child.takeProfit / 1.1, child.takeProfit - 0.1);
            child.stopLoss = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
          }
          if (Math.random() < geneticConfig.mutationRate) {
            // Mutate take profit while respecting minimum R:R ratio and max TP
            const minTP = 0.9;
            const maxTP = getMaxTakeProfit();
            child.takeProfit = getRandomInRange({ min: minTP, max: maxTP });
            const minSL = 0.5;
            const maxSL = Math.min(child.takeProfit / 1.1, child.takeProfit - 0.1);
            child.stopLoss = maxSL >= minSL ? getRandomInRange({ min: minSL, max: maxSL }) : minSL;
          }
          if (Math.random() < geneticConfig.mutationRate) {
            // 50% chance to create new combination, 50% chance to slightly modify existing
            if (Math.random() < 0.5) {
              // Create new 4-indicator combination
              const combinations = createIndicatorCombinations(bestIndicators, 4)
              if (combinations.length > 0) {
                const randomCombination = combinations[Math.floor(Math.random() * combinations.length)]
                child.indicators = randomCombination
              }
            } else {
              // Slight modification of existing indicator parameters
              const modifiedIndicators = child.indicators.map(indicator => {
                const modifiedIndicator = {...indicator}
                
                // Randomly modify one parameter with small variation
                const paramKeys = Object.keys(modifiedIndicator).filter(key => 
                  key !== 'type' && typeof modifiedIndicator[key as keyof GeneticIndicator] === 'number'
                ) as Array<keyof GeneticIndicator>
                
                if (paramKeys.length > 0 && Math.random() < 0.3) { // 30% chance to modify each indicator
                  const randomParam = paramKeys[Math.floor(Math.random() * paramKeys.length)]
                  const currentValue = modifiedIndicator[randomParam] as number
                  const defaultValue = DEFAULT_INDICATOR_VALUES[randomParam as keyof typeof DEFAULT_INDICATOR_VALUES]
                  
                  // Add small random variation (±20% of the difference from default)
                  const variation = (currentValue - defaultValue) * 0.2 * (Math.random() - 0.5)
                  const newValue = Math.max(1, Math.round(currentValue + variation))
                  
                  ;(modifiedIndicator as any)[randomParam] = newValue
                }
                
                return modifiedIndicator
              })
              
              child.indicators = modifiedIndicators
            }
          }
          
          // Enforce RR >= 1.1:1
          if (child.takeProfit < child.stopLoss * 1.1) {
            child.takeProfit = Math.min(getMaxTakeProfit(), child.stopLoss * 1.1);
          }
          if (child.stopLoss > child.takeProfit / 1.1) {
            child.stopLoss = Math.max(PARAMETER_RANGES.stopLoss.min, child.takeProfit / 1.1);
          }
          
          newPopulation.push(child)
        }
        
        population = newPopulation
      }
      
      t.update('success', 'Four-phase genetic algorithm completed!')
      
    } catch (error) {
      console.error('Genetic algorithm error:', error)
      t.update('error', 'Genetic algorithm failed: ' + error)
    } finally {
      setIsRunning(false)
      setLoading(false)
    }
  }, [geneticConfig, runBacktestForIndividual, calculateFitnessScore])
  
  // Prepare 3D plot data for Fitness vs Score vs Win Rate
  const plotData = results.length > 0 ? [
    {
      type: 'scatter3d' as const,
      mode: 'markers' as const,
      x: results.map(r => r.fitness),
      y: results.map(r => r.backtest?.score || 0),
      z: results.map(r => r.backtest?.winRate || 0),
      marker: {
        size: 5,
        color: results.map(r => r.generation),
        colorscale: 'Viridis' as const,
        opacity: 0.8,
      },
      text: results.map(r => {
        const loopbackPeriod = r.individual.indicators.length === 1 
          ? LOOPBACK_CONFIG.singleIndicator 
          : '5-15'
        
        // Format indicator parameters for display
        const indicatorDetails = r.individual.indicators.map((indicator: GeneticIndicator, index: number) => {
          let details = indicator.type.toString()
          if (indicator.period) details += ` (${indicator.period})`
          if (indicator.fastPeriods && indicator.slowPeriods) details += ` (${indicator.fastPeriods}/${indicator.slowPeriods})`
          if (indicator.multiplier) details += ` (${indicator.multiplier.toFixed(1)}x)`
          return details
        }).join(', ')
        
        const riskRewardRatio = r.individual.takeProfit / r.individual.stopLoss
        const riskRewardColor = riskRewardRatio >= 2 ? '🟢' : riskRewardRatio >= 1.5 ? '🟡' : '🔴'
        
        // Calculate drawdown color based on severity
        const maxDrawdownPc = Math.abs(r.backtest?.statistics?.maxDrawdownPc || 0)
        const drawdownColor = maxDrawdownPc <= 10 ? '🟢' : maxDrawdownPc <= 25 ? '🟡' : '🔴'
        
        return `Phase: ${r.phase || 'Unknown'}<br>` +
               `Gen: ${r.generation}<br>` +
               `Fitness: ${r.fitness.toFixed(2)}<br>` +
               `PnL: $${r.backtest?.statistics?.totalPnL?.toFixed(2) || 'N/A'}<br>` +
               `Win Rate: ${r.backtest?.winRate?.toFixed(1) || 'N/A'}%<br>` +
               `Max Drawdown: ${drawdownColor} ${maxDrawdownPc.toFixed(1)}%<br>` +
               `SL: ${r.individual.stopLoss.toFixed(1)}%<br>` +
               `TP: ${r.individual.takeProfit.toFixed(1)}%<br>` +
               `R/R: ${riskRewardColor} ${riskRewardRatio.toFixed(2)}<br>` +
               `Leverage: ${r.individual.leverage}x<br>` +
               `Cooldown: ${r.individual.cooldownPeriod} candles<br>` +
               `Max Loss Streak: ${r.individual.maxLossStreak}<br>` +
               `Max Time: ${r.individual.maxPositionTimeHours}h<br>` +
               `Indicators: ${indicatorDetails}<br>` +
               `Loopback: ${loopbackPeriod}`
      }),
      hovertemplate: '%{text}<extra></extra>',
    },
  ] : []
  
  // Prepare 3D plot data for TP% vs SL% vs PnL
  const plotDataTPvsSL = results.length > 0 ? [
    {
      type: 'scatter3d' as const,
      mode: 'markers' as const,
      x: results.map(r => r.individual.takeProfit),
      y: results.map(r => r.individual.stopLoss),
      z: results.map(r => r.backtest?.statistics?.totalPnL || 0),
      marker: {
        size: 5,
        color: results.map(r => {
          const pnl = r.backtest?.statistics?.totalPnL || 0
          return pnl > 0 ? pnl : 0 // Color by PnL (positive values only for better visualization)
        }),
        colorscale: 'RdYlGn' as const, // Red to Yellow to Green
        opacity: 0.8,
        colorbar: {
          title: 'PnL ($)',
          titleside: 'right',
        },
      },
      text: results.map(r => {
        const loopbackPeriod = r.individual.indicators.length === 1 
          ? LOOPBACK_CONFIG.singleIndicator 
          : '5-15'
        
        // Format indicator parameters for display
        const indicatorDetails = r.individual.indicators.map((indicator: GeneticIndicator, index: number) => {
          let details = indicator.type.toString()
          if (indicator.period) details += ` (${indicator.period})`
          if (indicator.fastPeriods && indicator.slowPeriods) details += ` (${indicator.fastPeriods}/${indicator.slowPeriods})`
          if (indicator.multiplier) details += ` (${indicator.multiplier.toFixed(1)}x)`
          return details
        }).join(', ')
        
        const riskRewardRatio = r.individual.takeProfit / r.individual.stopLoss
        const riskRewardColor = riskRewardRatio >= 2 ? '🟢' : riskRewardRatio >= 1.5 ? '🟡' : '🔴'
        
        // Calculate drawdown color based on severity
        const maxDrawdownPc = Math.abs(r.backtest?.statistics?.maxDrawdownPc || 0)
        const drawdownColor = maxDrawdownPc <= 10 ? '🟢' : maxDrawdownPc <= 25 ? '🟡' : '🔴'
        
        return `Phase: ${r.phase || 'Unknown'}<br>` +
               `Gen: ${r.generation}<br>` +
               `Fitness: ${r.fitness.toFixed(2)}<br>` +
               `PnL: $${r.backtest?.statistics?.totalPnL?.toFixed(2) || 'N/A'}<br>` +
               `Win Rate: ${r.backtest?.winRate?.toFixed(1) || 'N/A'}%<br>` +
               `Max Drawdown: ${drawdownColor} ${maxDrawdownPc.toFixed(1)}%<br>` +
               `SL: ${r.individual.stopLoss.toFixed(1)}%<br>` +
               `TP: ${r.individual.takeProfit.toFixed(1)}%<br>` +
               `R/R: ${riskRewardColor} ${riskRewardRatio.toFixed(2)}<br>` +
               `Leverage: ${r.individual.leverage}x<br>` +
               `Cooldown: ${r.individual.cooldownPeriod} candles<br>` +
               `Max Loss Streak: ${r.individual.maxLossStreak}<br>` +
               `Max Time: ${r.individual.maxPositionTimeHours}h<br>` +
               `Indicators: ${indicatorDetails}<br>` +
               `Loopback: ${loopbackPeriod}`
      }),
      hovertemplate: '%{text}<extra></extra>',
    },
  ] : []
  
  return (
    <div className="container mx-auto p-4">
      <h2 className="text-2xl font-bold mb-6">Genetic Algorithm Optimization</h2>
      
      {/* Configuration Panel */}
      <div className="card bg-base-100 shadow-xl mb-6">
        <div className="card-body">
          <h3 className="card-title">Genetic Algorithm Configuration</h3>
          
          {/* Selection Method Descriptions */}
          <div className="alert alert-info mb-4">
            <div>
              <h4 className="font-bold">Selection Methods:</h4>
              <ul className="text-sm mt-2">
                <li><strong>Tournament Selection:</strong> Randomly selects 3 individuals and picks the best one</li>
                <li><strong>Roulette Wheel:</strong> Selects individuals based on their fitness proportion</li>
                <li><strong>Fitness Weighted:</strong> Normalizes fitness scores and uses weighted probabilities</li>
              </ul>
              <p className="text-sm mt-2"><strong>Elitism:</strong> Preserves the top percentage of individuals unchanged in the next generation</p>
            </div>
          </div>
          
          {/* Four-Phase Optimization Strategy */}
          <div className="alert alert-info mb-4">
            <div>
              <h4 className="font-bold">Four-Phase Optimization Strategy:</h4>
              <p className="text-sm mt-2">
                <strong>Phase 1:</strong> Tests individual indicators using the full user-specified generation count. Each generation runs populationSize backtests. The algorithm cycles through eligible indicators to ensure comprehensive coverage, but the total backtests remain populationSize per generation.
              </p>
              <p className="text-sm mt-1">
                <strong>Phase 2:</strong> Tests 2-indicator combinations using the full user-specified generation count. Each generation runs populationSize backtests with combinations of the best indicators from Phase 1.
              </p>
              <p className="text-sm mt-1">
                <strong>Phase 3:</strong> Tests 3-indicator combinations using the full user-specified generation count. Each generation runs populationSize backtests with combinations of the best indicators from Phase 1.
              </p>
              <p className="text-sm mt-1">
                <strong>Phase 4:</strong> Tests 4-indicator combinations using the full user-specified generation count. Each generation runs populationSize backtests with combinations of the best indicators from Phase 1.
              </p>
              <p className="text-sm mt-1">
                <strong>No Duplicates:</strong> Each scenario contains unique indicator types to avoid redundancy.
              </p>
              <p className="text-sm mt-1">
                <strong>Population Size:</strong> For Phase 1, set population size to at least the number of indicators you want to test to ensure comprehensive coverage.
              </p>
              <p className="text-sm mt-1">
                <strong>Total Backtests:</strong> {calculateTotalBacktests()} backtests will be run. 
                {(() => {
                  const populationSize = formValues.populationSize || GENETIC_CONFIG.populationSize
                  const totalGenerations = formValues.generations || GENETIC_CONFIG.generations
                  return ` (${totalGenerations}×${populationSize}×4 phases = ${totalGenerations * populationSize * 4} total)`
                })()}.
              </p>
              <p className="text-sm mt-1">
                <strong>Parameter Optimization:</strong> Uses CustomScenario defaults as starting points with 70% bias towards proven values.
              </p>
            </div>
          </div>
          
          {/* Risk-Reward Ratio Constraint */}
          <div className="alert alert-success mb-4">
            <div>
              <h4 className="font-bold">Risk-Reward Ratio Constraint:</h4>
              <p className="text-sm mt-2">
                All generated individuals are guaranteed to have a positive risk-reward ratio (R:R ≥ 1.1). 
                The algorithm automatically adjusts take profit levels to ensure profitable trading strategies.
              </p>
              <p className="text-sm mt-1">
                <strong>Parameter Ranges:</strong> Stop Loss: 2-15% | Take Profit: 0.9-{formValues.maxTakeProfit || 4}% | Minimum TP: 0.9%
              </p>
              <p className="text-sm mt-1">
                <strong>R:R Ratio Legend:</strong> 🟢 ≥2.0 (Excellent) | 🟡 1.5-2.0 (Good) | 🔴 1.1-1.5 (Minimum)
              </p>
            </div>
          </div>
          
          {/* Fitness Function Components */}
          <div className="alert alert-info mb-4">
            <div>
              <h4 className="font-bold">Fitness Function Components:</h4>
              <p className="text-sm mt-2">
                <strong>PnL Score (30%):</strong> Normalized profit/loss performance
              </p>
              <p className="text-sm mt-1">
                <strong>Win Rate Score (20%):</strong> Percentage of winning trades
              </p>
              <p className="text-sm mt-1">
                <strong>Risk-Reward Score (20%):</strong> Ratio of winning to losing trades
              </p>
              <p className="text-sm mt-1">
                <strong>Consistency Score (10%):</strong> Stability of performance over time
              </p>
              <p className="text-sm mt-1">
                <strong>Risk-Reward Bonus (10%):</strong> Bonus for better R:R ratios (&gt;1.1)
              </p>
              <p className="text-sm mt-1">
                <strong>Drawdown Score (10%):</strong> Penalty for maximum drawdown (0% = 1.0, 50% = 0.0)
              </p>
              <p className="text-sm mt-1">
                <strong>Drawdown Legend:</strong> 🟢 ≤10% (Low Risk) | 🟡 10-25% (Medium Risk) | 🔴 &gt;25% (High Risk)
              </p>
            </div>
          </div>
          
          {/* Eligible Indicators Feature */}
          <div className="alert alert-info mb-4">
            <div>
              <h4 className="font-bold">Adaptive Indicator Selection:</h4>
              <p className="text-sm mt-2">
                <strong>Manual Selection:</strong> Choose which indicators to include in the optimization process.
              </p>
              <p className="text-sm mt-1">
                <strong>Automatic Removal:</strong> After Phase 1, indicators with fitness scores below the configured threshold (default: 10% of max fitness) are automatically removed from consideration.
              </p>
              <p className="text-sm mt-1">
                <strong>Smart Optimization:</strong> The algorithm focuses on promising indicators, improving convergence and reducing computation time.
              </p>
              <p className="text-sm mt-1">
                <strong>No Duplicates:</strong> Each scenario is guaranteed to contain unique indicator types only.
              </p>
            </div>
          </div>
          
          <form className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
            <div className="form-control">
              <label className="label">
                <span className="label-text">Ticker</span>
              </label>
              <select 
                className="select select-bordered w-full" 
                {...register('ticker')}
              >
                {Object.values(Ticker).map(ticker => (
                  <option key={ticker} value={ticker}>{ticker}</option>
                ))}
              </select>
            </div>
            
            <div className="form-control">
              <label className="label">
                <span className="label-text">Timeframe</span>
              </label>
              <select 
                className="select select-bordered w-full" 
                {...register('timeframe')}
              >
                {Object.values(Timeframe).map(tf => (
                  <option key={tf} value={tf}>{tf}</option>
                ))}
              </select>
            </div>
            
            <div className="form-control">
              <label className="label">
                <span className="label-text">Start Date</span>
              </label>
              <input 
                type="date" 
                className="input input-bordered w-full" 
                {...register('startDate')}
              />
            </div>
            
            <div className="form-control">
              <label className="label">
                <span className="label-text">End Date</span>
              </label>
              <input 
                type="date" 
                className="input input-bordered w-full" 
                {...register('endDate')}
              />
            </div>
            
            <div className="form-control">
              <label className="label">
                <span className="label-text">Balance</span>
              </label>
              <input 
                type="number" 
                className="input input-bordered w-full" 
                {...register('balance', {valueAsNumber: true})}
              />
            </div>
            
            <div className="form-control">
              <label className="label">
                <span className="label-text">Population Size</span>
              </label>
              <input 
                type="number" 
                className="input input-bordered w-full" 
                {...register('populationSize', {valueAsNumber: true})}
              />
            </div>
            
            <div className="form-control">
              <label className="label">
                <span className="label-text">Generations</span>
              </label>
              <input 
                type="number" 
                className="input input-bordered w-full" 
                {...register('generations', {valueAsNumber: true})}
              />
            </div>
            
            <div className="form-control">
              <label className="label">
                <span className="label-text">Mutation Rate</span>
              </label>
              <input 
                type="number" 
                step="0.1"
                className="input input-bordered w-full" 
                {...register('mutationRate', {valueAsNumber: true})}
              />
            </div>
            
            <div className="form-control">
              <label className="label">
                <span className="label-text">Selection Method</span>
              </label>
              <select 
                className="select select-bordered w-full" 
                {...register('selectionMethod')}
              >
                <option value="tournament">Tournament Selection</option>
                <option value="roulette">Roulette Wheel</option>
                <option value="fitness-weighted">Fitness Weighted</option>
              </select>
            </div>
            
            <div className="form-control">
              <label className="label">
                <span className="label-text">Elitism Percentage</span>
              </label>
              <input 
                type="number" 
                min="1"
                max="50"
                step="1"
                className="input input-bordered w-full" 
                {...register('elitismPercentage', {valueAsNumber: true})}
              />
            </div>
            
            <div className="form-control">
              <label className="label">
                <span className="label-text">Max Take Profit (%)</span>
              </label>
              <input 
                type="number" 
                min="0.9"
                max="8"
                step="0.1"
                className="input input-bordered w-full" 
                {...register('maxTakeProfit', {valueAsNumber: true})}
              />
            </div>
            

            
            <div className="form-control">
              <label className="label">
                <span className="label-text">Eligible Indicators</span>
              </label>
              <div className="grid grid-cols-2 gap-2 max-h-40 overflow-y-auto">
                {ALL_INDICATORS.map(indicator => (
                  <label key={indicator} className="flex items-center gap-2 cursor-pointer">
                    <input
                      type="checkbox"
                      checked={eligibleIndicators.includes(indicator)}
                      onChange={(e) => {
                        if (e.target.checked) {
                          setEligibleIndicators(prev => [...prev, indicator])
                          setRemovedIndicators(prev => {
                            const newSet = new Set(prev)
                            newSet.delete(indicator)
                            return newSet
                          })
                        } else {
                          setEligibleIndicators(prev => prev.filter(i => i !== indicator))
                          setRemovedIndicators(prev => new Set([...prev, indicator]))
                        }
                      }}
                      className="checkbox checkbox-sm"
                    />
                    <span className="text-sm">{indicator}</span>
                  </label>
                ))}
              </div>
              <div className="text-xs text-gray-500 mt-1">
                {eligibleIndicators.length} of {ALL_INDICATORS.length} indicators selected
              </div>
            </div>
          </form>
        </div>
      </div>
      
      {/* Control Panel */}
      <div className="flex gap-4 mb-6">
        <button
          onClick={runGeneticAlgorithm}
          disabled={isRunning}
          className="btn btn-primary"
        >
          {isRunning ? 'Running...' : `Start Genetic Algorithm (${calculateTotalBacktests()} backtests)`}
        </button>
        
        <button
          onClick={() => {
            setEligibleIndicators(ALL_INDICATORS)
            setRemovedIndicators(new Set())
          }}
          disabled={isRunning}
          className="btn btn-secondary"
        >
          Reset Indicators
        </button>
        
        {showResults && (
          <button
            onClick={() => setShowResults(false)}
            className="btn btn-secondary"
          >
            Hide Results
          </button>
        )}
      </div>
      
      {/* Progress */}
      {isRunning && (
        <div className="mb-6">
          <div className="flex justify-between mb-2">
            <span>Phase: {currentPhase === 'phase1' ? '1 - Single Indicators' : currentPhase === 'phase2' ? '2 - 2-Indicators' : currentPhase === 'phase3' ? '3 - 3-Indicators' : '4 - 4-Indicators'}</span>
            <span>Generation: {currentGeneration}/{(formValues.generations || GENETIC_CONFIG.generations) * 4}</span>
            <span>Individual: {currentIndividual}/{geneticConfig.populationSize}</span>
            <span>Best Fitness: {bestFitness.toFixed(2)}</span>
          </div>
          <div className="flex justify-between mb-2">
            <span>Overall Progress: {calculateProgress().percentage}%</span>
            <span>Results: {results.length}</span>
            <span>Best Single Indicators: {bestSingleIndicators.length}</span>
            {currentPhase === 'phase1' && (
              <span>Indicators Tested: {new Set(results.filter(r => r.phase === 'phase1').map(r => r.individual.indicators[0]?.type)).size}/{eligibleIndicators.length}</span>
            )}
          </div>
          <div className="flex justify-between mb-2">
            <span>Eligible Indicators: {eligibleIndicators.length}/{ALL_INDICATORS.length}</span>
            <span>Removed Indicators: {removedIndicators.size}</span>
            <span>Bad Indicator Threshold: 0.112 (10% of max fitness)</span>
            {removedIndicators.size > 0 && (
              <span className="text-warning">
                Removed: {Array.from(removedIndicators).slice(0, 3).join(', ')}
                {removedIndicators.size > 3 && ` +${removedIndicators.size - 3} more`}
              </span>
            )}
          </div>
          <div className="flex justify-between mb-2">
            <span>Average Time per Backtest: {backtestTimes.length > 0 ? formatTimeDuration(backtestTimes.reduce((sum, time) => sum + time, 0) / backtestTimes.length) : 'Calculating...'}</span>
            <span>Estimated Completion: {estimatedCompletion}</span>
          </div>
          <progress 
            className="progress progress-info w-full" 
            value={calculateProgress().completed}
            max={calculateProgress().total}
          />
        </div>
      )}
      
      {/* Results */}
      {showResults && results.length > 0 && (
        <div className="space-y-6">
          {/* 3D Visualizations */}
          <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
            {/* Fitness vs Score vs Win Rate */}
            <div className="card bg-base-100 shadow-xl">
              <div className="card-body">
                <h3 className="card-title">Fitness vs Score vs Win Rate</h3>
                <Fitness3DPlot backtests={results.map(r => r.backtest).filter(Boolean) as Backtest[]} theme={theme} />
              </div>
            </div>
            
            {/* TP% vs SL% vs PnL */}
            <div className="card bg-base-100 shadow-xl">
              <div className="card-body">
                <h3 className="card-title">Take Profit vs Stop Loss vs PnL</h3>
                <TPvsSLvsPnL3DPlot backtests={results.map(r => r.backtest).filter(Boolean) as Backtest[]} theme={theme} />
              </div>
            </div>
          </div>
          
          {/* Strategy Comparison Radar Chart */}
          <div className="mt-6">
            <IndicatorsComparison backtests={results.map(r => r.backtest).filter(Boolean) as Backtest[]} />
          </div>
          
          {/* Results Table */}
          <div className="card bg-base-100 shadow-xl">
            <div className="card-body">
              <h3 className="card-title">Best Results</h3>
              <BacktestTable list={results.map(r => r.backtest).filter(Boolean) as LightBacktestResponse[]} displaySummary={false} />
            </div>
          </div>
        </div>
      )}
    </div>
  )
}

export default BacktestGenetic 