import express from 'express';
import { backtestSignal } from '../services/backtester.js';
import { rawQuery } from '../db.js';
import crypto from 'crypto';

const router = express.Router();

// In-memory storage for backtest results (in production, use database)
const backtestResults = new Map();

/**
 * POST /api/backtest/run
 * Run a single backtest
 */
router.post('/run', async (req, res) => {
  try {
    const {
      pattern,
      options = {}
    } = req.body;

    if (!pattern) {
      return res.status(400).json({
        success: false,
        error: 'Pattern is required'
      });
    }

    const {
      lookbackDays = 30,
      minOccurrences = 10
    } = options;

    const results = await backtestSignal(pattern, lookbackDays);
    
    // Generate backtest ID
    const backtestId = crypto.randomUUID();
    
    // Store results
    backtestResults.set(backtestId, {
      id: backtestId,
      pattern,
      options,
      results,
      createdAt: new Date().toISOString()
    });

    // Check if we have enough occurrences
    if (results.totalOccurrences < minOccurrences) {
      return res.json({
        success: true,
        backtestId,
        results: {
          ...results,
          warning: `Only ${results.totalOccurrences} occurrences found (minimum: ${minOccurrences})`
        },
        timestamp: new Date().toISOString()
      });
    }

    res.json({
      success: true,
      backtestId,
      results,
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    console.error('Backtest error:', error);
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * POST /api/backtest/batch
 * Run multiple backtests
 */
router.post('/batch', async (req, res) => {
  try {
    const { patterns, options = {} } = req.body;

    if (!patterns || !Array.isArray(patterns) || patterns.length === 0) {
      return res.status(400).json({
        success: false,
        error: 'Patterns array is required'
      });
    }

    const {
      lookbackDays = 30,
      minOccurrences = 10
    } = options;

    const results = [];

    for (const pattern of patterns) {
      try {
        const result = await backtestSignal(pattern, lookbackDays);
        results.push({
          pattern,
          ...result,
          hasEnoughData: result.totalOccurrences >= minOccurrences
        });
      } catch (error) {
        results.push({
          pattern,
          error: error.message,
          hasEnoughData: false
        });
      }
    }

    res.json({
      success: true,
      results,
      summary: {
        total: results.length,
        successful: results.filter(r => !r.error && r.hasEnoughData).length,
        insufficientData: results.filter(r => !r.error && !r.hasEnoughData).length,
        errors: results.filter(r => r.error).length
      },
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    console.error('Batch backtest error:', error);
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * GET /api/backtest/results/:id
 * Get backtest results by ID
 */
router.get('/results/:id', async (req, res) => {
  try {
    const { id } = req.params;

    const result = backtestResults.get(id);

    if (!result) {
      return res.status(404).json({
        success: false,
        error: 'Backtest result not found'
      });
    }

    res.json({
      success: true,
      ...result
    });
  } catch (error) {
    console.error('Get backtest result error:', error);
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * GET /api/backtest/compare
 * Compare multiple strategies side-by-side
 */
router.post('/compare', async (req, res) => {
  try {
    const { strategies, options = {} } = req.body;

    if (!strategies || !Array.isArray(strategies) || strategies.length === 0) {
      return res.status(400).json({
        success: false,
        error: 'Strategies array is required'
      });
    }

    const {
      lookbackDays = 30,
      minOccurrences = 10
    } = options;

    const comparison = [];

    for (const strategy of strategies) {
      try {
        const result = await backtestSignal(strategy.pattern, lookbackDays);
        
        comparison.push({
          name: strategy.name || 'Unnamed Strategy',
          pattern: strategy.pattern,
          ...result,
          hasEnoughData: result.totalOccurrences >= minOccurrences,
          score: calculateStrategyScore(result)
        });
      } catch (error) {
        comparison.push({
          name: strategy.name || 'Unnamed Strategy',
          pattern: strategy.pattern,
          error: error.message,
          hasEnoughData: false,
          score: 0
        });
      }
    }

    // Sort by score (descending)
    comparison.sort((a, b) => (b.score || 0) - (a.score || 0));

    res.json({
      success: true,
      comparison,
      summary: {
        total: comparison.length,
        ranked: comparison.map((c, i) => ({
          rank: i + 1,
          name: c.name,
          score: c.score,
          winRate: c.winRate,
          expectancy: c.expectancy
        }))
      },
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    console.error('Compare strategies error:', error);
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * Calculate a composite score for strategy ranking
 */
function calculateStrategyScore(result) {
  if (result.error || !result.hasEnoughData) return 0;
  
  const winRateScore = (result.winRate || 0) * 0.3;
  const expectancyScore = (result.expectancy || 0) * 0.3;
  const sharpeScore = (result.sharpeRatio || 0) * 0.2;
  const drawdownPenalty = (result.maxDrawdown || 0) * 0.1;
  const volumeScore = Math.min((result.totalOccurrences || 0) / 100, 1) * 0.1;
  
  return winRateScore + expectancyScore + sharpeScore - drawdownPenalty + volumeScore;
}

// Legacy endpoint for backward compatibility
router.post('/signal', async (req, res) => {
  try {
    const { pattern, lookbackDays = 90 } = req.body;
    
    const results = await backtestSignal(pattern, lookbackDays);
    
    res.json({
      success: true,
      results,
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    console.error('Backtest error:', error);
    res.status(500).json({ success: false, error: error.message });
  }
});

// Preset backtests
router.get('/presets', async (req, res) => {
  try {
    const presets = [
      {
        name: 'High Score + Tape Aligned',
        pattern: { rocketScoreMin: 5, tapeAligned: true }
      },
      {
        name: 'RTH Session Only',
        pattern: { rocketScoreMin: 3, session: 'RTH' }
      },
      {
        name: 'All High Score',
        pattern: { rocketScoreMin: 5 }
      }
    ];
    
    const results = [];
    for (const preset of presets) {
      const result = await backtestSignal(preset.pattern, 30);
      results.push({ ...preset, ...result });
    }
    
    res.json({ success: true, results });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * GET /api/backtest/date-analysis
 * Get symbols with rocket scores 2 or 3 on a specific date
 */
router.get('/date-analysis', async (req, res) => {
  try {
    const { date, minRocketScore = 2, maxRocketScore = 3, minPremium = 80000 } = req.query;
    
    // Parse numeric parameters
    const minRocket = parseFloat(minRocketScore) || 2;
    const maxRocket = parseFloat(maxRocketScore) || 3;
    const minPrem = parseFloat(minPremium) || 80000;
    
    if (!date) {
      return res.status(400).json({
        success: false,
        error: 'Date parameter is required (YYYY-MM-DD)'
      });
    }
    
    // Query to get symbols with rocket scores 2-3 on the date
    const query = `
      WITH base AS (
        SELECT
          ofm.ctid AS rid,
          UPPER(TRIM(ofm."Symbol")) AS symbol_norm,
          NULLIF(REGEXP_REPLACE(ofm."Spot"::text, '[\\s$,]', '', 'g'),'')::numeric AS spot_num,
          NULLIF(REGEXP_REPLACE(ofm."Premium"::text, '[\\s$,]', '', 'g'),'')::numeric AS premium_num,
          NULLIF(REGEXP_REPLACE(ofm."Volume"::text, '[\\s$,]', '', 'g'),'')::numeric AS vol_num,
          NULLIF(REGEXP_REPLACE(ofm."OI"::text, '[\\s$,]', '', 'g'),'')::numeric AS oi_num,
          NULLIF(REGEXP_REPLACE(ofm."Strike"::text, '[\\s$,]', '', 'g'),'')::numeric AS strike_num,
          NULLIF(REGEXP_REPLACE(ofm."Dte"::text, '[\\s$,]', '', 'g'),'')::numeric AS dte_num,
          NULLIF(REGEXP_REPLACE(ofm."ImpliedVolatility"::text, '[\\s$,]', '', 'g'),'')::numeric AS iv_num,
          CASE WHEN UPPER(TRIM(ofm."CallPut")) IN ('C','CALL','CALLS','CE') THEN 'CALL'
               WHEN UPPER(TRIM(ofm."CallPut")) IN ('P','PUT','PUTS','PE')   THEN 'PUT'
          END AS cp_norm,
          CASE
            WHEN UPPER(TRIM(ofm."Side")) IN ('A','AA','ASK','BUY','BOT','BTO','AT_ASK')  THEN 'BUY'
            WHEN UPPER(TRIM(ofm."Side")) IN ('B','BB','BID','SELL','SLD','STO','AT_BID') THEN 'SELL'
            ELSE NULL
          END AS side_norm,
          CASE
            WHEN ofm."CreatedDate"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN 
              to_timestamp(ofm."CreatedDate"::text || ' ' || COALESCE(btrim(ofm."CreatedTime"::text), '00:00:00'), 'YYYY-MM-DD HH24:MI:SS')
            ELSE to_timestamp(ofm."CreatedDate"::text || ' ' || COALESCE(btrim(ofm."CreatedTime"::text), '00:00:00'), 'MM/DD/YYYY HH24:MI:SS')
          END AS flow_ts_local,
          CASE
            WHEN ofm."ExpirationDate"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN ofm."ExpirationDate"::date
            WHEN ofm."ExpirationDate"::text ~ '^\\d{1,2}/\\d{1,2}/\\d{2,4}$' THEN to_date(ofm."ExpirationDate"::text, 'MM/DD/YYYY')
            ELSE NULL
          END AS exp_date
        FROM public."OptionsFlow_monthly" ofm
        WHERE ofm."Premium" IS NOT NULL
          AND btrim(ofm."Premium"::text) <> ''
          AND ofm."StockEtf" = 'STOCK'
          AND (
            CASE 
              WHEN ofm."CreatedDate"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' 
                THEN ofm."CreatedDate"::date
              WHEN ofm."CreatedDate"::text ~ '^\\d{1,2}/\\d{1,2}/\\d{2,4}$'
                THEN to_date(ofm."CreatedDate"::text, 'MM/DD/YYYY')
              ELSE NULL
            END
          ) = $1::date
      ),
      enriched AS (
        SELECT
          b.*,
          -- Calculate moneyness (ITM/OTM percentage)
          CASE
            WHEN b.strike_num IS NOT NULL AND b.spot_num IS NOT NULL AND b.spot_num > 0 THEN
              CASE
                WHEN b.cp_norm = 'CALL' THEN ((b.strike_num - b.spot_num) / b.spot_num) * 100
                WHEN b.cp_norm = 'PUT' THEN ((b.spot_num - b.strike_num) / b.spot_num) * 100
                ELSE NULL
              END
            ELSE NULL
          END AS mny_pct,
          -- Calculate DTE if not provided
          CASE
            WHEN b.dte_num IS NOT NULL THEN b.dte_num
            WHEN b.exp_date IS NOT NULL AND b.flow_ts_local IS NOT NULL THEN
              (b.exp_date::date - b.flow_ts_local::date)::integer
            ELSE NULL
          END AS dte_calc,
          -- DTE bucket classification
          CASE
            WHEN b.dte_num IS NOT NULL AND b.dte_num <= 3 THEN '0-3 DTE (urgent)'
            WHEN b.dte_num IS NOT NULL AND b.dte_num <= 7 THEN '4-7 DTE (short)'
            WHEN b.dte_num IS NOT NULL AND b.dte_num <= 30 THEN '8-30 DTE (medium)'
            WHEN b.dte_num IS NOT NULL THEN '30+ DTE (long)'
            WHEN b.exp_date IS NOT NULL AND b.flow_ts_local IS NOT NULL THEN
              CASE
                WHEN (b.exp_date::date - b.flow_ts_local::date)::integer <= 3 THEN '0-3 DTE (urgent)'
                WHEN (b.exp_date::date - b.flow_ts_local::date)::integer <= 7 THEN '4-7 DTE (short)'
                WHEN (b.exp_date::date - b.flow_ts_local::date)::integer <= 30 THEN '8-30 DTE (medium)'
                ELSE '30+ DTE (long)'
              END
            ELSE NULL
          END AS dte_bucket,
          -- Volume/OI ratio
          CASE
            WHEN b.oi_num > 0 THEN b.vol_num / NULLIF(b.oi_num, 0)
            ELSE NULL
          END AS vol_oi_ratio,
          -- ITM/OTM classification
          CASE
            WHEN b.strike_num IS NOT NULL AND b.spot_num IS NOT NULL THEN
              CASE
                WHEN b.cp_norm = 'CALL' AND b.strike_num <= b.spot_num THEN 'ITM'
                WHEN b.cp_norm = 'PUT' AND b.strike_num >= b.spot_num THEN 'ITM'
                ELSE 'OTM'
              END
            ELSE NULL
          END AS moneyness_class
        FROM base b
      ),
      flow AS (
        SELECT
          e.*,
          CASE
            WHEN (e.cp_norm='CALL' AND e.side_norm='BUY') OR (e.cp_norm='PUT' AND e.side_norm='SELL') THEN 'BULL'
            WHEN (e.cp_norm='PUT'  AND e.side_norm='BUY') OR (e.cp_norm='CALL' AND e.side_norm='SELL') THEN 'BEAR'
            ELSE NULL
          END AS direction,
          CASE
            WHEN EXTRACT(HOUR FROM e.flow_ts_local)::int BETWEEN 4 AND 9
                 AND NOT (EXTRACT(HOUR FROM e.flow_ts_local)::int = 9 AND EXTRACT(MINUTE FROM e.flow_ts_local)::int >= 30) THEN 'PRE'
            WHEN (EXTRACT(HOUR FROM e.flow_ts_local)::int = 9 AND EXTRACT(MINUTE FROM e.flow_ts_local)::int >= 30)
              OR (EXTRACT(HOUR FROM e.flow_ts_local)::int > 9 AND EXTRACT(HOUR FROM e.flow_ts_local)::int < 16) THEN 'RTH'
            WHEN EXTRACT(HOUR FROM e.flow_ts_local)::int BETWEEN 16 AND 20 THEN 'POST'
            ELSE 'OFF'
          END AS session_bucket
        FROM enriched e
      ),
      catalyst_check AS (
        SELECT
          f.*,
          CASE WHEN EXISTS (
            SELECT 1
            FROM public."AlertStream_monthly" a
            WHERE UPPER(TRIM(a."Ticker")) = f.symbol_norm
              OR UPPER(TRIM(a."ticker")) = f.symbol_norm
              OR UPPER(TRIM(a."Symbol")) = f.symbol_norm
              OR UPPER(TRIM(a."symbol")) = f.symbol_norm
            AND (
              CASE
                WHEN a."Date"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN a."Date"::date
                WHEN a."date"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN a."date"::date
                WHEN a."AlertDate"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN a."AlertDate"::date
                WHEN a."alert_date"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN a."alert_date"::date
                ELSE NULL
              END
            ) = $1::date
          ) THEN 1 ELSE 0 END AS catalyst_flag
        FROM flow f
      ),
      scored AS (
        SELECT
          c.*,
          (CASE
             WHEN c.premium_num >= 2000000 THEN 3.0
             WHEN c.premium_num >=  800000 THEN 2.0
             WHEN c.premium_num >=  200000 THEN 1.0
             ELSE 0.0
           END)
          + 1.2 * CASE WHEN COALESCE(c.vol_num,0) > COALESCE(c.oi_num,0) THEN 1 ELSE 0 END
          + 1.0 * CASE c.session_bucket WHEN 'RTH' THEN 1 WHEN 'POST' THEN 0.5 WHEN 'PRE' THEN 0.3 ELSE 0 END
          + 1.0 * c.catalyst_flag
          + 0.8 * CASE WHEN c.moneyness_class = 'OTM' THEN 1 ELSE 0 END
          AS rocket_score
        FROM catalyst_check c
        WHERE c.premium_num >= $2
          AND c.direction IS NOT NULL
          AND c.spot_num IS NOT NULL
      ),
      strike_clusters AS (
        SELECT
          symbol_norm,
          MAX(trades_at_strike) as max_trades_at_single_strike
        FROM (
          SELECT
            symbol_norm,
            strike_num,
            COUNT(*) as trades_at_strike
          FROM scored
          WHERE strike_num IS NOT NULL
          GROUP BY symbol_norm, strike_num
        ) sc
        GROUP BY symbol_norm
      ),
      dte_buckets AS (
        SELECT
          symbol_norm,
          dte_bucket,
          COUNT(*) as bucket_count,
          ROW_NUMBER() OVER (PARTITION BY symbol_norm ORDER BY COUNT(*) DESC) as rn
        FROM scored
        WHERE dte_bucket IS NOT NULL
        GROUP BY symbol_norm, dte_bucket
      ),
      symbol_agg AS (
        SELECT
          s.symbol_norm,
          COUNT(*) as total_trades,  -- Total option trades (individual flow records)
          COUNT(DISTINCT DATE_TRUNC('hour', s.flow_ts_local) + 
            (EXTRACT(MINUTE FROM s.flow_ts_local)::int / 15) * INTERVAL '15 minutes') as signal_periods,  -- Distinct 15-min periods
          SUM(s.premium_num) as total_premium,
          MAX(s.rocket_score) as max_rocket_score,
          MIN(s.rocket_score) as min_rocket_score,
          AVG(s.rocket_score) as avg_rocket_score,
          MAX(s.spot_num) as entry_price,
          STRING_AGG(DISTINCT s.direction, ', ') as directions,
          STRING_AGG(DISTINCT s.session_bucket, ', ') as sessions,
          -- BULL signals aggregation
          COUNT(*) FILTER (WHERE s.direction = 'BULL') as bull_count,
          COALESCE(SUM(s.premium_num) FILTER (WHERE s.direction = 'BULL'), 0) as bull_premium,
          COALESCE(MIN(s.premium_num) FILTER (WHERE s.direction = 'BULL'), 0) as bull_min_premium,
          COALESCE(MAX(s.premium_num) FILTER (WHERE s.direction = 'BULL'), 0) as bull_max_premium,
          -- BEAR signals aggregation
          COUNT(*) FILTER (WHERE s.direction = 'BEAR') as bear_count,
          COALESCE(SUM(s.premium_num) FILTER (WHERE s.direction = 'BEAR'), 0) as bear_premium,
          COALESCE(MIN(s.premium_num) FILTER (WHERE s.direction = 'BEAR'), 0) as bear_min_premium,
          COALESCE(MAX(s.premium_num) FILTER (WHERE s.direction = 'BEAR'), 0) as bear_max_premium,
          -- Predictive factors aggregation
          -- Moneyness (ITM vs OTM)
          COUNT(*) FILTER (WHERE s.moneyness_class = 'ITM') as itm_count,
          COUNT(*) FILTER (WHERE s.moneyness_class = 'OTM') as otm_count,
          AVG(s.mny_pct) FILTER (WHERE s.mny_pct IS NOT NULL) as avg_moneyness_pct,
          -- DTE analysis
          AVG(s.dte_calc) FILTER (WHERE s.dte_calc IS NOT NULL) as avg_dte,
          db.dte_bucket as most_common_dte_bucket,
          -- Session analysis
          COUNT(*) FILTER (WHERE s.session_bucket = 'RTH') as rth_count,
          COUNT(*) FILTER (WHERE s.session_bucket = 'PRE') as pre_count,
          COUNT(*) FILTER (WHERE s.session_bucket = 'POST') as post_count,
          -- Catalyst detection
          COUNT(*) FILTER (WHERE s.catalyst_flag = 1) as catalyst_count,
          CASE WHEN COUNT(*) > 0 THEN 
            ROUND(100.0 * COUNT(*) FILTER (WHERE s.catalyst_flag = 1) / COUNT(*), 1)
          ELSE 0 END as pct_with_catalyst,
          -- Volume/OI ratio
          AVG(s.vol_oi_ratio) FILTER (WHERE s.vol_oi_ratio IS NOT NULL) as avg_vol_oi_ratio,
          COUNT(*) FILTER (WHERE s.vol_num > s.oi_num) as vol_exceeds_oi_count,
          -- Strike clustering (institutional layering)
          COUNT(DISTINCT s.strike_num) as unique_strikes,
          CASE WHEN COUNT(DISTINCT s.strike_num) > 0 THEN
            ROUND(COUNT(*)::numeric / COUNT(DISTINCT s.strike_num), 1)
          ELSE 0 END as avg_trades_per_strike,
          COALESCE(sc.max_trades_at_single_strike, 0) as max_trades_at_single_strike,
          -- IV analysis
          AVG(s.iv_num) FILTER (WHERE s.iv_num IS NOT NULL) as avg_iv
        FROM scored s
        LEFT JOIN strike_clusters sc ON sc.symbol_norm = s.symbol_norm
        LEFT JOIN dte_buckets db ON db.symbol_norm = s.symbol_norm AND db.rn = 1
        WHERE s.rocket_score >= $3 AND s.rocket_score <= $4
        GROUP BY s.symbol_norm, sc.max_trades_at_single_strike, db.dte_bucket
        HAVING COUNT(*) >= 1
      )
      SELECT * FROM symbol_agg
      ORDER BY total_trades DESC, total_premium DESC
      LIMIT 100
    `;
    
    const results = await rawQuery(query, [date, minPrem, minRocket, maxRocket]);
    
    res.json({
      success: true,
      date,
      symbols: results,
      count: results.length
    });
  } catch (error) {
    console.error('Date analysis error:', error);
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * GET /api/backtest/symbol-performance
 * Get price movement and profitability for a symbol after a specific date
 */
router.get('/symbol-performance', async (req, res) => {
  const { symbol, entryDate, daysForward = 7 } = req.query;
  
  try {
    if (!symbol || !entryDate) {
      return res.status(400).json({
        success: false,
        error: 'Symbol and entryDate parameters are required'
      });
    }
    
    // Get entry price
    const entryPriceQuery = `
      SELECT close, open, high, low, volume
      FROM public.prices_daily
      WHERE UPPER(symbol) = UPPER($1)
        AND "Date" = $2::date
      LIMIT 1
    `;
    
    const entryPriceData = await rawQuery(entryPriceQuery, [symbol, entryDate]);
    
    if (!entryPriceData || entryPriceData.length === 0) {
      return res.status(404).json({
        success: false,
        error: `No price data found for ${symbol} on ${entryDate}`
      });
    }
    
    const entryPrice = parseFloat(entryPriceData[0].close);
    
    if (isNaN(entryPrice) || !entryPrice) {
      return res.status(404).json({
        success: false,
        error: `Invalid entry price for ${symbol} on ${entryDate}`
      });
    }
    
    // Get price history for the next N days
    const endDate = new Date(entryDate);
    endDate.setDate(endDate.getDate() + parseInt(daysForward));
    const endDateStr = endDate.toISOString().split('T')[0];
    
    const priceHistoryQuery = `
      SELECT "Date", close, open, high, low, volume
      FROM public.prices_daily
      WHERE UPPER(symbol) = UPPER($1)
        AND "Date" > $2::date
        AND "Date" <= $3::date
      ORDER BY "Date" ASC
    `;
    
    const priceHistory = await rawQuery(priceHistoryQuery, [symbol, entryDate, endDateStr]);
    
    // Get signals for this symbol on entry date with all predictive factors
    const signalsQuery = `
      WITH base AS (
        SELECT
          ofm.ctid AS rid,
          UPPER(TRIM(ofm."Symbol")) AS symbol_norm,
          NULLIF(REGEXP_REPLACE(ofm."Spot"::text, '[\\s$,]', '', 'g'),'')::numeric AS spot_num,
          NULLIF(REGEXP_REPLACE(ofm."Premium"::text, '[\\s$,]', '', 'g'),'')::numeric AS premium_num,
          NULLIF(REGEXP_REPLACE(ofm."Volume"::text, '[\\s$,]', '', 'g'),'')::numeric AS vol_num,
          NULLIF(REGEXP_REPLACE(ofm."OI"::text, '[\\s$,]', '', 'g'),'')::numeric AS oi_num,
          NULLIF(REGEXP_REPLACE(ofm."Strike"::text, '[\\s$,]', '', 'g'),'')::numeric AS strike_num,
          NULLIF(REGEXP_REPLACE(ofm."Dte"::text, '[\\s$,]', '', 'g'),'')::numeric AS dte_num,
          CASE WHEN UPPER(TRIM(ofm."CallPut")) IN ('C','CALL','CALLS','CE') THEN 'CALL'
               WHEN UPPER(TRIM(ofm."CallPut")) IN ('P','PUT','PUTS','PE')   THEN 'PUT'
          END AS cp_norm,
          CASE
            WHEN UPPER(TRIM(ofm."Side")) IN ('A','AA','ASK','BUY','BOT','BTO','AT_ASK')  THEN 'BUY'
            WHEN UPPER(TRIM(ofm."Side")) IN ('B','BB','BID','SELL','SLD','STO','AT_BID') THEN 'SELL'
            ELSE NULL
          END AS side_norm,
          CASE
            WHEN ofm."CreatedDate"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN 
              to_timestamp(ofm."CreatedDate"::text || ' ' || COALESCE(btrim(ofm."CreatedTime"::text), '00:00:00'), 'YYYY-MM-DD HH24:MI:SS')
            ELSE to_timestamp(ofm."CreatedDate"::text || ' ' || COALESCE(btrim(ofm."CreatedTime"::text), '00:00:00'), 'MM/DD/YYYY HH24:MI:SS')
          END AS flow_ts_local
        FROM public."OptionsFlow_monthly" ofm
        WHERE UPPER(TRIM(ofm."Symbol")) = UPPER($1)
          AND (
            CASE 
              WHEN ofm."CreatedDate"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' 
                THEN ofm."CreatedDate"::date
              WHEN ofm."CreatedDate"::text ~ '^\\d{1,2}/\\d{1,2}/\\d{2,4}$'
                THEN to_date(ofm."CreatedDate"::text, 'MM/DD/YYYY')
              ELSE NULL
            END
          ) = $2::date
          AND ofm."Premium" IS NOT NULL
          AND btrim(ofm."Premium"::text) <> ''
      ),
      enriched AS (
        SELECT
          b.*,
          -- Calculate moneyness
          CASE
            WHEN b.strike_num IS NOT NULL AND b.spot_num IS NOT NULL AND b.spot_num > 0 THEN
              CASE
                WHEN b.cp_norm = 'CALL' THEN ((b.strike_num - b.spot_num) / b.spot_num) * 100
                WHEN b.cp_norm = 'PUT' THEN ((b.spot_num - b.strike_num) / b.spot_num) * 100
                ELSE NULL
              END
            ELSE NULL
          END AS mny_pct,
          -- ITM/OTM classification
          CASE
            WHEN b.strike_num IS NOT NULL AND b.spot_num IS NOT NULL THEN
              CASE
                WHEN b.cp_norm = 'CALL' AND b.strike_num <= b.spot_num THEN 'ITM'
                WHEN b.cp_norm = 'PUT' AND b.strike_num >= b.spot_num THEN 'ITM'
                ELSE 'OTM'
              END
            ELSE NULL
          END AS moneyness_class,
          -- DTE bucket
          CASE
            WHEN b.dte_num IS NOT NULL AND b.dte_num <= 3 THEN '0-3 DTE'
            WHEN b.dte_num IS NOT NULL AND b.dte_num <= 7 THEN '4-7 DTE'
            WHEN b.dte_num IS NOT NULL AND b.dte_num <= 30 THEN '8-30 DTE'
            WHEN b.dte_num IS NOT NULL THEN '30+ DTE'
            ELSE NULL
          END AS dte_bucket,
          -- Volume/OI ratio
          CASE
            WHEN b.oi_num > 0 THEN b.vol_num / NULLIF(b.oi_num, 0)
            ELSE NULL
          END AS vol_oi_ratio,
          -- Session bucket
          CASE
            WHEN EXTRACT(HOUR FROM b.flow_ts_local)::int BETWEEN 4 AND 9
                 AND NOT (EXTRACT(HOUR FROM b.flow_ts_local)::int = 9 AND EXTRACT(MINUTE FROM b.flow_ts_local)::int >= 30) THEN 'PRE'
            WHEN (EXTRACT(HOUR FROM b.flow_ts_local)::int = 9 AND EXTRACT(MINUTE FROM b.flow_ts_local)::int >= 30)
              OR (EXTRACT(HOUR FROM b.flow_ts_local)::int > 9 AND EXTRACT(HOUR FROM b.flow_ts_local)::int < 16) THEN 'RTH'
            WHEN EXTRACT(HOUR FROM b.flow_ts_local)::int BETWEEN 16 AND 20 THEN 'POST'
            ELSE 'OFF'
          END AS session_bucket
        FROM base b
      ),
      flow AS (
        SELECT
          e.*,
          CASE
            WHEN (e.cp_norm='CALL' AND e.side_norm='BUY') OR (e.cp_norm='PUT' AND e.side_norm='SELL') THEN 'BULL'
            WHEN (e.cp_norm='PUT'  AND e.side_norm='BUY') OR (e.cp_norm='CALL' AND e.side_norm='SELL') THEN 'BEAR'
            ELSE NULL
          END AS direction
        FROM enriched e
      ),
      catalyst_check AS (
        SELECT
          f.*,
          CASE WHEN EXISTS (
            SELECT 1
            FROM public."AlertStream_monthly" a
            WHERE (UPPER(TRIM(a."Ticker")) = f.symbol_norm
              OR UPPER(TRIM(a."ticker")) = f.symbol_norm
              OR UPPER(TRIM(a."Symbol")) = f.symbol_norm
              OR UPPER(TRIM(a."symbol")) = f.symbol_norm)
            AND (
              CASE
                WHEN a."Date"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN a."Date"::date
                WHEN a."date"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN a."date"::date
                WHEN a."AlertDate"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN a."AlertDate"::date
                WHEN a."alert_date"::text ~ '^\\d{4}-\\d{2}-\\d{2}$' THEN a."alert_date"::date
                ELSE NULL
              END
            ) = $2::date
          ) THEN 1 ELSE 0 END AS catalyst_flag
        FROM flow f
      ),
      scored AS (
        SELECT
          c.*,
          (CASE
             WHEN c.premium_num >= 2000000 THEN 3.0
             WHEN c.premium_num >=  800000 THEN 2.0
             WHEN c.premium_num >=  200000 THEN 1.0
             ELSE 0.0
           END) AS rocket_score
        FROM catalyst_check c
        WHERE c.premium_num >= 80000
      )
      SELECT 
        symbol_norm,
        COUNT(*) as signal_count,
        SUM(premium_num) as total_premium,
        MAX(rocket_score) as max_rocket_score,
        AVG(rocket_score) as avg_rocket_score,
        STRING_AGG(DISTINCT direction, ', ') as directions,
        STRING_AGG(DISTINCT cp_norm, ', ') as option_types,
        -- Predictive factors
        COUNT(*) FILTER (WHERE moneyness_class = 'ITM') as itm_count,
        COUNT(*) FILTER (WHERE moneyness_class = 'OTM') as otm_count,
        AVG(mny_pct) FILTER (WHERE mny_pct IS NOT NULL) as avg_moneyness_pct,
        AVG(dte_num) FILTER (WHERE dte_num IS NOT NULL) as avg_dte,
        MODE() WITHIN GROUP (ORDER BY dte_bucket) as most_common_dte_bucket,
        COUNT(*) FILTER (WHERE session_bucket = 'RTH') as rth_count,
        COUNT(*) FILTER (WHERE session_bucket = 'PRE') as pre_count,
        COUNT(*) FILTER (WHERE session_bucket = 'POST') as post_count,
        COUNT(*) FILTER (WHERE catalyst_flag = 1) as catalyst_count,
        CASE WHEN COUNT(*) > 0 THEN 
          ROUND(100.0 * COUNT(*) FILTER (WHERE catalyst_flag = 1) / COUNT(*), 1)
        ELSE 0 END as pct_with_catalyst,
        AVG(vol_oi_ratio) FILTER (WHERE vol_oi_ratio IS NOT NULL) as avg_vol_oi_ratio,
        COUNT(*) FILTER (WHERE vol_num > oi_num) as vol_exceeds_oi_count,
        COUNT(DISTINCT strike_num) as unique_strikes
      FROM scored
      GROUP BY symbol_norm
    `;
    
    let signals = [];
    try {
      signals = await rawQuery(signalsQuery, [symbol, entryDate]);
    } catch (error) {
      console.error('Error fetching signals:', error);
      // Continue with empty signals array if query fails
      signals = [];
    }
    
    // Calculate performance metrics
    const priceData = priceHistory.map(p => {
      const close = parseFloat(p.close) || 0;
      const open = parseFloat(p.open) || 0;
      const high = parseFloat(p.high) || 0;
      const low = parseFloat(p.low) || 0;
      const volume = parseFloat(p.volume || 0);
      
      return {
        date: p.Date,
        close,
        open,
        high,
        low,
        volume,
        change: close - entryPrice,
        changePct: entryPrice > 0 ? ((close - entryPrice) / entryPrice) * 100 : 0
      };
    });
    
    // Calculate profitability insights
    const maxGain = priceData.length > 0 
      ? Math.max(...priceData.map(p => p.changePct || 0))
      : 0;
    const maxLoss = priceData.length > 0
      ? Math.min(...priceData.map(p => p.changePct || 0))
      : 0;
    const finalGain = priceData.length > 0
      ? (priceData[priceData.length - 1].changePct || 0)
      : 0;
    
    // Determine if signals were profitable
    const signalData = (signals && signals.length > 0) ? signals[0] : {};
    const directions = signalData.directions || '';
    const isBullish = directions.includes('BULL');
    const isBearish = directions.includes('BEAR');
    
    // Calculate separate profitability for each direction
    const bullProfitable = finalGain > 0;
    const bearProfitable = finalGain < 0;
    
    let wouldBeProfitable = false;
    let profitReason = '';
    let detailedAnalysis = null;
    
    if (isBullish && !isBearish) {
      // Pure bullish signals
      wouldBeProfitable = bullProfitable;
      profitReason = wouldBeProfitable 
        ? `✅ Price increased ${finalGain.toFixed(2)}% - Bullish signals would have been profitable`
        : `❌ Price decreased ${Math.abs(finalGain).toFixed(2)}% - Bullish signals would have lost money`;
    } else if (isBearish && !isBullish) {
      // Pure bearish signals
      wouldBeProfitable = bearProfitable;
      profitReason = wouldBeProfitable
        ? `✅ Price decreased ${Math.abs(finalGain).toFixed(2)}% - Bearish signals would have been profitable`
        : `❌ Price increased ${finalGain.toFixed(2)}% - Bearish signals would have lost money`;
    } else {
      // Mixed signals - analyze both directions
      const priceMove = Math.abs(finalGain);
      const significantMove = priceMove > 1;
      
      // Determine overall profitability based on which direction had more premium
      // If we can't determine, consider it mixed/unclear
      wouldBeProfitable = significantMove && (bullProfitable || bearProfitable);
      
      // Build detailed analysis for mixed signals
      const bullResult = bullProfitable ? '✅ Profitable' : '❌ Loss';
      const bearResult = bearProfitable ? '✅ Profitable' : '❌ Loss';
      
      profitReason = `Mixed signals detected - Price moved ${finalGain >= 0 ? '+' : ''}${finalGain.toFixed(2)}%`;
      
      detailedAnalysis = {
        bullSignals: {
          profitable: bullProfitable,
          result: bullResult,
          explanation: finalGain > 0 
            ? `Price went UP ${finalGain.toFixed(2)}% - BULL signals would have been profitable`
            : `Price went DOWN ${Math.abs(finalGain).toFixed(2)}% - BULL signals would have lost money`
        },
        bearSignals: {
          profitable: bearProfitable,
          result: bearResult,
          explanation: finalGain < 0
            ? `Price went DOWN ${Math.abs(finalGain).toFixed(2)}% - BEAR signals would have been profitable`
            : `Price went UP ${Math.abs(finalGain).toFixed(2)}% - BEAR signals would have lost money`
        },
        recommendation: finalGain < 0
          ? 'BEAR signals would have been profitable (price declined)'
          : finalGain > 0
          ? 'BULL signals would have been profitable (price rose)'
          : 'Price was flat - neither direction would have been profitable'
      };
    }
    
    res.json({
      success: true,
      symbol: symbol.toUpperCase(),
      entryDate,
      daysForward: parseInt(daysForward),
      entryPrice,
      signals: signalData,
      priceHistory: priceData,
      performance: {
        maxGain,
        maxLoss,
        finalGain,
        wouldBeProfitable,
        profitReason,
        detailedAnalysis,
        bestDay: priceData.length > 0 
          ? priceData.reduce((best, current) => {
              const bestPct = Math.abs(best.changePct || 0);
              const currentPct = Math.abs(current.changePct || 0);
              return currentPct > bestPct ? current : best;
            }, priceData[0])
          : null
      }
    });
  } catch (error) {
    console.error('Symbol performance error:', error);
    console.error('Error details:', {
      symbol,
      entryDate,
      daysForward,
      stack: error.stack
    });
    res.status(500).json({
      success: false,
      error: error.message,
      details: process.env.NODE_ENV === 'development' ? error.stack : undefined
    });
  }
});

export default router;

