'use strict';

/**
 * HARNESS RUNNER (Session 64) — joins, runs the backtest, appends the result.
 *
 * Runs on OUR scheduler, in our container. No external dependency: the join is
 * plain SQL through the service client, the harness is a pure function, and the
 * result lands in `harness_results`.
 *
 * THE JOIN: model_snapshots (the model's INPUTS + prediction) → ledger_entries
 * (the single source of truth for OUTCOMES) on the natural key
 * (sport, player_key, stat, line, side, game_date). Outcomes are never
 * denormalized onto snapshots.
 *
 * Rows that don't join are EXPECTED: retention stores both sides of every prop
 * plus refusals, while the ledger keeps only the graded side of graded props.
 */

const harness = require('./backtestHarness');

const HARNESS_VERSION = 'harness@2026-07-20';

/** Pull joined rows. Read-only. */
async function fetchJoinedRows(deps = {}) {
  const getClient = deps.getClient || require('../utils/supabase').getSupabaseServiceClient;
  const sb = getClient();
  if (!sb) return { rows: [], skipped: true };

  // Supabase's client cannot express this join, so it runs as two reads and is
  // joined in memory — the volumes here are hundreds of rows, not millions.
  const [{ data: snaps }, { data: ledger }] = await Promise.all([
    sb.from('model_snapshots')
      .select('sport, model_version, grade, grade_11, p_win, player_key, stat, line, side, game_date, quarantine_reason, refused')
      .eq('refused', false)
      .limit(20000),
    sb.from('ledger_entries')
      .select('sport, player_key, stat, line, side, game_date, outcome, quarantine_reason')
      .is('user_id', null)
      .limit(20000),
  ]);

  const key = (r) => `${r.sport}|${r.player_key}|${r.stat}|${Number(r.line)}|${r.side}|${r.game_date}`;
  const byKey = new Map();
  for (const l of ledger || []) byKey.set(key(l), l);

  const rows = [];
  for (const s of snaps || []) {
    const l = byKey.get(key(s));
    if (!l) continue;                 // expected: unselected side / refusal
    rows.push({
      sport: s.sport,
      model_version: s.model_version,
      grade: s.grade,
      grade_11: s.grade_11,
      p_win: s.p_win,
      outcome: l.outcome,
      quarantine_reason: l.quarantine_reason,
      snap_quarantine: s.quarantine_reason,
    });
  }
  return { rows, skipped: false, joined: rows.length, snapshots: (snaps || []).length };
}

/**
 * Run the harness and append the result. Never throws — a failed harness run
 * must not break the scheduler tick, and the staleness alarm is what surfaces
 * a run that stops happening.
 */
async function runAndRecord(deps = {}) {
  try {
    const { rows, skipped, joined, snapshots } = await fetchJoinedRows(deps);
    if (skipped) return { skipped: true, reason: 'no supabase client' };

    const report = harness.runBacktest(rows, {});
    const getClient = deps.getClient || require('../utils/supabase').getSupabaseServiceClient;
    const sb = getClient();

    const record = {
      verdict: report.verdict,
      can_validate: report.can_validate,
      min_sample: report.min_sample,
      scored: report.counts.scored,
      excluded: {
        quarantine: report.counts.excluded_quarantine,
        terminal: report.counts.excluded_terminal,
        pending: report.counts.excluded_pending,
        push: report.counts.excluded_push,
      },
      report,
      harness_version: HARNESS_VERSION,
    };
    const { error } = await sb.from('harness_results').insert([record]);
    return {
      ok: !error,
      error: error ? error.message : null,
      verdict: report.verdict,
      scored: report.counts.scored,
      joined,
      snapshots,
    };
  } catch (e) {
    return { ok: false, error: e && e.message ? e.message : String(e) };
  }
}

/** Newest run timestamp, for the staleness alarm. */
async function lastRunAt(deps = {}) {
  try {
    const getClient = deps.getClient || require('../utils/supabase').getSupabaseServiceClient;
    const sb = getClient();
    if (!sb) return null;
    const { data } = await sb.from('harness_results')
      .select('ran_at').order('ran_at', { ascending: false }).limit(1);
    return data && data[0] ? data[0].ran_at : null;
  } catch { return null; }
}

module.exports = { runAndRecord, fetchJoinedRows, lastRunAt, HARNESS_VERSION };
