import express from 'express';
import { rawQuery } from '../db.js';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { enhanceScannerResults } from '../services/scannerEnhancer.js';

const router = express.Router();

// Get the directory of the current file
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Read the SQL query file
const sqlFilePath = join(__dirname, '../../database/intradaysignalscorer.sql');
let multiSignalQuery = '';

try {
  multiSignalQuery = readFileSync(sqlFilePath, 'utf-8');
} catch (error) {
  console.error('Error reading SQL file:', error);
  // Fallback to inline query if file read fails
  multiSignalQuery = `
WITH
params AS (
  SELECT
    (now() AT TIME ZONE 'America/Chicago') AS now_local,
    INTERVAL '20 minutes' AS win_darkblock,
    INTERVAL '90 minutes' AS win_highs,
    INTERVAL '45 minutes' AS win_lows,
    INTERVAL '30 minutes' AS win_stealth,
    INTERVAL '20 minutes' AS win_shock,
    INTERVAL '10 minutes' AS win_options,
    INTERVAL '20 minutes' AS win_recent,
    TIME '08:30:00' AS orb_start,
    TIME '09:30:00' AS orb_end,
    1000000::numeric AS per_print_min_notional,
    5000000::numeric AS cluster_min_notional,
    150::numeric AS orb_min_rvol,
    2000000::numeric AS orb_min_notional,
    120::numeric AS highs_min_rvol,
    3000000::numeric AS stealth_min_notional,
    100::numeric AS stealth_max_rvol,
    2::int AS shock_min_events,
    2::int AS opt_pair_min,
    2000000::numeric AS opt_min_notional,
    3::int AS min_score
),
base AS (
  SELECT
    symbol::text AS ticker,
    alert_type::text AS evt_type,
    message::text AS message,
    notional::numeric AS notional,
    pct_of_avg30::numeric AS pct_of_avg30,
    CASE
      WHEN alert_time_txt ~* '(AM|PM)\\s*$'
        THEN to_timestamp(to_char(alert_date,'YYYY-MM-DD') || ' ' || alert_time_txt,
                          'YYYY-MM-DD HH12:MI:SS AM')::timestamp
      ELSE to_timestamp(to_char(alert_date,'YYYY-MM-DD') || ' ' || alert_time_txt,
                        'YYYY-MM-DD HH24:MI:SS')::timestamp
    END AS event_ts_local
  FROM public.alertstream_monthly
  WHERE symbol IS NOT NULL
),
s1_agg AS (
  SELECT b.ticker,
         COUNT(*) AS prints,
         SUM(b.notional) AS notional_usd,
         ROUND(AVG(b.pct_of_avg30),1) AS avg_rvol
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_darkblock
    AND b.evt_type IN ('DarkPool','Block')
    AND b.notional >= p.per_print_min_notional
  GROUP BY b.ticker
),
s1 AS (
  SELECT s.*
  FROM s1_agg s CROSS JOIN params p
  WHERE s.notional_usd >= p.cluster_min_notional
),
s2_agg AS (
  SELECT b.ticker,
         COUNT(*) AS orb_triggers,
         SUM(b.notional) AS notional_usd,
         MAX(b.pct_of_avg30) AS rvol_peak
  FROM base b
  CROSS JOIN params p
  WHERE DATE(b.event_ts_local) = DATE(p.now_local)
    AND b.event_ts_local::time BETWEEN p.orb_start AND p.orb_end
    AND b.evt_type = 'Orb'
  GROUP BY b.ticker
),
s2 AS (
  SELECT s.*
  FROM s2_agg s CROSS JOIN params p
  WHERE s.rvol_peak >= p.orb_min_rvol
    AND s.notional_usd >= p.orb_min_notional
),
s3_agg AS (
  SELECT b.ticker,
         COUNT(*) AS hits,
         MAX(b.pct_of_avg30) AS rvol_peak,
         SUM(CASE WHEN b.message ILIKE 'Required Dollar Volume Met%' THEN 1 ELSE 0 END) AS dollar_vol_flags
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_highs
    AND b.evt_type = 'AnnualHigh'
  GROUP BY b.ticker
),
s3 AS (
  SELECT s.*
  FROM s3_agg s CROSS JOIN params p
  WHERE s.rvol_peak >= p.highs_min_rvol
    AND s.dollar_vol_flags >= 1
),
s4_lows AS (
  SELECT b.ticker, COUNT(*) AS low_hits, MAX(b.pct_of_avg30) AS rvol
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_lows
    AND b.evt_type = 'AnnualLow'
  GROUP BY b.ticker
),
s4_volpop AS (
  SELECT DISTINCT b.ticker
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_lows
    AND b.evt_type IN ('VolumeActive','PriceSpike')
),
s4 AS (
  SELECT l.ticker, l.low_hits, l.rvol
  FROM s4_lows l
  JOIN s4_volpop v ON v.ticker = l.ticker
),
s5_agg AS (
  SELECT b.ticker,
         COUNT(*) AS prints,
         SUM(b.notional) AS notional_usd,
         ROUND(AVG(b.pct_of_avg30),1) AS avg_rvol
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_stealth
    AND b.evt_type = 'DarkPool'
  GROUP BY b.ticker
),
s5 AS (
  SELECT s.*
  FROM s5_agg s CROSS JOIN params p
  WHERE s.notional_usd >= p.stealth_min_notional
    AND s.avg_rvol < p.stealth_max_rvol
),
s6_agg AS (
  SELECT b.ticker,
         SUM(CASE WHEN b.evt_type='PriceSpike' THEN 1 ELSE 0 END)::int AS spike_cnt,
         SUM(CASE WHEN b.evt_type='RapidDecline' THEN 1 ELSE 0 END)::int AS flush_cnt,
         SUM(b.notional) AS notional_usd
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_shock
    AND b.evt_type IN ('PriceSpike','RapidDecline')
  GROUP BY b.ticker
),
s6 AS (
  SELECT s.*
  FROM s6_agg s CROSS JOIN params p
  WHERE (s.spike_cnt + s.flush_cnt) >= p.shock_min_events
),
s7_agg AS (
  SELECT b.ticker,
         SUM(CASE WHEN b.evt_type='Options' THEN 1 ELSE 0 END)::int AS opt_hits,
         SUM(CASE WHEN b.evt_type='Block' THEN 1 ELSE 0 END)::int AS block_hits,
         SUM(CASE WHEN b.evt_type='DarkPool' THEN 1 ELSE 0 END)::int AS dark_hits,
         SUM(b.notional) AS notional_usd
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_options
  GROUP BY b.ticker
),
s7 AS (
  SELECT s.*
  FROM s7_agg s CROSS JOIN params p
  WHERE s.opt_hits >= 1
    AND (s.block_hits + s.dark_hits) >= p.opt_pair_min
    AND s.notional_usd >= p.opt_min_notional
),
rr AS (
  SELECT b.ticker,
         SUM(b.notional) AS recent_notional,
         MAX(b.pct_of_avg30) AS recent_rvol_peak
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_recent
  GROUP BY b.ticker
),
last_seen AS (
  SELECT DISTINCT ON (b.ticker)
         b.ticker,
         b.event_ts_local AS last_seen_ts
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_recent
  ORDER BY b.ticker, b.event_ts_local DESC
),
universe AS (
  SELECT ticker FROM s1
  UNION SELECT ticker FROM s2
  UNION SELECT ticker FROM s3
  UNION SELECT ticker FROM s4
  UNION SELECT ticker FROM s5
  UNION SELECT ticker FROM s6
  UNION SELECT ticker FROM s7
),
signals AS (
  SELECT ticker, 'Dark/Block cluster' AS sig, 3 AS w FROM s1
  UNION ALL SELECT ticker, 'ORB momentum', 3 FROM s2
  UNION ALL SELECT ticker, '52W High + $Vol', 2 FROM s3
  UNION ALL SELECT ticker, 'Low + VolPop', 2 FROM s4
  UNION ALL SELECT ticker, 'Stealth Dark', 2 FROM s5
  UNION ALL SELECT ticker, 'Shock Tape', 1 FROM s6
  UNION ALL SELECT ticker, 'Options + Prints', 3 FROM s7
),
scored AS (
  SELECT
    u.ticker,
    COALESCE(rr.recent_notional,0)::numeric AS recent_notional,
    COALESCE(rr.recent_rvol_peak,0)::numeric AS rvol_peak,
    ls.last_seen_ts AS last_seen_ts,
    SUM(s.w)::int AS score,
    string_agg(s.sig, ' | ' ORDER BY s.w DESC, s.sig) AS reasons
  FROM universe u
  LEFT JOIN signals s ON s.ticker = u.ticker
  LEFT JOIN rr ON rr.ticker = u.ticker
  LEFT JOIN last_seen ls ON ls.ticker = u.ticker
  GROUP BY u.ticker, rr.recent_notional, rr.recent_rvol_peak, ls.last_seen_ts
)
SELECT
  ticker,
  score,
  CASE
    WHEN recent_notional >= 1e9 THEN '$' || to_char(ROUND(recent_notional/1e9,1), 'FM999999990.0') || 'B'
    WHEN recent_notional >= 1e6 THEN '$' || to_char(ROUND(recent_notional/1e6,1), 'FM999999990.0') || 'M'
    WHEN recent_notional >= 1e3 THEN '$' || to_char(ROUND(recent_notional/1e3,0), 'FM999999990') || 'K'
    ELSE '$' || to_char(ROUND(recent_notional,0), 'FM999999990')
  END AS notional_20m,
  ROUND(rvol_peak,1) AS rvol_peak,
  to_char(last_seen_ts, 'HH24:MI:SS') AS last_seen_ct,
  reasons
FROM scored, params p
WHERE score >= p.min_score
ORDER BY score DESC, recent_notional DESC, rvol_peak DESC
LIMIT 50;
  `;
}

/**
 * Helper function to replace now() with a specific datetime in SQL queries
 * @param {string} query - SQL query string
 * @param {string} dateTime - Date/time string in format YYYY-MM-DD or YYYY-MM-DD HH:MM:SS
 * @returns {string} Modified query with now() replaced
 */
function replaceNowWithDateTime(query, dateTime) {
  if (!dateTime) {
    return query; // Return original query if no datetime provided
  }
  
  try {
    // Parse the datetime string - treat as naive timestamp (no timezone)
    let dateStr, timeStr;
    
    if (dateTime.includes('T')) {
      // ISO format: 2024-01-15T14:30:00
      [dateStr, timeStr] = dateTime.split('T');
    } else if (dateTime.includes(' ')) {
      // Space format: 2024-01-15 14:30:00
      [dateStr, timeStr] = dateTime.split(' ');
    } else {
      // Date only: 2024-01-15 (default to 16:00:00 CT = end of trading day)
      dateStr = dateTime;
      timeStr = '16:00:00';
    }
    
    // Validate date format (YYYY-MM-DD)
    if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
      throw new Error(`Invalid date format: ${dateStr}. Expected YYYY-MM-DD`);
    }
    
    // Validate time format (HH:MM:SS or HH:MM)
    if (timeStr && !/^\d{2}:\d{2}(:\d{2})?$/.test(timeStr)) {
      throw new Error(`Invalid time format: ${timeStr}. Expected HH:MM:SS or HH:MM`);
    }
    
    // Ensure time has seconds
    if (timeStr && !timeStr.includes(':', 2)) {
      timeStr = timeStr + ':00';
    }
    
    // Format for PostgreSQL: '2024-01-15 14:30:00'::timestamp AT TIME ZONE 'America/Chicago'
    const ctDateTime = `'${dateStr} ${timeStr || '00:00:00'}'::timestamp AT TIME ZONE 'America/Chicago'`;
    
    // Replace (now() AT TIME ZONE 'America/Chicago') with the target datetime
    return query.replace(
      /\(now\(\)\s+AT\s+TIME\s+ZONE\s+'America\/Chicago'\)/gi,
      ctDateTime
    );
  } catch (error) {
    console.error('Error parsing datetime:', error);
    return query; // Return original query on error
  }
}

/**
 * GET /api/scanner/multi-signal
 * Multi-signal institutional scanner
 * Query params: date (YYYY-MM-DD), time (HH:MM:SS) - optional, defaults to now()
 */
router.get('/multi-signal', async (req, res) => {
  try {
    const { date, time } = req.query;
    
    // Build datetime string if provided
    let dateTime = null;
    if (date) {
      dateTime = time ? `${date} ${time}` : date;
    }
    
    // Replace now() with target datetime if provided
    const query = dateTime ? replaceNowWithDateTime(multiSignalQuery, dateTime) : multiSignalQuery;
    
    const data = await rawQuery(query);
    const rawResults = Array.isArray(data) ? data : [];
    
    // Enhance results with convergence, horizon, and false signal filtering
    const enhancedResults = enhanceScannerResults(rawResults);
    
    res.json({
      success: true,
      data: enhancedResults,
      count: enhancedResults.length,
      timestamp: new Date().toISOString(),
      queryDateTime: dateTime || 'current'
    });
  } catch (error) {
    console.error('Scanner error:', error);
    res.status(500).json({ 
      success: false, 
      error: error.message,
      timestamp: new Date().toISOString()
    });
  }
});

/**
 * GET /api/scanner/dark-block-cluster
 * Dark pool / block clusters only
 * Query params: date (YYYY-MM-DD), time (HH:MM:SS) - optional, defaults to now()
 */
router.get('/dark-block-cluster', async (req, res) => {
  try {
    const { date, time } = req.query;
    let dateTime = null;
    if (date) {
      dateTime = time ? `${date} ${time}` : date;
    }
    
    // Query for dark pool / block clusters only (S1 signal)
    let query = `
WITH
params AS (
  SELECT
    (now() AT TIME ZONE 'America/Chicago') AS now_local,
    INTERVAL '20 minutes' AS win_darkblock,
    1000000::numeric AS per_print_min_notional,
    5000000::numeric AS cluster_min_notional
),
base AS (
  SELECT
    symbol::text AS ticker,
    alert_type::text AS evt_type,
    notional::numeric AS notional,
    pct_of_avg30::numeric AS pct_of_avg30,
    CASE
      WHEN alert_time_txt ~* '(AM|PM)\\s*$'
        THEN to_timestamp(to_char(alert_date,'YYYY-MM-DD') || ' ' || alert_time_txt,
                          'YYYY-MM-DD HH12:MI:SS AM')::timestamp
      ELSE to_timestamp(to_char(alert_date,'YYYY-MM-DD') || ' ' || alert_time_txt,
                        'YYYY-MM-DD HH24:MI:SS')::timestamp
    END AS event_ts_local
  FROM public.alertstream_monthly
  WHERE symbol IS NOT NULL
),
s1_agg AS (
  SELECT b.ticker,
         COUNT(*) AS prints,
         SUM(b.notional) AS notional_usd,
         ROUND(AVG(b.pct_of_avg30),1) AS avg_rvol
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_darkblock
    AND b.evt_type IN ('DarkPool','Block')
    AND b.notional >= p.per_print_min_notional
  GROUP BY b.ticker
)
SELECT s.*
FROM s1_agg s CROSS JOIN params p
WHERE s.notional_usd >= p.cluster_min_notional
ORDER BY s.notional_usd DESC
LIMIT 50;
    `;
    
    // Replace now() with target datetime if provided
    if (dateTime) {
      query = replaceNowWithDateTime(query, dateTime);
    }
    
    const data = await rawQuery(query);
    res.json({ 
      success: true, 
      data: Array.isArray(data) ? data : [],
      queryDateTime: dateTime || 'current'
    });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * GET /api/scanner/orb-momentum
 * ORB momentum signals only
 * Query params: date (YYYY-MM-DD), time (HH:MM:SS) - optional, defaults to now()
 */
router.get('/orb-momentum', async (req, res) => {
  try {
    const { date, time } = req.query;
    let dateTime = null;
    if (date) {
      dateTime = time ? `${date} ${time}` : date;
    }
    
    let query = `
WITH
params AS (
  SELECT
    (now() AT TIME ZONE 'America/Chicago') AS now_local,
    TIME '08:30:00' AS orb_start,
    TIME '09:30:00' AS orb_end,
    150::numeric AS orb_min_rvol,
    2000000::numeric AS orb_min_notional
),
base AS (
  SELECT
    symbol::text AS ticker,
    alert_type::text AS evt_type,
    notional::numeric AS notional,
    pct_of_avg30::numeric AS pct_of_avg30,
    CASE
      WHEN alert_time_txt ~* '(AM|PM)\\s*$'
        THEN to_timestamp(to_char(alert_date,'YYYY-MM-DD') || ' ' || alert_time_txt,
                          'YYYY-MM-DD HH12:MI:SS AM')::timestamp
      ELSE to_timestamp(to_char(alert_date,'YYYY-MM-DD') || ' ' || alert_time_txt,
                        'YYYY-MM-DD HH24:MI:SS')::timestamp
    END AS event_ts_local
  FROM public.alertstream_monthly
  WHERE symbol IS NOT NULL
),
s2_agg AS (
  SELECT b.ticker,
         COUNT(*) AS orb_triggers,
         SUM(b.notional) AS notional_usd,
         MAX(b.pct_of_avg30) AS rvol_peak
  FROM base b
  CROSS JOIN params p
  WHERE DATE(b.event_ts_local) = DATE(p.now_local)
    AND b.event_ts_local::time BETWEEN p.orb_start AND p.orb_end
    AND b.evt_type = 'Orb'
  GROUP BY b.ticker
)
SELECT s.*
FROM s2_agg s CROSS JOIN params p
WHERE s.rvol_peak >= p.orb_min_rvol
  AND s.notional_usd >= p.orb_min_notional
ORDER BY s.rvol_peak DESC, s.notional_usd DESC
LIMIT 50;
    `;
    
    // Replace now() with target datetime if provided
    if (dateTime) {
      query = replaceNowWithDateTime(query, dateTime);
    }
    
    const data = await rawQuery(query);
    res.json({ 
      success: true, 
      data: Array.isArray(data) ? data : [],
      queryDateTime: dateTime || 'current'
    });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

/**
 * GET /api/scanner/stealth-dark
 * Stealth dark pool activity
 * Query params: date (YYYY-MM-DD), time (HH:MM:SS) - optional, defaults to now()
 */
router.get('/stealth-dark', async (req, res) => {
  try {
    const { date, time } = req.query;
    let dateTime = null;
    if (date) {
      dateTime = time ? `${date} ${time}` : date;
    }
    
    let query = `
WITH
params AS (
  SELECT
    (now() AT TIME ZONE 'America/Chicago') AS now_local,
    INTERVAL '30 minutes' AS win_stealth,
    3000000::numeric AS stealth_min_notional,
    100::numeric AS stealth_max_rvol
),
base AS (
  SELECT
    symbol::text AS ticker,
    alert_type::text AS evt_type,
    notional::numeric AS notional,
    pct_of_avg30::numeric AS pct_of_avg30,
    CASE
      WHEN alert_time_txt ~* '(AM|PM)\\s*$'
        THEN to_timestamp(to_char(alert_date,'YYYY-MM-DD') || ' ' || alert_time_txt,
                          'YYYY-MM-DD HH12:MI:SS AM')::timestamp
      ELSE to_timestamp(to_char(alert_date,'YYYY-MM-DD') || ' ' || alert_time_txt,
                        'YYYY-MM-DD HH24:MI:SS')::timestamp
    END AS event_ts_local
  FROM public.alertstream_monthly
  WHERE symbol IS NOT NULL
),
s5_agg AS (
  SELECT b.ticker,
         COUNT(*) AS prints,
         SUM(b.notional) AS notional_usd,
         ROUND(AVG(b.pct_of_avg30),1) AS avg_rvol
  FROM base b
  CROSS JOIN params p
  WHERE b.event_ts_local >= p.now_local - p.win_stealth
    AND b.evt_type = 'DarkPool'
  GROUP BY b.ticker
)
SELECT s.*
FROM s5_agg s CROSS JOIN params p
WHERE s.notional_usd >= p.stealth_min_notional
  AND s.avg_rvol < p.stealth_max_rvol
ORDER BY s.notional_usd DESC
LIMIT 50;
    `;
    
    // Replace now() with target datetime if provided
    if (dateTime) {
      query = replaceNowWithDateTime(query, dateTime);
    }
    
    const data = await rawQuery(query);
    res.json({ 
      success: true, 
      data: Array.isArray(data) ? data : [],
      queryDateTime: dateTime || 'current'
    });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

export default router;

