'use strict';

/**
 * deskService — assembles Kev's daily copy-paste arsenal (Session 63 / A1-S4).
 *
 * Pulls the day's REAL pipeline data (snapshots, streaks+lens, ledger
 * aggregate + settled rows) and fills the VOICE v1.1 templates. Output is
 * the /desk pack: every format as text + pre-segmented tweets (≤280) +
 * 2 copy variants (with/without the Stark line) + the DATA BRIEF (structured
 * numbers Kev pastes into claude.ai for freeform writing).
 *
 * NOTHING auto-posts. This service only assembles.
 */

const media = require('./mediaEngine');

const SPORTS = ['mlb', 'wnba', 'nba', 'soccer'];

function variants(text) {
  if (!text) return [];
  // Variant B: the same post without its Stark line (pure numbers). The
  // Stark lines are the only non-data lines, all sourced from the library.
  const starkSet = new Set(Object.values(media.__internals.STARK).flat());
  const stripped = text.split('\n').filter((l) => !starkSet.has(l)).join('\n');
  const out = [{ label: 'wire', text, tweets: media.segmentThread(text) }];
  if (stripped !== text) out.push({ label: 'numbers only', text: stripped, tweets: media.segmentThread(stripped) });
  return out;
}

/**
 * Assemble the pack. deps injectable: cacheGet, loadRosterLogs,
 * computeStreaks/computeFormHeat/applyLens, ledger (getModelAggregate + sb
 * reads), now.
 */
async function assembleDeskPack(opts = {}) {
  const deps = {
    cacheGet: opts.cacheGet || require('../utils/redis').cacheGet,
    loadRosterLogs: opts.loadRosterLogs || require('./rosterLogs').loadRosterLogs,
    streaks: opts.streaks || require('./streaksService'),
    lens: opts.lens || require('./streakLens'),
    ledger: opts.ledger || require('./ledgerService'),
    now: opts.now || (() => new Date().toISOString()),
  };
  const nowIso = deps.now();

  // 1. Snapshots → counts, loudest signal, signals, archetype watch, dispatches.
  const counts = {};
  let allGrades = [];
  for (const sp of SPORTS) {
    const snap = await deps.cacheGet(`snapshot:${sp}:latest`);
    const grades = snap && Array.isArray(snap.grades) ? snap.grades : [];
    counts[sp] = grades.length;
    allGrades = allGrades.concat(grades.map((g) => ({ ...g, sport: sp })));
  }
  const graded = allGrades.filter((g) => g.grade && !g.outcome);
  graded.sort((a, b) => (Number(b.confidence) || 0) - (Number(a.confidence) || 0));
  const loudest = graded[0] || null;

  // 2. Streaks through the lens (MLB primary; extendable).
  let streakRows = [];
  try {
    const roster = await deps.loadRosterLogs('mlb');
    const rows = [
      ...deps.streaks.computeStreaks(roster, 'mlb', { limit: 6 }),
      ...deps.streaks.computeFormHeat(roster, 'mlb', { limit: 3 }),
    ];
    const sched = await deps.cacheGet(`schedule:mlb:${nowIso.slice(0, 10)}`);
    streakRows = deps.lens.applyLens(rows, { scheduleGames: Array.isArray(sched) ? sched : [], pitcherGames: [] });
  } catch { /* streak section degrades to absent */ }

  // 3. Ledger — aggregate + yesterday's settled rows for the Settle/Receipts.
  const aggregate = await deps.ledger.getModelAggregate({});
  let settledRows = [];
  try {
    if (opts.fetchSettled) settledRows = await opts.fetchSettled();
    else {
      const { getSupabaseServiceClient } = require('../utils/supabase');
      if (deps.ledger.__internals.isConfigured()) {
        const sb = getSupabaseServiceClient();
        const { data } = await sb.from('ledger_entries')
          .select('player_name, stat, line, side, grade, graded_at, locked_odds, closing_line, closing_odds, clv, clv_result, outcome, actual_value, settled_at')
          .is('user_id', null).not('outcome', 'is', null)
          .order('settled_at', { ascending: false }).limit(30);
        settledRows = data || [];
      }
    }
  } catch { /* receipts degrade to absent */ }
  const misses = settledRows.filter((r) => r.outcome === 'miss');

  // 4. Fill the formats. Absent data → absent format, never filler.
  const dispatches = graded
    .filter((g) => g.movement && (g.movement.kind === 'steam' || g.movement.kind === 'value'))
    .slice(0, 4)
    .map((g) => media.lineDispatch(g, nowIso))
    .filter(Boolean);

  const pack = {
    generated_at: nowIso,
    formats: {
      morning_wire: variants(media.morningWire({ dateIso: nowIso, counts, loudest })),
      signals: graded.slice(0, 3).map((g) => variants(media.signal(g))).filter((v) => v.length),
      streak_watch: variants(media.streakWatch(streakRows, nowIso)),
      settle: variants(media.theSettle({ aggregate, misses, dateIso: nowIso })),
      receipts: settledRows.slice(0, 3).map((r) => variants(media.receipt(r))).filter((v) => v.length),
      archetype_watch: variants(media.archetypeWatch(graded, nowIso)),
      line_dispatches: dispatches.map((d) => variants(d)),
    },
    // 5. DATA BRIEF — the day's structured numbers for freeform writing.
    data_brief: {
      date: nowIso,
      slate_counts: counts,
      top_signals: graded.slice(0, 8).map((g) => ({
        player: g.player || g.player_name, sport: g.sport, stat: g.stat_type || g.stat,
        line: g.line, side: g.direction, grade: g.grade, confidence: g.confidence,
        archetype: g.archetype || null, odds: (g.gradedAt && g.gradedAt.odds) || null,
        movement: g.movement || null,
      })),
      streaks: streakRows.slice(0, 8).map((r) => ({ player: r.player, team: r.team, read: r.lens && r.lens.read })),
      record: aggregate,
      settled_today: settledRows.slice(0, 20),
    },
  };
  return pack;
}

module.exports = { assembleDeskPack, __internals: { variants, SPORTS } };
