// silver.json read/write + lesson catalog access. Mirrors the existing
// per-mentee file pattern in /mentees/{slug}/.

import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync, readdirSync } from 'fs';
import { join, dirname } from 'path';
import { randomBytes } from 'crypto';
import { now, nowIso } from './clock.js';

const NINETY_DAYS_MS = 90 * 86_400_000;

// ── Schema defaults (used by initSilver + migrateSilver) ─────────────────────
// Keep this exhaustive; any new top-level field MUST appear here so migrate
// fills it for existing mentees on load.
export function defaultSilverState() {
  return {
    tier: 'silver',
    slug: null,
    leadName: null,
    locale: 'en',                     // 'en' | 'pt' | 'es' | 'fr' — drives messages.{locale}.json
    groupJid: null,
    clientPhone: null,
    coachJid: null,                   // null = unassigned (coach claims from admin dashboard)
    coachName: null,
    coachJoinedAt: null,              // ECO-39: lessons gated until coach joins the group
    coachJoinSource: null,            // 'baileys-event' | 'manual-admin' | 'force-cli'
    email: null,                      // Hotmart buyer email
    startedAt: null,
    accessExpiresAt: null,            // startedAt + 90d once payment approved
    currentDay: 1,

    // Hotmart (ECO-5/24)
    hotmartTransactionId: null,
    hotmartProductId: null,
    hotmartOfferId: null,
    paymentStatus: null,              // approved | pending | refunded | chargeback | canceled | expired
    pausedAt: null,
    pausedReason: null,

    // Coach gating (ECO-6/15/38)
    accessLevel: 'course_only',       // course_only | dossier_pending | full
    accessLevelChangedAt: null,
    accessLevelChangedBy: null,
    module1CompletedAt: null,
    module2CompletedAt: null,
    module3CompletedAt: null,
    module4CompletedAt: null,
    // Module completion bools — true ONLY when both lessons in module N are
    // all completedAt AND all tasks tagged for module N (gatesModule or
    // week === N) are status='claimed'. See maybeMarkModuleComplete().
    modulesCompleted: { 1: false, 2: false, 3: false, 4: false },
    coachTriggeredAt: null,           // when coach clicked "Trigger Bot" (ECO-15/38)
    coachTriggeredBy: null,
    coachUnlockedAt: null,            // when coach clicked "Unlock Job-bot" (ECO-15/38)
    coachUnlockedBy: null,

    dossier: {
      status: 'not_started',          // not_started | collecting | ready_for_review | approved
      collectedAt: null,
      collectionTurns: [],            // [{ q, a, at }]
      generatedAt: null,
      strategyPath: null,             // mentees/{slug}/career-strategy.json
      reviewedBy: null,
      reviewedAt: null,
      jobbotPlanId: null,
    },
    // ECO-41: website-side strategy nudge tracking. After Module 1
    // completion the bot tells the user to fill the strategy on the
    // website + nudges daily until detected.
    strategyNudgesSent: 0,
    strategyLastNudgedAt: null,
    strategyDetectedAt: null,         // when bot first noticed career-strategy.json on disk

    whatsappToken: null,              // { value, expiresAt, mintedAt, consumedAt } — minted on coach Unlock

    // Community (ECO-20)
    communityAddedAt: null,
    communityAddedBy: null,

    // Tracker (ECO-16/17/18)
    shownRoles: [],                   // [{ jobbotRoleId, sentAt, response: 'applied'|'skipped'|null, infoRequestedAt }]
    appliedRoles: [],                 // [{ jobbotRoleId, title, company, location, applyUrl, appliedAt, status, statusHistory[], nextStatusCheckAt, lastStatusPromptAt }]
    awaitingRoleReplyFor: null,       // jobbotRoleId of the most-recently sent role offer
    lastRoleBatchAt: null,            // ISO timestamp of last weekly role push
    statusPromptsSentToday: 0,        // throttle: max 3/day per user (ECO-18)
    statusPromptsResetDate: null,     // YYYY-MM-DD when statusPromptsSentToday last reset

    // Course progression (existing — keep for back-compat)
    lessons: {},
    weeklyCheckins: [],
    tasks: {},
    pendingTaskAsk: null,
    awaitingYesFor: null,
    pendingQuestionFor: null,
    lastInteractionAt: null,
    escalations: [],
    qaHistory: [],
    completedAt: null,
  };
}

// Backwards-compatible loader: fills any missing keys with defaults so old
// silver.json files keep working without a one-shot migration script.
export function migrateSilver(state) {
  const defaults = defaultSilverState();
  const out = { ...defaults, ...state };
  // Field rename: thinkificEmail → email (Thinkific is no longer used; only
  // Hotmart). Carry the old value forward if present, then drop the legacy key.
  if (state.thinkificEmail && !out.email) out.email = state.thinkificEmail;
  delete out.thinkificEmail;
  // accessLevel value rename: 'unlocked' → 'full' (per ECO-15 spec).
  if (out.accessLevel === 'unlocked') out.accessLevel = 'full';
  // modulesCompleted: deep-merge so partial old state (only module1)
  // doesn't lose modules 2-4.
  out.modulesCompleted = { ...defaults.modulesCompleted, ...(state.modulesCompleted || {}) };
  // Back-fill from legacy module1CompletedAt timestamp.
  if (out.module1CompletedAt && !out.modulesCompleted[1]) out.modulesCompleted[1] = true;
  // Deep-merge `dossier` so partial old states don't clobber new keys.
  out.dossier = { ...defaults.dossier, ...(state.dossier || {}) };
  if (!Array.isArray(out.appliedRoles)) out.appliedRoles = [];
  if (!Array.isArray(out.escalations)) out.escalations = [];
  if (!Array.isArray(out.qaHistory)) out.qaHistory = [];
  if (!Array.isArray(out.weeklyCheckins)) out.weeklyCheckins = [];
  if (typeof out.lessons !== 'object' || !out.lessons) out.lessons = {};
  if (typeof out.tasks !== 'object' || !out.tasks) out.tasks = {};
  return out;
}

let _catalog = null;
let _tasks = null;

export function loadCatalog(scriptDir) {
  if (_catalog) return _catalog;
  const p = join(scriptDir, 'data', 'silver', 'lessons.json');
  _catalog = JSON.parse(readFileSync(p, 'utf8'));
  return _catalog;
}

export function loadTaskRegistry(scriptDir) {
  if (_tasks) return _tasks;
  const p = join(scriptDir, 'data', 'silver', 'tasks.json');
  if (!existsSync(p)) { _tasks = { tasks: [] }; return _tasks; }
  _tasks = JSON.parse(readFileSync(p, 'utf8'));
  return _tasks;
}

export function taskById(scriptDir, id) {
  return loadTaskRegistry(scriptDir).tasks.find(t => t.id === id);
}

export function tasksDueOnDay(scriptDir, day) {
  return loadTaskRegistry(scriptDir).tasks.filter(t => t.askOnDay === day);
}

export function lessonById(scriptDir, id) {
  return loadCatalog(scriptDir).lessons.find(l => l.id === id);
}

export function lessonByDay(scriptDir, day) {
  return loadCatalog(scriptDir).lessons.find(l => l.day === day);
}

export function templateById(scriptDir, id) {
  return loadCatalog(scriptDir).templates.find(t => t.id === id);
}

export function silverPath(scriptDir, slug) {
  return join(scriptDir, 'mentees', slug, 'silver.json');
}

export function loadSilver(scriptDir, slug) {
  const p = silverPath(scriptDir, slug);
  if (!existsSync(p)) return null;
  try {
    const raw = JSON.parse(readFileSync(p, 'utf8'));
    return migrateSilver(raw);
  } catch { return null; }
}

export function saveSilver(scriptDir, slug, state) {
  const p = silverPath(scriptDir, slug);
  mkdirSync(dirname(p), { recursive: true });
  writeFileSync(p, JSON.stringify(state, null, 2));
}

export function initSilver(scriptDir, slug, opts = {}) {
  const {
    groupJid = null,
    clientPhone = null,
    leadName = null,
    startDate,
    email = null,
    locale,
    coachJid = null,
    coachName = null,
    hotmartTransactionId = null,
    hotmartProductId = null,
    hotmartOfferId = null,
    paymentStatus = null,
  } = opts;

  const startedAt = startDate ? new Date(startDate).toISOString() : nowIso();
  const accessExpiresAt = paymentStatus === 'approved'
    ? new Date(new Date(startedAt).getTime() + NINETY_DAYS_MS).toISOString()
    : null;

  const state = {
    ...defaultSilverState(),
    slug,
    leadName,
    locale: locale || detectLocaleFromPhone(clientPhone) || 'en',
    groupJid,
    clientPhone,
    coachJid,
    coachName,
    email,
    startedAt,
    accessExpiresAt,
    hotmartTransactionId,
    hotmartProductId,
    hotmartOfferId,
    paymentStatus,
  };
  saveSilver(scriptDir, slug, state);
  return state;
}

// Locale detection from phone country code — mirrors detectLangFromPhone in
// lib/report.js, but returns ISO-639-1 codes used by messages.{locale}.json.
export function detectLocaleFromPhone(phone) {
  if (!phone) return 'en';
  const digits = String(phone).replace(/\D/g, '');
  if (digits.startsWith('55') || digits.startsWith('351')) return 'pt';
  if (digits.startsWith('34') || digits.startsWith('52') || digits.startsWith('54') ||
      digits.startsWith('56') || digits.startsWith('57') || digits.startsWith('58') ||
      digits.startsWith('51') || digits.startsWith('591') || digits.startsWith('595') ||
      digits.startsWith('593') || digits.startsWith('598')) return 'es';
  if (digits.startsWith('33')) return 'fr';
  return 'en';
}

export function recordTaskProof(state, { taskId, status, userReply, proofUrls = [], notes = '' }) {
  state.tasks = state.tasks || {};
  const prior = state.tasks[taskId] || {};
  state.tasks[taskId] = {
    ...prior,
    taskId,
    status,
    userReply,
    proofUrls: [...new Set([...(prior.proofUrls || []), ...proofUrls])],
    notes,
    recordedAt: nowIso(),
  };
  if (state.pendingTaskAsk === taskId) state.pendingTaskAsk = null;
  return state.tasks[taskId];
}

export function markTaskAsked(state, taskId) {
  state.tasks = state.tasks || {};
  state.tasks[taskId] = state.tasks[taskId] || { taskId, status: 'asked' };
  state.tasks[taskId].askedAt = nowIso();
  state.pendingTaskAsk = taskId;
}

// Has user replied to this task probe? (any non-asked status counts as recorded)
export function isTaskRecorded(state, taskId) {
  const t = state.tasks?.[taskId];
  return !!t && t.status && t.status !== 'asked';
}

export function resetSilver(scriptDir, slug) {
  const p = join(scriptDir, 'mentees', slug);
  if (existsSync(p)) rmSync(p, { recursive: true, force: true });
}

// ── Lesson selection ────────────────────────────────────────────────────────
// "Oldest incomplete" — used by the daily 08:00 send.
export function nextLessonToSend(scriptDir, state) {
  const cat = loadCatalog(scriptDir);
  for (const l of cat.lessons) {
    const status = state.lessons[l.id];
    if (!status?.completedAt) return l;
  }
  return null;
}

// After a completion, find the next lesson the user should receive immediately
// (catch-up). Returns null if user is up-to-date or beyond today.
export function nextCatchupLesson(scriptDir, state) {
  const cat = loadCatalog(scriptDir);
  // The current day is bounded by elapsed days since startedAt.
  const today = elapsedDayNumber(state);
  for (const l of cat.lessons) {
    if (l.day > today) return null; // user is caught up to today
    const status = state.lessons[l.id];
    if (!status?.completedAt) return l;
  }
  return null;
}

export function elapsedDayNumber(state) {
  const start = new Date(state.startedAt).getTime();
  const ms = now().getTime() - start;
  return Math.max(1, Math.floor(ms / 86_400_000) + 1);
}

export function markLessonSent(state, lessonId) {
  state.lessons[lessonId] = state.lessons[lessonId] || {};
  state.lessons[lessonId].sentAt = state.lessons[lessonId].sentAt || nowIso();
  state.lessons[lessonId].lastSentAt = nowIso();
  state.awaitingYesFor = lessonId;
  return state;
}

export function markLessonNudged(state, lessonId, slot) {
  state.lessons[lessonId] = state.lessons[lessonId] || {};
  state.lessons[lessonId].nudgedAt = state.lessons[lessonId].nudgedAt || [];
  state.lessons[lessonId].nudgedAt.push({ slot, at: nowIso() });
  return state;
}

export function markLessonComplete(state, lessonId, source) {
  state.lessons[lessonId] = state.lessons[lessonId] || {};
  if (state.lessons[lessonId].completedAt) return state;
  state.lessons[lessonId].completedAt = nowIso();
  state.lessons[lessonId].source = source; // 'user-yes' (Thinkific deprecated; only Hotmart)
  if (state.awaitingYesFor === lessonId) state.awaitingYesFor = null;
  state.pendingQuestionFor = lessonId;
  return state;
}

// ── Payment + lifecycle (ECO-5/24) ──────────────────────────────────────────

export function setPaymentStatus(state, status, opts = {}) {
  state.paymentStatus = status;
  if (status === 'approved' && !state.accessExpiresAt) {
    const start = state.startedAt ? new Date(state.startedAt).getTime() : now().getTime();
    state.accessExpiresAt = new Date(start + NINETY_DAYS_MS).toISOString();
  }
  if (opts.hotmartTransactionId) state.hotmartTransactionId = opts.hotmartTransactionId;
  if (opts.hotmartProductId)     state.hotmartProductId     = opts.hotmartProductId;
  if (opts.hotmartOfferId)       state.hotmartOfferId       = opts.hotmartOfferId;
  return state;
}

export function pauseSilver(state, reason) {
  state.pausedAt = nowIso();
  state.pausedReason = reason;
  return state;
}

export function resumeSilver(state) {
  state.pausedAt = null;
  state.pausedReason = null;
  return state;
}

export function isPaused(state) {
  return !!state.pausedAt;
}

// ── Coach gating (ECO-6/15/38) ──────────────────────────────────────────────

export const ACCESS_LEVELS = Object.freeze({
  COURSE_ONLY:      'course_only',
  DOSSIER_PENDING:  'dossier_pending',
  FULL:             'full',
});

export function setAccessLevel(state, level, by = 'system') {
  if (!Object.values(ACCESS_LEVELS).includes(level)) {
    throw new Error(`Unknown accessLevel: ${level}`);
  }
  state.accessLevel = level;
  state.accessLevelChangedAt = nowIso();
  state.accessLevelChangedBy = by;
  return state;
}

// ECO-39: coach-join detection. Called by:
//   - lib/index.js Baileys group-participants.update listener (auto)
//   - lib/coachAdmin.js force-coach-join admin endpoint (manual)
// Flips coachJoinedAt + coachJid + coachName + coachJoinSource so the morning
// scheduler unblocks the daily lesson loop next slot. Idempotent — second call
// is a no-op (returns false).
export function markCoachJoined(state, { coachJid, coachName = null, source = 'manual-admin' }) {
  if (state.coachJoinedAt) return false;
  state.coachJoinedAt = nowIso();
  state.coachJid = coachJid || null;
  state.coachName = coachName;
  state.coachJoinSource = source;
  return true;
}

export function findSilverByGroupJid(scriptDir, groupJid) {
  if (!groupJid) return null;
  const root = join(scriptDir, 'mentees');
  if (!existsSync(root)) return null;
  for (const slug of readdirSync(root)) {
    const sp = join(root, slug, 'silver.json');
    if (!existsSync(sp)) continue;
    try {
      const raw = JSON.parse(readFileSync(sp, 'utf8'));
      if (raw.groupJid === groupJid) return migrateSilver(raw);
    } catch {}
  }
  return null;
}

// Per the refined flow: a module is complete when (a) all lessons in that
// module are confirmed done AND (b) all gating tasks for that module are
// status='claimed'. Tasks are matched by `gatesModule` field if present,
// else fallback to `week === N`.
//
// Returns:
//   { ready: bool, lessonsDone: bool, lessonsRemaining: [], tasksRemaining: [...task objs] }
//
// When `ready` is true and the module isn't yet marked, mutates state:
//   - state.modulesCompleted[N] = true
//   - state.module<N>CompletedAt = nowIso()
//   - For module 1: also flips accessLevel to dossier_pending (existing
//     ECO-15/41 unlock-gate behavior)
//
// Idempotent — re-calling on an already-complete module returns
// { ready: true, lessonsDone: true, lessonsRemaining: [], tasksRemaining: [] }
// without re-mutating.
export function maybeMarkModuleComplete(scriptDir, state, moduleN) {
  const cat = loadCatalog(scriptDir);
  const taskRegistry = loadTaskRegistry(scriptDir);

  const moduleLessons = cat.lessons.filter(l =>
    l.module === moduleN ||
    (l.module == null && lessonDayInModule(l.day, moduleN))
  );
  const lessonsRemaining = moduleLessons.filter(l => !state.lessons?.[l.id]?.completedAt);
  const lessonsDone = lessonsRemaining.length === 0 && moduleLessons.length > 0;

  const moduleTasks = (taskRegistry.tasks || []).filter(t =>
    (typeof t.gatesModule === 'number' ? t.gatesModule === moduleN : t.week === moduleN)
  );
  const tasksRemaining = moduleTasks.filter(t => state.tasks?.[t.id]?.status !== 'claimed');

  const ready = lessonsDone && tasksRemaining.length === 0;

  if (ready && !state.modulesCompleted?.[moduleN]) {
    state.modulesCompleted = { ...(state.modulesCompleted || {}), [moduleN]: true };
    state[`module${moduleN}CompletedAt`] = nowIso();
    if (moduleN === 1) {
      // ECO-15: Module 1 is the coach-unlock gate.
      setAccessLevel(state, ACCESS_LEVELS.DOSSIER_PENDING, 'system:module1_complete');
      state.dossier.status = state.dossier.status === 'not_started' ? 'not_started' : state.dossier.status;
    }
  }
  return { ready, lessonsDone, lessonsRemaining, tasksRemaining };
}

// Default day→module mapping fallback when lesson.module is unset (legacy
// catalogs). Module 1 = days 1-7, Module 2 = 8-14, Module 3 = 15-21,
// Module 4 = 22-30.
function lessonDayInModule(day, moduleN) {
  if (moduleN === 1) return day >= 1 && day <= 7;
  if (moduleN === 2) return day >= 8 && day <= 14;
  if (moduleN === 3) return day >= 15 && day <= 21;
  if (moduleN === 4) return day >= 22 && day <= 30;
  return false;
}

// Backwards-compat wrapper kept for the existing call site in silverCourse.
// Returns true when Module 1 *just* flipped to complete on this call.
export function maybeMarkModule1Complete(scriptDir, state) {
  const wasComplete = !!state.modulesCompleted?.[1];
  const r = maybeMarkModuleComplete(scriptDir, state, 1);
  return r.ready && !wasComplete;
}

// ── Website strategy nudge (ECO-41) ─────────────────────────────────────────
// Cap of 7 daily nudges; >24h gap between sends. Returns true when the
// caller should fire the nudge copy (and bumps the counter); false when
// the cap has been hit or the spacing isn't due yet.

const STRATEGY_NUDGE_CAP = 7;
const STRATEGY_NUDGE_INTERVAL_MS = 22 * 3600_000; // 22h, slight leeway under 24h

export function shouldSendStrategyNudge(state) {
  if (state.accessLevel !== 'dossier_pending') return false;
  if (state.dossier?.status === 'ready_for_review' || state.dossier?.status === 'approved') return false;
  if ((state.strategyNudgesSent || 0) >= STRATEGY_NUDGE_CAP) return false;
  if (!state.strategyLastNudgedAt) return true;
  const last = new Date(state.strategyLastNudgedAt).getTime();
  return (Date.now() - last) >= STRATEGY_NUDGE_INTERVAL_MS;
}

export function recordStrategyNudge(state) {
  state.strategyNudgesSent = (state.strategyNudgesSent || 0) + 1;
  state.strategyLastNudgedAt = nowIso();
}

// Returns true on the first call after career-strategy.json appears on disk
// for this mentee. Used by the polling fallback for ECO-41.
export function detectStrategyOnDisk(scriptDir, state) {
  if (state.strategyDetectedAt) return false;
  const p = join(scriptDir, 'mentees', state.slug, 'career-strategy.json');
  if (!existsSync(p)) return false;
  state.strategyDetectedAt = nowIso();
  return true;
}

// ── Dossier (ECO-15) ────────────────────────────────────────────────────────

export function setDossierStatus(state, status, patch = {}) {
  state.dossier = { ...state.dossier, ...patch, status };
  if (status === 'collecting' && !state.dossier.collectedAt) {
    state.dossier.collectedAt = nowIso();
  }
  if (status === 'ready_for_review' && !state.dossier.generatedAt) {
    state.dossier.generatedAt = nowIso();
  }
  return state;
}

export function appendDossierTurn(state, q, a) {
  state.dossier.collectionTurns = state.dossier.collectionTurns || [];
  state.dossier.collectionTurns.push({ q, a, at: nowIso() });
  return state;
}

// ── One-time WhatsApp token for Job-bot handoff (ECO-15) ────────────────────

export function mintWhatsappToken(state, ttlMin = 30) {
  const value = randomBytes(32).toString('base64url');
  const mintedAt = nowIso();
  const expiresAt = new Date(Date.now() + ttlMin * 60_000).toISOString();
  state.whatsappToken = { value, mintedAt, expiresAt, consumedAt: null };
  return state.whatsappToken;
}

export function consumeWhatsappToken(state, value) {
  const tok = state.whatsappToken;
  if (!tok || tok.value !== value) return { ok: false, reason: 'not_found' };
  if (tok.consumedAt) return { ok: false, reason: 'already_used' };
  if (new Date(tok.expiresAt).getTime() < Date.now()) return { ok: false, reason: 'expired' };
  tok.consumedAt = nowIso();
  return { ok: true };
}

// ── Tracker (ECO-16/17/18) ──────────────────────────────────────────────────

export function recordAppliedRole(state, role) {
  state.appliedRoles = state.appliedRoles || [];
  const existing = state.appliedRoles.find(r => r.jobbotRoleId === role.jobbotRoleId);
  if (existing) {
    Object.assign(existing, role, { appliedAt: existing.appliedAt || nowIso() });
    return existing;
  }
  const entry = {
    jobbotRoleId: role.jobbotRoleId,
    title: role.title || null,
    company: role.company || null,
    location: role.location || null,
    appliedAt: nowIso(),
    status: 'applied',
    statusHistory: [{ status: 'applied', at: nowIso(), note: role.note || null }],
  };
  state.appliedRoles.push(entry);
  return entry;
}

export function updateAppliedRoleStatus(state, jobbotRoleId, status, note = null) {
  const role = (state.appliedRoles || []).find(r => r.jobbotRoleId === jobbotRoleId);
  if (!role) return null;
  role.status = status;
  role.statusHistory = role.statusHistory || [];
  role.statusHistory.push({ status, at: nowIso(), note });
  return role;
}

// Mark that we sent a role offer to the user (ECO-16). Used for dedup so
// the weekly fetch never re-shows a role.
export function recordRoleSent(state, role) {
  state.shownRoles = state.shownRoles || [];
  const existing = state.shownRoles.find(r => r.jobbotRoleId === role.jobbotRoleId);
  if (existing) {
    existing.sentAt = existing.sentAt || nowIso();
    return existing;
  }
  const entry = { jobbotRoleId: role.jobbotRoleId, sentAt: nowIso(), response: null, infoRequestedAt: null };
  state.shownRoles.push(entry);
  state.awaitingRoleReplyFor = role.jobbotRoleId;
  return entry;
}

// Mark user response to a role offer ('applied' | 'skipped'). 'applied'
// also schedules the first status check 7 days out (ECO-17 → ECO-18).
export function markRoleResponse(state, jobbotRoleId, response) {
  state.shownRoles = state.shownRoles || [];
  const entry = state.shownRoles.find(r => r.jobbotRoleId === jobbotRoleId);
  if (entry) {
    entry.response = response;
    entry.respondedAt = nowIso();
  }
  if (state.awaitingRoleReplyFor === jobbotRoleId) state.awaitingRoleReplyFor = null;
  return entry;
}

export function markInfoRequested(state, jobbotRoleId) {
  state.shownRoles = state.shownRoles || [];
  const entry = state.shownRoles.find(r => r.jobbotRoleId === jobbotRoleId);
  if (entry) {
    entry.infoRequestedAt = nowIso();
    entry.infoRequestCount = (entry.infoRequestCount || 0) + 1;
  }
  return entry;
}

export function isRoleAlreadyShown(state, jobbotRoleId) {
  return !!(state.shownRoles || []).find(r => r.jobbotRoleId === jobbotRoleId);
}

// Schedule next status check for an applied role. Default cadence: +7d.
export function scheduleStatusCheck(state, jobbotRoleId, daysOut = 7) {
  const role = (state.appliedRoles || []).find(r => r.jobbotRoleId === jobbotRoleId);
  if (!role) return null;
  role.nextStatusCheckAt = new Date(Date.now() + daysOut * 86_400_000).toISOString();
  return role.nextStatusCheckAt;
}

export function recordStatusPromptSent(state, jobbotRoleId) {
  const role = (state.appliedRoles || []).find(r => r.jobbotRoleId === jobbotRoleId);
  if (role) role.lastStatusPromptAt = nowIso();
  // Throttle counter — reset once per Zurich-day
  const today = new Date().toISOString().slice(0, 10);
  if (state.statusPromptsResetDate !== today) {
    state.statusPromptsResetDate = today;
    state.statusPromptsSentToday = 0;
  }
  state.statusPromptsSentToday = (state.statusPromptsSentToday || 0) + 1;
  return state.statusPromptsSentToday;
}

export function statusPromptsRemainingToday(state, dailyCap = 3) {
  const today = new Date().toISOString().slice(0, 10);
  if (state.statusPromptsResetDate !== today) return dailyCap;
  return Math.max(0, dailyCap - (state.statusPromptsSentToday || 0));
}

// ── Copy resolution (ECO-28) ────────────────────────────────────────────────
// Reads data/silver/messages.{locale}.json and interpolates {var} placeholders.
// Falls back to 'en' when the locale file or key is missing. Vars left
// unresolved are kept in place so they're visible in tests / logs.

let _messages = {};
export function loadMessages(scriptDir, locale) {
  if (_messages[locale]) return _messages[locale];
  const p = join(scriptDir, 'data', 'silver', `messages.${locale}.json`);
  if (!existsSync(p)) {
    _messages[locale] = null;
    return null;
  }
  try {
    _messages[locale] = JSON.parse(readFileSync(p, 'utf8'));
  } catch {
    _messages[locale] = null;
  }
  return _messages[locale];
}

export function resolveMessage(scriptDir, state, key, vars = {}) {
  const locale = (state && state.locale) || 'en';
  const tryLocales = locale === 'en' ? ['en'] : [locale, 'en'];
  let template = null;
  for (const loc of tryLocales) {
    const bundle = loadMessages(scriptDir, loc);
    if (bundle && typeof bundle[key] === 'string' && !bundle[key].startsWith('_todo')) {
      template = bundle[key];
      break;
    }
  }
  if (!template) return `[missing:${key}]`;
  return template.replace(/\{(\w+)\}/g, (_, k) =>
    Object.prototype.hasOwnProperty.call(vars, k) ? String(vars[k]) : `{${k}}`
  );
}

// Test-only: clear the messages cache so tests can hot-reload edits.
export function _resetMessageCache() { _messages = {}; }

// ── Dossier question registry (ECO-15/34) ───────────────────────────────────
// Multi-turn questions asked in the shared WhatsApp group after a coach
// clicks "Trigger Bot" in the admin dashboard. The user's free-text answers
// are captured into state.dossier.collectionTurns[] and, on the last turn,
// written into mentees/{slug}/career-strategy.json by silverCourse.

export const DOSSIER_QUESTIONS = Object.freeze([
  {
    id: 'targetRoles',
    prompt: '*Target roles* — what 2–3 specific role titles are you applying to in the next 30 days? (e.g. "Senior Product Manager (B2B SaaS)", "Growth Marketing Lead")',
  },
  {
    id: 'targetGeos',
    prompt: '*Geography* — which cities/countries/remote setups are you targeting? (e.g. "Zurich on-site", "Remote-EU", "Lisbon hybrid")',
  },
  {
    id: 'salaryFloor',
    prompt: '*Salary floor* — minimum total compensation you would accept? (rough number is fine, including currency)',
  },
  {
    id: 'languages',
    prompt: '*Languages* — which languages can you work in professionally? (e.g. "English fluent, Portuguese native, German B1")',
  },
  {
    id: 'nonNegotiables',
    prompt: '*Non-negotiables* — what would make you turn down a role? (e.g. "no on-call rotation", "must allow 2 days remote", "no relocation")',
  },
]);

export function dossierQuestionByIndex(idx) {
  return DOSSIER_QUESTIONS[idx] || null;
}

export function nextDossierQuestion(state) {
  const turns = state.dossier?.collectionTurns || [];
  const idx = turns.length;
  return idx < DOSSIER_QUESTIONS.length ? { idx, total: DOSSIER_QUESTIONS.length, ...DOSSIER_QUESTIONS[idx] } : null;
}

export function dossierAnswers(state) {
  // Map collectionTurns[] back to a structured object keyed by question id.
  const out = {};
  const turns = state.dossier?.collectionTurns || [];
  turns.forEach((t, i) => {
    const q = DOSSIER_QUESTIONS[i];
    if (q) out[q.id] = t.a;
  });
  return out;
}
