import { query } from '../../db';
import { alerter } from '../../services/alerter';

// Hard guardrails
const GUARDRAILS = {
  maxWeightChangePct: 0.20,     // max 20% weight change per week
  minSampleSize: 200,           // minimum sample before adjusting
  minInfraHealthScore: 70,      // disabled if infra is unhealthy
  minConsecutivePositiveWeeks: 2, // need 2 weeks of positive CLV trend
};

export async function runSelfImprover(): Promise<void> {
  try {
    // Check infra health first
    const stateResult = await query(`SELECT infra_health_score FROM sc_global_state WHERE id = 'primary'`);
    const infraScore = parseFloat(stateResult.rows[0]?.infra_health_score || '0');

    if (infraScore < GUARDRAILS.minInfraHealthScore) {
      await alerter.logEvent(
        'self_improver_skipped', 'info', 'self-improver',
        `Skipped: infra health ${infraScore} below threshold ${GUARDRAILS.minInfraHealthScore}`
      );
      return;
    }

    // Check CLV trend requirement: positive for 2 consecutive weeks
    const trendResult = await query(`
      SELECT metric_date, avg_clv
      FROM sc_edge_metrics
      WHERE window_size = 200 AND league = 'nba'
      ORDER BY metric_date DESC LIMIT 14
    `);

    if (trendResult.rows.length < 14) {
      await alerter.logEvent(
        'self_improver_skipped', 'info', 'self-improver',
        'Skipped: insufficient trend data (need 14+ days)'
      );
      return;
    }

    // Check last 2 weeks of CLV trend
    const week1 = trendResult.rows.slice(0, 7);
    const week2 = trendResult.rows.slice(7, 14);
    const week1Avg = week1.reduce((sum, r) => sum + parseFloat(r.avg_clv || '0'), 0) / week1.length;
    const week2Avg = week2.reduce((sum, r) => sum + parseFloat(r.avg_clv || '0'), 0) / week2.length;

    if (week1Avg <= 0 || week2Avg <= 0) {
      await alerter.logEvent(
        'self_improver_skipped', 'info', 'self-improver',
        `Skipped: CLV trend not positive for 2 consecutive weeks (week1: ${week1Avg.toFixed(3)}, week2: ${week2Avg.toFixed(3)})`
      );
      return;
    }

    const changes: Array<{ dimension: string; value: string; oldWeight: number; newWeight: number }> = [];

    // 1. Pull false positive rates and compute weight adjustments
    const fpResult = await query(`
      SELECT dimension, dimension_value, false_positive_rate, sample_size, weight_adjustment
      FROM sc_scoring_weights
      WHERE effective_from = (
        SELECT MAX(effective_from) FROM sc_scoring_weights
        WHERE effective_from <= CURRENT_DATE
      )
      AND sample_size >= $1
    `, [GUARDRAILS.minSampleSize]);

    for (const row of fpResult.rows) {
      const currentWeight = parseFloat(row.weight_adjustment);
      const fpRate = parseFloat(row.false_positive_rate);

      // Compute ideal weight: penalize high FP rates
      let idealWeight = 1.0 - Math.max(0, fpRate - 0.3);
      idealWeight = Math.max(0.3, Math.min(1.5, idealWeight));

      // Apply guardrail: max 20% change
      const maxDelta = currentWeight * GUARDRAILS.maxWeightChangePct;
      let newWeight = currentWeight;

      if (idealWeight > currentWeight) {
        newWeight = Math.min(currentWeight + maxDelta, idealWeight);
      } else if (idealWeight < currentWeight) {
        newWeight = Math.max(currentWeight - maxDelta, idealWeight);
      }

      // Only update if meaningful change
      if (Math.abs(newWeight - currentWeight) > 0.01) {
        const nextWeek = new Date();
        nextWeek.setDate(nextWeek.getDate() + 7);
        const effectiveDate = nextWeek.toISOString().split('T')[0];

        await query(`
          INSERT INTO sc_scoring_weights (dimension, dimension_value, weight_adjustment, false_positive_rate, sample_size, effective_from)
          VALUES ($1, $2, $3, $4, $5, $6)
          ON CONFLICT (dimension, dimension_value, effective_from) DO UPDATE SET
            weight_adjustment = $3, false_positive_rate = $4, sample_size = $5
        `, [row.dimension, row.dimension_value, newWeight.toFixed(3), fpRate, row.sample_size, effectiveDate]);

        changes.push({
          dimension: row.dimension,
          value: row.dimension_value,
          oldWeight: currentWeight,
          newWeight,
        });
      }
    }

    // 2. Flag leagues needing recalibration
    const decayResult = await query(`
      SELECT league, edge_trend, avg_clv
      FROM sc_edge_metrics
      WHERE metric_date = CURRENT_DATE - 1 AND window_size = 200
        AND edge_trend IN ('decaying', 'critical')
    `);

    const decayingLeagues = decayResult.rows.map(r => `${r.league} (${r.edge_trend}, CLV: ${parseFloat(r.avg_clv).toFixed(3)})`);

    // 3. Pull simulation recommendations
    const simResult = await query(`
      SELECT recommendation, vs_current
      FROM sc_simulation_runs
      WHERE run_date >= CURRENT_DATE - 7
      ORDER BY run_date DESC LIMIT 1
    `);

    const simRecommendation = simResult.rows[0]?.recommendation || 'No recent simulation data';

    // Log all changes to audit trail
    if (changes.length > 0) {
      await query(`
        INSERT INTO sc_admin_log (action, details)
        VALUES ('self_improver_weight_update', $1)
      `, [JSON.stringify({
        changes,
        guardrails: GUARDRAILS,
        clv_trend: { week1: week1Avg, week2: week2Avg },
      })]);
    }

    // Generate improvement report
    const reportLines = [
      `Weight Changes: ${changes.length}`,
      ...changes.map(c => `  ${c.dimension}/${c.value}: ${c.oldWeight.toFixed(3)} → ${c.newWeight.toFixed(3)}`),
      '',
      `Decaying Leagues: ${decayingLeagues.length > 0 ? decayingLeagues.join(', ') : 'None'}`,
      '',
      `Simulation: ${simRecommendation}`,
      '',
      `CLV Trend: Week1 avg ${week1Avg.toFixed(3)}, Week2 avg ${week2Avg.toFixed(3)}`,
      `Infra Health: ${infraScore}/100`,
    ];

    // Send report to admins
    await alerter.alertAdmins(
      'info',
      `🧠 Weekly Self-Improvement Report\n\n${reportLines.join('\n')}`,
      { changes, decaying_leagues: decayingLeagues, simulation: simRecommendation }
    );

    await alerter.logEvent(
      'self_improver_complete', 'info', 'self-improver',
      `Self-improvement: ${changes.length} weight changes applied`,
      { changes_count: changes.length }
    );
  } catch (err) {
    console.error('[self-improver] Error:', err);
    throw err;
  }
}
