/**
 * Scanner Enhancer
 * Adds convergence detection, stealth vs aggressive classification, and false signal filtering
 */

/**
 * Detect signal convergence (multiple signals firing together within time window)
 * @param {Array} signals - Array of signal objects with ticker and timestamp
 * @param {number} windowMinutes - Time window for convergence (default 10)
 * @param {number} minSignals - Minimum number of signals for convergence (default 3)
 * @returns {Object} Convergence analysis per ticker
 */
export function detectSignalConvergence(signals, windowMinutes = 10, minSignals = 3) {
  if (!signals || signals.length === 0) {
    return {};
  }
  
  // Group signals by ticker
  const tickerSignals = {};
  signals.forEach(signal => {
    const ticker = signal.ticker || signal.symbol;
    if (!ticker) return;
    
    if (!tickerSignals[ticker]) {
      tickerSignals[ticker] = [];
    }
    
    tickerSignals[ticker].push({
      ...signal,
      timestamp: new Date(signal.timestamp || signal.event_ts_local || Date.now())
    });
  });
  
  const convergenceResults = {};
  
  // Check each ticker for convergence
  for (const [ticker, tickerSignalList] of Object.entries(tickerSignals)) {
    // Sort by timestamp
    tickerSignalList.sort((a, b) => a.timestamp - b.timestamp);
    
    // Check for convergence windows
    for (let i = 0; i < tickerSignalList.length; i++) {
      const windowStart = tickerSignalList[i].timestamp;
      const windowEnd = new Date(windowStart.getTime() + windowMinutes * 60 * 1000);
      
      const signalsInWindow = tickerSignalList.filter(s => 
        s.timestamp >= windowStart && s.timestamp <= windowEnd
      );
      
      if (signalsInWindow.length >= minSignals) {
        // Convergence detected!
        const signalTypes = signalsInWindow.map(s => s.signal || s.type || s.sig || 'UNKNOWN');
        const uniqueTypes = [...new Set(signalTypes)];
        
        convergenceResults[ticker] = {
          hasConvergence: true,
          convergenceTime: windowStart.toISOString(),
          signalCount: signalsInWindow.length,
          signalTypes: uniqueTypes,
          signals: signalsInWindow,
          reasoning: generateConvergenceReasoning(uniqueTypes),
          label: '🔥 CONVERGENCE EVENT'
        };
        
        break; // Only report first convergence per ticker
      }
    }
    
    // If no convergence found, mark as no convergence
    if (!convergenceResults[ticker]) {
      convergenceResults[ticker] = {
        hasConvergence: false,
        signalCount: tickerSignalList.length
      };
    }
  }
  
  return convergenceResults;
}

/**
 * Generate reasoning for convergence event
 */
function generateConvergenceReasoning(signalTypes) {
  const typeMap = {
    'Dark/Block cluster': 'Dark pool',
    'ORB momentum': 'ORB',
    '52W High + $Vol': 'Breakout',
    'Options + Prints': 'Options',
    'Stealth Dark': 'Stealth accumulation',
    'Shock Tape': 'Momentum',
    'Low + VolPop': 'Reversal'
  };
  
  const mapped = signalTypes.map(t => typeMap[t] || t).filter(Boolean);
  
  if (mapped.length >= 3) {
    return `${mapped.join(' + ')} = Institutional momentum ignition`;
  }
  
  return `${mapped.join(' + ')} = Coordinated activity`;
}

/**
 * Classify trade horizon (SCALP, INTRADAY, SWING)
 * @param {Object} scannerResult - Scanner result with signals and metadata
 * @returns {Object} Classification with horizon and reasoning
 */
export function classifyTradeHorizon(scannerResult) {
  const reasons = scannerResult.reasons || '';
  const score = scannerResult.score || 0;
  const rvol = scannerResult.rvol_peak || 0;
  const avgRvol = scannerResult.avg_rvol || 0;
  
  // Check for SCALP indicators (ORB + Shock Tape + high rVol)
  const hasORB = reasons.includes('ORB momentum');
  const hasShockTape = reasons.includes('Shock Tape');
  const hasHighRvol = rvol >= 150 || avgRvol >= 150;
  
  if ((hasORB || hasShockTape) && hasHighRvol) {
    return {
      horizon: 'SCALP',
      reasoning: 'ORB + Shock Tape + high rVol = institutions chasing NOW = scalp trade (minutes to hours)',
      expectedHoldTime: 'Minutes to hours',
      maxHoldTime: '2 hours'
    };
  }
  
  // Check for INTRADAY indicators (Options + Prints + RTH session)
  const hasOptions = reasons.includes('Options + Prints');
  const hasBlock = reasons.includes('Dark/Block cluster');
  const hasBreakout = reasons.includes('52W High + $Vol');
  
  if ((hasOptions || hasBlock) && (hasBreakout || hasORB)) {
    return {
      horizon: 'INTRADAY',
      reasoning: `${hasOptions ? 'Options' : 'Block prints'} + ${hasBreakout ? 'Breakout' : 'ORB'} = intraday momentum`,
      expectedHoldTime: '2-4 hours',
      maxHoldTime: 'EOD (cut if no move)'
    };
  }
  
  // Check for SWING indicators (Stealth Dark + low rVol + accumulation)
  const hasStealth = reasons.includes('Stealth Dark');
  const hasLowRvol = (rvol < 100 && avgRvol < 100) || !hasHighRvol;
  
  if (hasStealth && hasLowRvol) {
    return {
      horizon: 'SWING',
      reasoning: 'Stealth Dark + low rVol = institutions accumulating SLOWLY = swing trade (1-5 days)',
      expectedHoldTime: '1-5 days',
      maxHoldTime: '5 days'
    };
  }
  
  // Default to INTRADAY if score is high
  if (score >= 6) {
    return {
      horizon: 'INTRADAY',
      reasoning: 'High score with multiple signals = intraday momentum',
      expectedHoldTime: '2-4 hours',
      maxHoldTime: 'EOD'
    };
  }
  
  // Default classification
  return {
    horizon: 'INTRADAY',
    reasoning: 'Standard intraday setup',
    expectedHoldTime: '2-4 hours',
    maxHoldTime: 'EOD'
  };
}

/**
 * Filter false signals (retail noise, spoofed liquidity, EOD noise)
 * @param {Object} scannerResult - Scanner result
 * @param {Object} options - Filtering options
 * @returns {Object} Filter result with flags and warnings
 */
export function filterFalseSignals(scannerResult, options = {}) {
  const {
    checkRetailNoise = true,
    checkSpoofedLiquidity = true,
    checkEODNoise = true,
    currentTime = new Date()
  } = options;
  
  const warnings = [];
  const flags = [];
  let isFalseSignal = false;
  
  const reasons = scannerResult.reasons || '';
  const lastSeen = scannerResult.last_seen_ts || scannerResult.last_seen_ct;
  const ticker = scannerResult.ticker;
  
  // 1. Dumb Money Trap Detection
  if (checkRetailNoise) {
    // Check if it's during retail hours (9:30-10 AM)
    const signalTime = lastSeen ? new Date(lastSeen) : currentTime;
    const hour = signalTime.getHours();
    const minute = signalTime.getMinutes();
    
    if (hour === 9 && minute >= 30 && minute < 60) {
      flags.push('RETAIL_HOURS');
      warnings.push('⚠️ Retail Noise - Signals firing during retail hours (9:30-10 AM) = Fade This');
      
      // If it's just volume spike without institutional signals, mark as false
      if (!reasons.includes('Dark/Block') && !reasons.includes('Options') && 
          !reasons.includes('Stealth')) {
        isFalseSignal = true;
      }
    }
    
    // Check for OTM lotto tickets (would need options data - simplified check)
    // This is a placeholder - real implementation would query options flow
    if (reasons.includes('Options') && !reasons.includes('Dark/Block') && 
        !reasons.includes('Stealth')) {
      warnings.push('⚠️ Options flow without institutional prints = potential retail lotto tickets');
    }
  }
  
  // 2. Spoofed Liquidity Detection
  if (checkSpoofedLiquidity) {
    // Check if dark pool/block prints appear but price doesn't move
    // This would require price data comparison - simplified check
    if (reasons.includes('Dark/Block cluster') && scannerResult.rvol_peak < 50) {
      flags.push('SPOOFED_LIQUIDITY');
      warnings.push('🚫 Fake Flow - Dark pool prints but low rVol = potential spoofed liquidity');
      
      // If multiple block prints in quick succession without price movement
      if (scannerResult.notional_20m && parseFloat(scannerResult.notional_20m.replace(/[^0-9.]/g, '')) > 10) {
        isFalseSignal = true;
      }
    }
  }
  
  // 3. End-of-Day Noise Detection
  if (checkEODNoise) {
    const signalTime = lastSeen ? new Date(lastSeen) : currentTime;
    const hour = signalTime.getHours();
    const minute = signalTime.getMinutes();
    
    // Last 30 minutes of session (3:30-4 PM)
    if (hour === 15 && minute >= 30) {
      flags.push('EOD_NOISE');
      warnings.push('⏰ MOC Noise - Signals firing in last 30min (3:30-4 PM) = MOC rebalancing, not directional = Wait for Tomorrow');
      
      // Mark as false if it's just volume-based signals
      if (!reasons.includes('Dark/Block') && !reasons.includes('Stealth')) {
        isFalseSignal = true;
      }
    }
  }
  
  return {
    isFalseSignal,
    flags,
    warnings,
    shouldIgnore: isFalseSignal,
    shouldFade: flags.includes('RETAIL_HOURS') || flags.includes('EOD_NOISE'),
    shouldWait: flags.includes('EOD_NOISE')
  };
}

/**
 * Enhance scanner results with all new features
 * @param {Array} scannerResults - Array of scanner results
 * @returns {Array} Enhanced scanner results
 */
export function enhanceScannerResults(scannerResults) {
  if (!scannerResults || scannerResults.length === 0) {
    return [];
  }
  
  // Convert scanner results to signal format for convergence detection
  const signals = scannerResults.map(result => ({
    ticker: result.ticker,
    timestamp: result.last_seen_ts || new Date(),
    signal: result.reasons,
    type: 'scanner',
    score: result.score
  }));
  
  // Detect convergence
  const convergence = detectSignalConvergence(signals, 10, 3);
  
  // Enhance each result
  return scannerResults.map(result => {
    const ticker = result.ticker;
    const convergenceData = convergence[ticker] || { hasConvergence: false };
    const horizon = classifyTradeHorizon(result);
    const falseSignalCheck = filterFalseSignals(result);
    
    return {
      ...result,
      convergence: convergenceData,
      tradeHorizon: horizon,
      falseSignalFilter: falseSignalCheck,
      // Add display helpers
      convergenceDisplay: convergenceData.hasConvergence 
        ? `${convergenceData.label}: ${convergenceData.reasoning}`
        : 'No convergence',
      horizonDisplay: `${horizon.horizon} (${horizon.expectedHoldTime})`,
      falseSignalDisplay: falseSignalCheck.isFalseSignal 
        ? `🚫 ${falseSignalCheck.warnings.join(' | ')}`
        : null
    };
  });
}

