/**
 * config/plans.js
 * Single source of truth for all plan limits.
 * Routes and middleware MUST read from here — never hardcode.
 *
 * Each paid plan has:
 *   - monthly price  (stripe_price_env)         — used for `interval: 'month'`
 *   - annual  price  (stripe_price_annual_env) — used for `interval: 'year'`
 *   - per_seat        (boolean)                — if true, quantity on the
 *                                                Stripe subscription
 *                                                scales the included
 *                                                features (e.g. Agency
 *                                                seats = team members)
 *   - seat_price_cents (int)                   — for per-seat plans, the
 *                                                monthly per-seat amount
 *
 * Annual prices follow the standard "12 months for the price of 10"
 * (≈ 17% savings). Tweak per-plan via stripe_price_annual_env.
 *
 * The free plan has no Stripe price (no subscription).
 */

'use strict';

const PLANS = {
  free: {
    name: 'Free',
    price_cents: 0,
    price_cents_annual: 0,
    interval: 'month',
    stripe_price_env: null,
    stripe_price_annual_env: null,
    per_seat: false,
    cases_limit: 1,
    scans_limit: 10,
    takedowns_limit: 5,
    users_limit: 1,
    api_access: false,
    features: [
      '1 active case',
      '10 scans / month',
      '5 takedown requests / month',
      'Email support',
    ],
  },
  standard: {
    name: 'Standard',
    price_cents: 2900,        //  $29 / month
    price_cents_annual: 29000, // $290 / year  (2 months free)
    interval: 'month',
    stripe_price_env: 'STRIPE_PRICE_STANDARD',
    stripe_price_annual_env: 'STRIPE_PRICE_STANDARD_ANNUAL',
    per_seat: false,
    cases_limit: 5,
    scans_limit: 100,
    takedowns_limit: 50,
    users_limit: 1,
    api_access: false,
    features: [
      '5 active cases',
      '100 scans / month',
      '50 takedown requests / month',
      'Email + chat support',
    ],
  },
  pro: {
    name: 'Pro',
    price_cents: 7900,         //  $79 / month
    price_cents_annual: 79000, // $790 / year  (2 months free)
    interval: 'month',
    stripe_price_env: 'STRIPE_PRICE_PRO',
    stripe_price_annual_env: 'STRIPE_PRICE_PRO_ANNUAL',
    per_seat: false,
    cases_limit: 20,
    scans_limit: 500,
    takedowns_limit: 200,
    users_limit: 3,
    api_access: false,
    features: [
      '20 active cases',
      '500 scans / month',
      '200 takedown requests / month',
      'Priority support',
    ],
  },
  agency: {
    name: 'Agency',
    price_cents: 7900,         //  $79 / seat / month  (was $199 flat)
    price_cents_annual: 79000, // $790 / seat / year
    interval: 'month',
    stripe_price_env: 'STRIPE_PRICE_AGENCY',
    stripe_price_annual_env: 'STRIPE_PRICE_AGENCY_ANNUAL',
    per_seat: true,            // quantity scales team-member seats
    seat_price_cents: 7900,    // matches price_cents
    min_seats: 3,              // agency is team-only — minimum 3
    max_seats: 50,
    cases_limit_per_seat: 25,  // 25 cases per seat
    scans_limit_per_seat: 500, // 500 scans / seat / month
    takedowns_limit_per_seat: 250,
    api_access: true,
    features: [
      'Per-seat team workspace',
      '500 scans / seat / month',
      '250 takedowns / seat / month',
      'Multi-user org',
      'REST API access',
      'Dedicated support',
    ],
  },
  // ---- One-time credit packs --------------------------------------
  // kind: 'credit_pack' — these aren't subscriptions. They grant
  // non-expiring usage credits (extra_scans / extra_takedowns) that
  // are ADDITIVE to whatever monthly plan the user is on. Stripe
  // checkout for these uses mode='payment' (single charge), not
  // mode='subscription'.
  credit_pack_small: {
    kind: 'credit_pack',
    name: 'Credit Pack',
    price_cents: 300,           // $3 one-time
    interval: 'one_time',
    stripe_price_env: 'STRIPE_PRICE_CREDIT_PACK_SMALL',
    stripe_price_annual_env: null,
    per_seat: false,
    grants: { scans: 5, takedowns: 3 },
    features: [
      '5 scan credits',
      '3 takedown credits',
      'Never expires',
      'Works with any plan',
    ],
  },
};

const PLAN_NAMES = Object.keys(PLANS);

function getPlan(planKey, overrideJson) {
  const base = PLANS[planKey] || PLANS.free;
  if (overrideJson) {
    try {
      const override = typeof overrideJson === 'string' ? JSON.parse(overrideJson) : overrideJson;
      return { ...base, ...override };
    } catch (e) {
      // fall through
    }
  }
  return base;
}

// Resolve a plan to its effective limits given the user's seat count.
// Agency is per-seat — its limits scale with the number of seats.
// Free/Standard/Pro ignore seats and return base limits.
function resolveLimits(planKey, seats) {
  const p = PLANS[planKey] || PLANS.free;
  if (!p.per_seat) return { ...p };
  const n = Math.max(1, parseInt(seats, 10) || 1);
  return {
    ...p,
    seats: n,
    cases_limit:    n * p.cases_limit_per_seat,
    scans_limit:    n * p.scans_limit_per_seat,
    takedowns_limit: n * p.takedowns_limit_per_seat,
    users_limit:     n,   // 1 user per seat
  };
}

function listPlans() {
  return Object.entries(PLANS).map(([key, p]) => ({
    key,
    kind: p.kind || 'subscription',
    name: p.name,
    price_cents: p.price_cents,
    price_cents_annual: p.price_cents_annual != null ? p.price_cents_annual : null,
    interval: p.interval,
    per_seat: p.per_seat || false,
    seat_price_cents: p.seat_price_cents != null ? p.seat_price_cents : null,
    min_seats: p.min_seats || 1,
    max_seats: p.max_seats || 1,
    cases_limit: p.cases_limit,
    scans_limit: p.scans_limit,
    takedowns_limit: p.takedowns_limit,
    cases_limit_per_seat:    p.cases_limit_per_seat    != null ? p.cases_limit_per_seat    : null,
    scans_limit_per_seat:    p.scans_limit_per_seat    != null ? p.scans_limit_per_seat    : null,
    takedowns_limit_per_seat: p.takedowns_limit_per_seat != null ? p.takedowns_limit_per_seat : null,
    grants: p.grants || null,
    users_limit: p.users_limit,
    api_access: p.api_access,
  }));
}

// Resolve a Stripe price id for {plan, interval}. The free plan
// has no Stripe price; per-seat plans still return a single price id
// because the seat count is set via `quantity` on the subscription
// item, not via separate Stripe prices.
//
// Rejects bogus intervals (anything other than 'month' | 'year') by
// returning null — callers should treat null as INVALID_PLAN_OR_INTERVAL.
// Credit packs don't have an interval (one-time purchase); they
// always use stripe_price_env regardless of the interval arg.
function getStripePriceId(planKey, interval = 'month') {
  const p = PLANS[planKey];
  if (!p) return null;
  if (p.kind === 'credit_pack') {
    return process.env[p.stripe_price_env] || null;
  }
  if (interval !== 'month' && interval !== 'year') return null;
  const envKey = interval === 'year' ? p.stripe_price_annual_env : p.stripe_price_env;
  if (!envKey) return null;
  return process.env[envKey] || null;
}

// Validate a quantity for a plan's per-seat model. Returns a
// sanitized int (clamped to [min, max]) or null if invalid for
// per-seat plans.
function validateQuantity(planKey, requested) {
  const p = PLANS[planKey];
  if (!p) return null;
  if (!p.per_seat) return 1;  // non-per-seat plans are always qty=1
  const n = parseInt(requested, 10);
  if (!Number.isFinite(n) || n < 1) return null;
  const min = p.min_seats || 1;
  const max = p.max_seats || 100;
  if (n < min) return null;     // refuse, don't silently clamp
  if (n > max) return max;     // cap at max
  return n;
}

module.exports = { PLANS, PLAN_NAMES, getPlan, listPlans, getStripePriceId, resolveLimits, validateQuantity };
