/**
 * Tier access matrix — single source of truth for what each tier unlocks.
 *
 * The tier set DOES NOT YET match the DB CHECK constraint. Sessions
 * 001 + 011 wrote `tier IN ('free', 'analyst', 'desk')`. Session 12
 * adds 'africa' here, but the constraint must be extended in a
 * follow-up migration BEFORE the Stripe webhook can write 'africa'
 * to users.tier — otherwise inserts will 23514 (check_violation).
 *
 * Roadmap:
 *   1. Manual SQL to extend the constraint (drop + re-add covering all
 *      four tiers) — out of scope for this session per no-migration rule.
 *   2. Stripe webhook event for the africa price ID maps to tier='africa'.
 *   3. The frontend Pricing component shows the tier (already done here).
 *
 * `api_access` is FALSE on every tier and must stay false. VYNDR is a
 * consumer product; the proprietary engine is never exposed externally.
 *
 * `scans_per_day` is the per-tier daily limit. The scan-limit middleware
 * reads this and 429s on overflow. Anonymous callers fall back to the
 * 'free' bucket, IP-keyed.
 */

const TIERS = Object.freeze({
  free: Object.freeze({
    scans_per_day: 3,
    grade_visible: true,
    reasoning_visible: false,      // blurred — frontend renders tier-locked
    kill_conditions_detail: false, // count only, not codes/reasons
    kill_conditions_count: true,
    alerts: false,
    portfolio: false,
    engine2: false,
    stat_dashboard: true,
    api_access: false,
    // Session 62 (A1-S1) — Desk-only intelligence (pricing-page promises).
    alt_line_ladder: false,
    kelly_sizing: false,
  }),
  // Session 12 — $4.99/mo Africa tier. Slotted between free and analyst.
  // Same intelligence surface as analyst (reasoning + kill conditions
  // visible) but lower scan budget; no alerts. Activation blocked on a
  // DB CHECK constraint update — see header comment.
  africa: Object.freeze({
    scans_per_day: 10,
    grade_visible: true,
    reasoning_visible: true,
    kill_conditions_detail: true,
    kill_conditions_count: true,
    alerts: false,
    portfolio: false,
    engine2: false,
    stat_dashboard: true,
    api_access: false,
    // Session 62 (A1-S1) — Desk-only intelligence (pricing-page promises).
    alt_line_ladder: false,
    kelly_sizing: false,
  }),
  analyst: Object.freeze({
    // Session 62 (A1-S1) — the Founder Access card promises "Unlimited
    // reads" and the Nav shows an ∞ badge for every paid tier; the 15/day
    // cap made both claims false. Analyst is unlimited; Desk differentiates
    // on capabilities (alt ladder, Kelly, feed), not on rationing reads.
    scans_per_day: Infinity,
    grade_visible: true,
    reasoning_visible: true,
    kill_conditions_detail: true,
    kill_conditions_count: true,
    alerts: true,
    portfolio: false,
    engine2: false,
    stat_dashboard: true,
    api_access: false,
    // Session 62 (A1-S1) — Desk-only intelligence (pricing-page promises).
    alt_line_ladder: false,
    kelly_sizing: false,
      // Session 64 — per-read directional-CLV badge. Analyst+Desk only;
    // Free never RECEIVES the data (gated server-side, not hidden client-side).
    clv_badge: true,
    // Session 66 — VYNDR's OWN price (model_odds) on the price triplet.
    // Analyst+Desk. Free keeps BOOK and FAIR: the de-vigged honest number is
    // the hook and is NEVER the paywall — only our own price gates.
    model_price: true,
}),
  desk: Object.freeze({
    scans_per_day: Infinity,
    grade_visible: true,
    reasoning_visible: true,
    kill_conditions_detail: true,
    kill_conditions_count: true,
    alerts: true,
    portfolio: true,
    engine2: true,                 // LLM deep analysis
    stat_dashboard: true,
    api_access: false,
    alt_line_ladder: true,
    kelly_sizing: true,
      // Session 64 — per-read directional-CLV badge. Analyst+Desk only;
    // Free never RECEIVES the data (gated server-side, not hidden client-side).
    clv_badge: true,
    // Session 66 — VYNDR's OWN price (model_odds) on the price triplet.
    // Analyst+Desk. Free keeps BOOK and FAIR: the de-vigged honest number is
    // the hook and is NEVER the paywall — only our own price gates.
    model_price: true,
}),
});

const VALID_TIERS = Object.freeze(Object.keys(TIERS));

function getTier(tierName) {
  const key = String(tierName || 'free').toLowerCase();
  return TIERS[key] || TIERS.free;
}

function getScanLimit(tierName) {
  return getTier(tierName).scans_per_day;
}

function canAccess(tierName, feature) {
  const t = getTier(tierName);
  if (!(feature in t)) return false;
  return !!t[feature];
}

module.exports = {
  TIERS,
  VALID_TIERS,
  getTier,
  getScanLimit,
  canAccess,
};
