/**
 * False Signal Detector
 * Detects false signals (retail noise, spoofed flow, MOC noise)
 */

/**
 * Detects false signals in flow data
 * @param {Array} flowData - Array of flow records
 * @returns {Array} Array of false signals with warnings
 */
export function detectFalseSignals(flowData) {
  const falseSignals = [];
  
  for (const flow of flowData) {
    const warnings = [];
    let falseSignalType = null;
    
    // 1. DUMB MONEY TRAP (OTM lotto tickets)
    if (isDumbMoneyTrap(flow)) {
      falseSignalType = 'DUMB_MONEY_TRAP';
      warnings.push('⚠️ All OTM lotto tickets (>20% OTM) = retail speculation');
    }
    
    // 2. ROBINHOOD HOURS (9:30-10:00 AM = retail FOMO)
    if (isRobinhoodHours(flow)) {
      falseSignalType = 'ROBINHOOD_HOURS';
      warnings.push('⚠️ First 30min of RTH = retail panic buying');
    }
    
    // 3. SMALL PREMIUM (< $50K = retail size)
    if (isSmallPremium(flow)) {
      falseSignalType = 'SMALL_PREMIUM';
      warnings.push('⚠️ Premium < $50K = likely retail, not institutional');
    }
    
    // 4. SPOOFED LIQUIDITY (dark pool but price flat)
    if (isSpoofedLiquidity(flow)) {
      falseSignalType = 'SPOOFED_LIQUIDITY';
      warnings.push('⚠️ Dark pool print but price unchanged = fake/hedged flow');
    }
    
    // 5. MOC NOISE (last 30min of session)
    if (isMOCNoise(flow)) {
      falseSignalType = 'MOC_NOISE';
      warnings.push('⚠️ Last 30min of session = MOC rebalancing, not directional');
    }
    
    // 6. OPPOSITE FLOW IMMEDIATELY AFTER (hedge unwind)
    if (hasImmediateOppositeFlow(flow)) {
      falseSignalType = 'HEDGE_UNWIND';
      warnings.push('⚠️ Opposite flow within 5min = hedge unwind, not conviction');
    }
    
    if (falseSignalType) {
      falseSignals.push({
        ...flow,
        falseSignalType,
        warnings,
        shouldFilter: true
      });
    }
  }
  
  return falseSignals;
}

/**
 * Check if flow is a dumb money trap (OTM lotto tickets)
 */
function isDumbMoneyTrap(flow) {
  // Check if all options are >20% OTM
  if (!flow.mny_pct && flow.mny_pct !== 0) return false;
  
  const isOTM = flow.mny_pct < 0; // Negative = OTM
  const otmDistance = Math.abs(flow.mny_pct);
  
  return isOTM && otmDistance > 20;
}

/**
 * Check if flow occurred during Robinhood hours (first 30min of RTH)
 */
function isRobinhoodHours(flow) {
  // First 30min of RTH (9:30-10:00 AM ET)
  if (flow.session_bucket !== 'RTH') return false;
  
  const flowTime = new Date(flow.flow_ts_utc || flow.CreatedDate || 0);
  
  // Handle timezone conversion (UTC to ET)
  // ET is UTC-5 (EST) or UTC-4 (EDT)
  // For simplicity, we'll use UTC-5 as approximation
  // Better approach: use a timezone library, but for now this works
  const hour = flowTime.getUTCHours() - 5; // Convert to ET (approximate)
  const minute = flowTime.getUTCMinutes();
  
  // Check if time is between 9:30 AM and 10:00 AM ET
  if (hour === 9 && minute >= 30) return true;
  if (hour === 10 && minute === 0) return true;
  
  return false;
}

/**
 * Check if premium is too small (retail size)
 */
function isSmallPremium(flow) {
  const premium = flow.premium_num || flow.Premium || 0;
  
  // Handle string format like "500 K" or "1.2 M"
  if (typeof premium === 'string') {
    const numStr = premium.replace(/[^0-9.-]/g, '');
    const num = parseFloat(numStr);
    if (!isNaN(num)) {
      if (premium.toUpperCase().includes('M')) {
        return num * 1000000 < 50000;
      } else if (premium.toUpperCase().includes('K')) {
        return num * 1000 < 50000;
      }
      return num < 50000;
    }
  }
  
  return premium < 50000;
}

/**
 * Check if flow is spoofed liquidity (dark pool but price flat)
 */
function isSpoofedLiquidity(flow) {
  // Dark pool print but price didn't move
  const isDarkPool = flow.near_alert_type?.includes('DarkPool') || 
                     flow.near_alert_type?.includes('dark') ||
                     flow.near_alert_type?.includes('DARK');
  
  // Check price movement (use various price change fields)
  const pctChange = flow.pct_vs_prior_close || 
                    flow.pct_vs_rth_open || 
                    flow.PctVsPriorClose ||
                    flow.PctVsRthOpen ||
                    0;
  
  const priceFlat = Math.abs(pctChange) < 0.1; // <0.1% move
  
  return isDarkPool && priceFlat;
}

/**
 * Check if flow is MOC noise (last 30min of session)
 */
function isMOCNoise(flow) {
  // Last 30min of session (3:30-4:00 PM ET)
  if (flow.session_bucket !== 'RTH') return false;
  
  const flowTime = new Date(flow.flow_ts_utc || flow.CreatedDate || 0);
  const hour = flowTime.getUTCHours() - 5; // Convert to ET (approximate)
  const minute = flowTime.getUTCMinutes();
  
  return hour === 15 && minute >= 30; // 3:30 PM ET onwards
}

/**
 * Check if opposite flow appeared immediately after (hedge unwind)
 * This requires querying recent flow history - placeholder for now
 */
function hasImmediateOppositeFlow(flow) {
  // This would require access to recent flow history
  // For now, return false - can be enhanced later with flow history lookup
  return false;
}

// ===== SCORING FUNCTION =====

/**
 * Calculate noise score for a flow (0-100)
 * Higher score = more noise/retail activity
 */
export function calculateNoiseScore(flow) {
  // Score from 0 (clean signal) to 100 (pure noise)
  let noiseScore = 0;
  
  if (isDumbMoneyTrap(flow)) noiseScore += 40;
  if (isRobinhoodHours(flow)) noiseScore += 25;
  if (isSmallPremium(flow)) noiseScore += 20;
  if (isSpoofedLiquidity(flow)) noiseScore += 30;
  if (isMOCNoise(flow)) noiseScore += 35;
  if (hasImmediateOppositeFlow(flow)) noiseScore += 25;
  
  return Math.min(noiseScore, 100);
}

/**
 * Get signal quality label based on noise score
 */
export function getSignalQuality(noiseScore) {
  if (noiseScore >= 70) return 'TRASH';
  if (noiseScore >= 40) return 'QUESTIONABLE';
  if (noiseScore >= 20) return 'ACCEPTABLE';
  return 'CLEAN';
}

/**
 * Get warnings for a flow based on detected issues
 */
export function getFlowWarnings(flow) {
  const warnings = [];
  
  if (isDumbMoneyTrap(flow)) {
    warnings.push('⚠️ All OTM lotto tickets (>20% OTM) = retail speculation');
  }
  if (isRobinhoodHours(flow)) {
    warnings.push('⚠️ First 30min of RTH = retail panic buying');
  }
  if (isSmallPremium(flow)) {
    warnings.push('⚠️ Premium < $50K = likely retail, not institutional');
  }
  if (isSpoofedLiquidity(flow)) {
    warnings.push('⚠️ Dark pool print but price unchanged = fake/hedged flow');
  }
  if (isMOCNoise(flow)) {
    warnings.push('⚠️ Last 30min of session = MOC rebalancing, not directional');
  }
  if (hasImmediateOppositeFlow(flow)) {
    warnings.push('⚠️ Opposite flow within 5min = hedge unwind, not conviction');
  }
  
  return warnings;
}

