/**
 * Deep-dive on the 3 most promising signals from the edge search:
 *
 * 1. MEAN REVERSION after large moves (t=6.53 at 120s→300s)
 * 2. CROSS-EXCHANGE DIVERGENCE: follow Bybit when Binance disagrees (t=5.12)
 * 3. HIGH-VOL OFI: delta+surge in rv>12 regime (t=4.62)
 *
 * For each: regime-conditioned analysis, transaction cost breakeven,
 * session-level consistency, implementable strategy simulation.
 */
import fs from 'node:fs'
import readline from 'node:readline'
import type { Bar10s } from '../core/strategy.js'

interface RBar extends Bar10s {
  ts: number
  exBuyVol: Record<string, number>
  exSellVol: Record<string, number>
}

function tTest(v: number[]): { mean: number; t: number; n: number } {
  const n = v.length; if (n < 3) return { mean: 0, t: 0, n }
  const mean = v.reduce((a, b) => a + b, 0) / n
  const variance = v.reduce((s, x) => s + (x - mean) ** 2, 0) / (n - 1)
  const se = Math.sqrt(variance / n)
  return { mean, t: se > 0 ? mean / se : 0, n }
}

function sig(t: number): string {
  const a = Math.abs(t)
  return a > 3.29 ? '***' : a > 2.58 ? '**' : a > 1.96 ? '*' : ''
}

function percentile(arr: number[], p: number): number {
  const s = [...arr].sort((a, b) => a - b)
  return s[Math.min(Math.floor(s.length * p), s.length - 1)]
}

async function loadAllHistorical(): Promise<Map<string, RBar[]>> {
  const sessions = new Map<string, RBar[]>()
  const histDir = 'data/historical'
  const dates = fs.readdirSync(histDir).filter(d => {
    try { return fs.statSync(`${histDir}/${d}/trades.jsonl`).size > 100_000 } catch { return false }
  }).sort()
  for (const date of dates) {
    process.stderr.write(`Loading ${date}...`)
    const barsMap = new Map<number, RBar>()
    const rl = readline.createInterface({ input: fs.createReadStream(`${histDir}/${date}/trades.jsonl`) })
    for await (const line of rl) {
      const d = JSON.parse(line)
      const k = Math.floor(d.ts / 10000)
      const p = d.data.price, ex = d.data.exchange, side = d.data.side, vol = d.data.notionalUsd
      const e = barsMap.get(k)
      if (!e) {
        barsMap.set(k, {
          ts: k * 10000, o: p, h: p, l: p, c: p,
          buyVol: side === 'buy' ? vol : 0, sellVol: side === 'sell' ? vol : 0,
          n: 1, exVol: { [ex]: vol },
          exBuyVol: { [ex]: side === 'buy' ? vol : 0 },
          exSellVol: { [ex]: side === 'sell' ? vol : 0 }
        })
      } else {
        e.h = Math.max(e.h, p); e.l = Math.min(e.l, p); e.c = p
        if (side === 'buy') e.buyVol += vol; else e.sellVol += vol
        e.n++; e.exVol[ex] = (e.exVol[ex] || 0) + vol
        e.exBuyVol[ex] = (e.exBuyVol[ex] || 0) + (side === 'buy' ? vol : 0)
        e.exSellVol[ex] = (e.exSellVol[ex] || 0) + (side === 'sell' ? vol : 0)
      }
    }
    sessions.set(date, [...barsMap.values()].sort((a, b) => a.ts - b.ts))
    process.stderr.write(` ${sessions.get(date)!.length}\n`)
  }
  return sessions
}

function delta(b: RBar): number { const v = b.buyVol + b.sellVol; return v > 0 ? (b.buyVol - b.sellVol) / v : 0 }
function exDelta(b: RBar, ex: string): number { const bv = b.exBuyVol[ex]||0, sv = b.exSellVol[ex]||0, t = bv+sv; return t > 0 ? (bv-sv)/t : 0 }
function fwdRet(bars: RBar[], i: number, fwd: number): number { return i+fwd < bars.length && bars[i].c > 0 ? (bars[i+fwd].c - bars[i].c) / bars[i].c * 10000 : NaN }
function momentum(bars: RBar[], i: number, n: number): number { return i >= n && bars[i-n].c > 0 ? (bars[i].c - bars[i-n].c) / bars[i-n].c * 10000 : 0 }
function rv(bars: RBar[], i: number, lb = 6): number {
  if (i < lb) return 0
  const r: number[] = []
  for (let j = i - lb + 1; j <= i; j++) if (bars[j-1].c > 0) r.push((bars[j].c - bars[j-1].c) / bars[j-1].c * 10000)
  if (r.length < 3) return 0
  const m = r.reduce((a,b)=>a+b,0)/r.length
  return Math.sqrt(r.reduce((s,x)=>s+(x-m)**2,0)/(r.length-1))
}
function rollingDelta(bars: RBar[], i: number, n: number): number {
  let bv = 0, sv = 0
  for (let j = Math.max(0,i-n+1); j <= i; j++) { bv += bars[j].buyVol; sv += bars[j].sellVol }
  const t = bv + sv; return t > 0 ? (bv - sv) / t : 0
}
function surge(bars: RBar[], i: number): number {
  const cur = bars[i].buyVol + bars[i].sellVol
  let avg = 0
  for (let j = Math.max(0, i-20); j < i; j++) avg += bars[j].buyVol + bars[j].sellVol
  avg /= Math.min(20, i); return avg > 0 ? cur / avg : 1
}

// ── Strategy simulation helper ──
interface SimTrade {
  entryBar: number; exitBar: number; side: 'long'|'short'
  entryPrice: number; exitPrice: number; grossBps: number; netBps: number
}

function simulate(
  bars: RBar[],
  signalFn: (bars: RBar[], i: number) => 'long'|'short'|null,
  holdBars: number,
  feeBps: number,  // one-way fee (entry + exit = 2 * feeBps)
  cooldownBars: number
): SimTrade[] {
  const trades: SimTrade[] = []
  let lastExit = -cooldownBars
  for (let i = 60; i < bars.length - holdBars; i++) {
    if (i - lastExit < cooldownBars) continue
    const dir = signalFn(bars, i)
    if (!dir) continue
    const entry = bars[i].c
    const exit = bars[i + holdBars].c
    const grossBps = dir === 'long' ? (exit - entry) / entry * 10000 : (entry - exit) / entry * 10000
    const netBps = grossBps - 2 * feeBps
    trades.push({ entryBar: i, exitBar: i + holdBars, side: dir, entryPrice: entry, exitPrice: exit, grossBps, netBps })
    lastExit = i + holdBars
  }
  return trades
}

function simReport(name: string, trades: SimTrade[], sessionName?: string) {
  if (trades.length === 0) return
  const gross = trades.map(t => t.grossBps)
  const net = trades.map(t => t.netBps)
  const tGross = tTest(gross), tNet = tTest(net)
  const wins = net.filter(r => r > 0).length
  const cumNet = net.reduce((a, b) => a + b, 0)
  const maxDd = (() => {
    let peak = 0, dd = 0, cum = 0
    for (const r of net) { cum += r; if (cum > peak) peak = cum; const d = peak - cum; if (d > dd) dd = d }
    return dd
  })()
  const prefix = sessionName ? `    ${sessionName}` : `  ${name}`
  console.log(`${prefix.padEnd(30)} n=${String(trades.length).padStart(4)} WR=${(wins/trades.length*100).toFixed(0)}% gross=${tGross.mean.toFixed(2)}bps(t=${tGross.t.toFixed(2)}) net=${tNet.mean.toFixed(2)}bps(t=${tNet.t.toFixed(2)}) cum=${cumNet.toFixed(1)}bps maxDD=${maxDd.toFixed(1)}bps ${sig(tNet.t)}`)
}

async function main() {
  const sessions = await loadAllHistorical()

  console.log(`\n${'═'.repeat(90)}`)
  console.log('DEEP DIVE: ACTIONABLE EDGES')
  console.log(`${'═'.repeat(90)}\n`)

  // ════════════════════════════════════════════════════════
  // SIGNAL A: MEAN REVERSION (strongest pooled t-stat)
  // ════════════════════════════════════════════════════════
  console.log('A. MEAN REVERSION AFTER LARGE MOVES')
  console.log('   After top-decile 120s price move, bet on reversion')
  console.log()

  // Sweep: lookback × hold × fee
  console.log('  A.1 Parameter sweep (maker 2bps each way):')
  for (const lookback of [6, 12, 30]) {
    for (const hold of [6, 12, 30, 60]) {
      const allTrades: SimTrade[] = []
      for (const [, bars] of sessions) {
        const thresh = percentile(
          bars.slice(lookback + 60).map((_, j) => Math.abs(momentum(bars, j + lookback + 60, lookback))).filter(v => v > 0), 0.90)
        const trades = simulate(bars, (b, i) => {
          const move = momentum(b, i, lookback)
          if (Math.abs(move) < thresh) return null
          return move > 0 ? 'short' : 'long'  // reversion
        }, hold, 2, hold + 3)
        allTrades.push(...trades)
      }
      simReport(`lb=${lookback} hold=${hold}`, allTrades)
    }
  }

  // A.2 Session-level consistency
  console.log()
  console.log('  A.2 Best config (lb=12, hold=30) per session:')
  let totalA = 0, posA = 0
  for (const [name, bars] of sessions) {
    const thresh = percentile(
      bars.slice(72).map((_, j) => Math.abs(momentum(bars, j + 72, 12))).filter(v => v > 0), 0.90)
    const trades = simulate(bars, (b, i) => {
      const move = momentum(b, i, 12)
      if (Math.abs(move) < thresh) return null
      return move > 0 ? 'short' : 'long'
    }, 30, 2, 33)
    if (trades.length >= 3) {
      totalA++; if (tTest(trades.map(t => t.netBps)).mean > 0) posA++
      simReport('', trades, name)
    }
  }
  console.log(`  Positive sessions: ${posA}/${totalA}`)

  // A.3 Fee sensitivity
  console.log()
  console.log('  A.3 Fee sensitivity (lb=12, hold=30):')
  for (const fee of [0, 1, 2, 3, 4, 5.5]) {
    const allTrades: SimTrade[] = []
    for (const [, bars] of sessions) {
      const thresh = percentile(
        bars.slice(72).map((_, j) => Math.abs(momentum(bars, j + 72, 12))).filter(v => v > 0), 0.90)
      allTrades.push(...simulate(bars, (b, i) => {
        const move = momentum(b, i, 12)
        return Math.abs(move) < thresh ? null : move > 0 ? 'short' : 'long'
      }, 30, fee, 33))
    }
    const tt = tTest(allTrades.map(t => t.netBps))
    console.log(`    fee=${String(fee).padStart(4)}bps: mean_net=${tt.mean.toFixed(2)}bps t=${tt.t.toFixed(2)} n=${tt.n} ${sig(tt.t)}`)
  }

  // ════════════════════════════════════════════════════════
  // SIGNAL B: FOLLOW BYBIT WHEN BINANCE DISAGREES
  // ════════════════════════════════════════════════════════
  console.log(`\n${'─'.repeat(90)}`)
  console.log('B. CROSS-EXCHANGE DIVERGENCE: FOLLOW BYBIT')
  console.log('   When Binance and Bybit deltas disagree, Bybit is right')
  console.log()

  console.log('  B.1 Parameter sweep:')
  for (const deltaThresh of [0.15, 0.20, 0.25, 0.30]) {
    for (const hold of [3, 6, 12, 30]) {
      const allTrades: SimTrade[] = []
      for (const [, bars] of sessions) {
        allTrades.push(...simulate(bars, (b, i) => {
          const bd = exDelta(b[i], 'BINANCE'), byd = exDelta(b[i], 'BYBIT')
          if (bd * byd >= 0 || Math.abs(bd) < deltaThresh || Math.abs(byd) < deltaThresh) return null
          return byd > 0 ? 'long' : 'short'  // follow Bybit
        }, hold, 2, hold + 3))
      }
      simReport(`δ>${deltaThresh} hold=${hold}`, allTrades)
    }
  }

  // B.2 Session-level
  console.log()
  console.log('  B.2 Best config (δ>0.20, hold=12) per session:')
  let totalB = 0, posB = 0
  for (const [name, bars] of sessions) {
    const trades = simulate(bars, (b, i) => {
      const bd = exDelta(b[i], 'BINANCE'), byd = exDelta(b[i], 'BYBIT')
      if (bd * byd >= 0 || Math.abs(bd) < 0.20 || Math.abs(byd) < 0.20) return null
      return byd > 0 ? 'long' : 'short'
    }, 12, 2, 15)
    if (trades.length >= 3) {
      totalB++; if (tTest(trades.map(t => t.netBps)).mean > 0) posB++
      simReport('', trades, name)
    }
  }
  console.log(`  Positive sessions: ${posB}/${totalB}`)

  // B.3 Fee sensitivity
  console.log()
  console.log('  B.3 Fee sensitivity (δ>0.20, hold=12):')
  for (const fee of [0, 1, 2, 3, 4, 5.5]) {
    const allTrades: SimTrade[] = []
    for (const [, bars] of sessions) {
      allTrades.push(...simulate(bars, (b, i) => {
        const bd = exDelta(b[i], 'BINANCE'), byd = exDelta(b[i], 'BYBIT')
        if (bd * byd >= 0 || Math.abs(bd) < 0.20 || Math.abs(byd) < 0.20) return null
        return byd > 0 ? 'long' : 'short'
      }, 12, fee, 15))
    }
    const tt = tTest(allTrades.map(t => t.netBps))
    console.log(`    fee=${String(fee).padStart(4)}bps: mean_net=${tt.mean.toFixed(2)}bps t=${tt.t.toFixed(2)} n=${tt.n} ${sig(tt.t)}`)
  }

  // ════════════════════════════════════════════════════════
  // SIGNAL C: HIGH-VOL OFI (rv > 12)
  // ════════════════════════════════════════════════════════
  console.log(`\n${'─'.repeat(90)}`)
  console.log('C. HIGH-VOLATILITY REGIME OFI')
  console.log('   Delta+surge in rv>12 regime → continuation')
  console.log()

  console.log('  C.1 Parameter sweep:')
  for (const rvMin of [8, 10, 12, 15]) {
    for (const hold of [3, 6, 12, 30]) {
      const allTrades: SimTrade[] = []
      for (const [, bars] of sessions) {
        allTrades.push(...simulate(bars, (b, i) => {
          const r = rv(b, i); if (r < rvMin) return null
          const d = rollingDelta(b, i, 5)
          if (Math.abs(d) < 0.25) return null
          const s = surge(b, i)
          if (s < 2) return null
          return d > 0 ? 'long' : 'short'
        }, hold, 2, hold + 3))
      }
      simReport(`rv>${rvMin} hold=${hold}`, allTrades)
    }
  }

  // C.2 Session-level
  console.log()
  console.log('  C.2 Best config (rv>12, hold=12) per session:')
  let totalC = 0, posC = 0
  for (const [name, bars] of sessions) {
    const trades = simulate(bars, (b, i) => {
      const r = rv(b, i); if (r < 12) return null
      const d = rollingDelta(b, i, 5); if (Math.abs(d) < 0.25) return null
      const s = surge(b, i); if (s < 2) return null
      return d > 0 ? 'long' : 'short'
    }, 12, 2, 15)
    if (trades.length >= 3) {
      totalC++; if (tTest(trades.map(t => t.netBps)).mean > 0) posC++
      simReport('', trades, name)
    }
  }
  console.log(`  Positive sessions: ${posC}/${totalC}`)

  // ════════════════════════════════════════════════════════
  // SIGNAL D: COMBINED — mean-reversion + divergence filter
  // ════════════════════════════════════════════════════════
  console.log(`\n${'─'.repeat(90)}`)
  console.log('D. COMBINED: MEAN REVERSION + BYBIT DIVERGENCE CONFIRMATION')
  console.log('   Reversion signal, only take if Bybit delta agrees with reversion direction')
  console.log()

  for (const hold of [6, 12, 30]) {
    const allTrades: SimTrade[] = []
    for (const [, bars] of sessions) {
      const thresh = percentile(
        bars.slice(72).map((_, j) => Math.abs(momentum(bars, j + 72, 12))).filter(v => v > 0), 0.90)
      allTrades.push(...simulate(bars, (b, i) => {
        const move = momentum(b, i, 12)
        if (Math.abs(move) < thresh) return null
        const revDir: 'long'|'short' = move > 0 ? 'short' : 'long'
        // Confirm with Bybit delta
        const byd = exDelta(b[i], 'BYBIT')
        if (revDir === 'long' && byd < 0.10) return null
        if (revDir === 'short' && byd > -0.10) return null
        return revDir
      }, hold, 2, hold + 3))
    }
    simReport(`rev+bybit hold=${hold}`, allTrades)
  }

  // Session-level for best
  console.log()
  console.log('  D.2 Best config (hold=30) per session:')
  let totalD = 0, posD = 0
  for (const [name, bars] of sessions) {
    const thresh = percentile(
      bars.slice(72).map((_, j) => Math.abs(momentum(bars, j + 72, 12))).filter(v => v > 0), 0.90)
    const trades = simulate(bars, (b, i) => {
      const move = momentum(b, i, 12)
      if (Math.abs(move) < thresh) return null
      const revDir: 'long'|'short' = move > 0 ? 'short' : 'long'
      const byd = exDelta(b[i], 'BYBIT')
      if (revDir === 'long' && byd < 0.10) return null
      if (revDir === 'short' && byd > -0.10) return null
      return revDir
    }, 30, 2, 33)
    if (trades.length >= 3) {
      totalD++; if (tTest(trades.map(t => t.netBps)).mean > 0) posD++
      simReport('', trades, name)
    }
  }
  console.log(`  Positive sessions: ${posD}/${totalD}`)

  // ════════════════════════════════════════════════════════
  // SUMMARY
  // ════════════════════════════════════════════════════════
  console.log(`\n${'═'.repeat(90)}`)
  console.log('SUMMARY')
  console.log('Criteria for actionable edge:')
  console.log('  1. t > 2.0 after fees (2bps maker each way = 4bps round trip)')
  console.log('  2. Positive in > 60% of sessions')
  console.log('  3. mean_net > 0.5 bps per trade (covers slippage uncertainty)')
  console.log(`${'═'.repeat(90)}`)
}

main().catch(e => { process.stderr.write(`FATAL: ${e instanceof Error ? e.stack : e}\n`); process.exit(1) })
