import { generateSyntheticData } from './synthetic.js';
import { loadUniverse } from './universe.js';
import { loadFundamentals } from './fundamentals.js';
import { setForwardReturnsMap, runFactor } from './crossSection.js';
import { mean, neweyWestStdErr } from './metrics.js';

function printReport(title, results, horizonMonths) {
  if (results.length === 0) {
    console.log(`\n=== ${title} ===`);
    console.log("No valid results.");
    return;
  }

  const ics = results.map(r => r.ic);
  const meanIc = mean(ics);
  // We use Newey-West standard error for the t-stat if overlapping.
  // For 1-month horizon, lags = 0 (simple std err). 
  // For H-month horizon, lags = H - 1.
  const lags = Math.max(0, horizonMonths - 1);
  const seIc = neweyWestStdErr(ics, lags);
  const tStatIc = meanIc / seIc;

  // Average Decile Returns (Gross)
  const deciles = Array(10).fill(0);
  for (const r of results) {
    for (let d = 0; d < 10; d++) {
      deciles[d] += r.decileReturns[d];
    }
  }
  for (let d = 0; d < 10; d++) deciles[d] /= results.length;

  // Long/Short Spread (Net)
  const lsSpreads = results.map(r => r.d10Net - r.d1Net);
  const meanLsSpread = mean(lsSpreads);
  const seLsSpread = neweyWestStdErr(lsSpreads, lags);
  const tStatLs = meanLsSpread / seLsSpread;
  const sharpeLs = (meanLsSpread / (seLsSpread * Math.sqrt(results.length))) * Math.sqrt(12 / horizonMonths); // Approx Annualized Gross Sharpe

  // Long-Only Top-Decile Excess (Net)
  const loExcess = results.map(r => r.d10Net - r.universeMeanRet);
  const meanLoExcess = mean(loExcess);

  // Consistency
  const numPositiveLs = lsSpreads.filter(s => s > 0).length;
  const consistencyPct = (numPositiveLs / lsSpreads.length) * 100;

  // Turnover (Avg over periods)
  const avgTurnoverD10 = mean(results.map(r => r.d10Turnover)) * 100;

  console.log(`\n=== ${title} ===`);
  console.log(`Periods (N):         ${results.length} months`);
  console.log(`Horizon:             ${horizonMonths} month(s)`);
  console.log(`Mean IC:             ${meanIc.toFixed(4)}`);
  console.log(`IC t-stat:           ${tStatIc.toFixed(2)}  (Target: >= 3.0)`);
  console.log(`L/S Spread (Net):    ${(meanLsSpread * 100).toFixed(2)}% per period (t-stat: ${tStatLs.toFixed(2)})`);
  console.log(`Long-Only Excess:    ${(meanLoExcess * 100).toFixed(2)}% per period`);
  console.log(`Consistency:         ${consistencyPct.toFixed(1)}% positive periods`);
  console.log(`D10 Avg Turnover:    ${avgTurnoverD10.toFixed(1)}% per rebalance`);
  
  console.log(`\nDecile Monotonicity (Gross):`);
  const maxRet = Math.max(...deciles);
  const minRet = Math.min(...deciles);
  for (let d = 0; d < 10; d++) {
    // simple text bar chart
    const pct = deciles[d] * 100;
    const barLen = Math.max(1, Math.floor((deciles[d] - minRet) / (maxRet - minRet + 0.0001) * 20));
    console.log(`  D${d+1}: ${pct.toFixed(2).padStart(5)}% | ${'#'.repeat(barLen)}`);
  }
}

function runSyntheticTests() {
  console.log("==================================================");
  console.log("   SYNTHETIC KNOWN-ANSWER TESTS");
  console.log("==================================================");

  // Generate 25 years (300 months) of data for 500 stocks
  // 1. NULL TEST (IC = 0)
  const nullData = generateSyntheticData({ numMonths: 300, numStocks: 500, plantedIC: 0.0 });
  loadUniverse(nullData.universe);
  loadFundamentals(nullData.fundamentals);
  setForwardReturnsMap(nullData.forwardReturnsMap);

  const allDatesNull = nullData.universe.map(u => u.date);
  const resultsNull = runFactor('value', allDatesNull, 1, 5); // 5 bps costs
  printReport("TEST 1: NULL FACTOR (Target: IC ≈ 0, t-stat < 3)", resultsNull, 1);

  // 2. PLANTED SIGNAL TEST (IC = 0.05)
  const plantedData = generateSyntheticData({ numMonths: 300, numStocks: 500, plantedIC: 0.05 });
  loadUniverse(plantedData.universe);
  loadFundamentals(plantedData.fundamentals);
  setForwardReturnsMap(plantedData.forwardReturnsMap);

  const allDatesPlanted = plantedData.universe.map(u => u.date);
  const resultsPlanted = runFactor('value', allDatesPlanted, 1, 5); // 5 bps costs
  printReport("TEST 2: PLANTED SIGNAL (Target: IC ≈ 0.05, t-stat >= 3, Monotonic)", resultsPlanted, 1);
}

runSyntheticTests();
