'use strict';

/**
 * Canonical DashCaddy Pro product catalog.
 *
 * Keep product identity, license duration, USD amount, and the Stripe price
 * environment variable in one place. Checkout (src/billing/stripe-client.js),
 * the webhook bridge (scripts/stripe-license-bridge.js), the public pricing
 * page (status/pricing/index.html), and tests must NOT maintain separate
 * product lists — all of them import from here.
 *
 * Pricing source of truth: PRODUCT-SPEC-DECISIONS.md (locked 2026-07-20).
 *
 * Lifecycle:
 *   - To add a new tier: append a new frozen product entry below, then add
 *     the matching STRIPE_PRICE_PRO_*<duration>D environment variable to
 *     the deployment. The pricing page (status/pricing/index.html) and
 *     the catalog consistency test (__tests__/billing/pricing-page-catalog.test.js)
 *     will both fail until the pricing page is updated in lockstep — this
 *     is the documented drift guard.
 *   - To change a price: edit the matching product entry AND the pricing
 *     page's hardcoded price label (status/pricing/index.html). The
 *     pricing-page-catalog.test.js enforces the two match.
 *
 * Note: The pricing page hard-codes the 4 product IDs, prices, and
 * duration strings (rather than being server-rendered from this catalog).
 * The hard-coding is intentional — the page is served as static HTML from
 * `status.sami/pricing` and never touches the live API. The
 * pricing-page-catalog.test.js enforces consistency between the two
 * sources, so any drift fails the test suite.
 */

const PRODUCTS = Object.freeze([
  Object.freeze({
    id: 'pro-30d',
    durationDays: 30,
    amountCents: 2000,
    priceEnv: 'STRIPE_PRICE_PRO_30D',
    label: '1 month',
    priceLabel: '$20',
  }),
  Object.freeze({
    id: 'pro-90d',
    durationDays: 90,
    amountCents: 5000,
    priceEnv: 'STRIPE_PRICE_PRO_90D',
    label: '3 months',
    priceLabel: '$50',
  }),
  Object.freeze({
    id: 'pro-180d',
    durationDays: 180,
    amountCents: 7000,
    priceEnv: 'STRIPE_PRICE_PRO_180D',
    label: '6 months',
    priceLabel: '$70',
  }),
  Object.freeze({
    id: 'pro-365d',
    durationDays: 365,
    amountCents: 9900,
    priceEnv: 'STRIPE_PRICE_PRO_365D',
    label: '12 months',
    priceLabel: '$99',
  }),
]);

const BY_ID = new Map(PRODUCTS.map((product) => [product.id, product]));

function listProducts() {
  return PRODUCTS.slice();
}

function getProduct(productId) {
  return BY_ID.get(productId) || null;
}

/**
 * Resolve the Stripe Price ID configured for a product. Returns '' if unset
 * (caller treats empty string as "this tier is not configured").
 */
function getConfiguredPrice(product, env = process.env) {
  if (!product) return '';
  return env[product.priceEnv] || '';
}

/**
 * Map a Stripe Price ID back to a product. Used by the webhook bridge to
 * validate that a Checkout session's price matches a configured product
 * (defense against Stripe price-ID drift / repointing).
 *
 * Returns null when the price ID is unset or doesn't match any configured
 * product.
 */
function findProductByPriceId(priceId, env = process.env) {
  if (!priceId || typeof priceId !== 'string') return null;
  for (const product of PRODUCTS) {
    const configured = getConfiguredPrice(product, env);
    if (configured && configured === priceId) return product;
  }
  return null;
}

/**
 * Same as getConfiguredPrice but returns the full product list with the
 * resolved Stripe Price ID merged in. Useful for the pricing page renderer.
 */
function getConfiguredProducts(env = process.env) {
  return PRODUCTS.map((product) => ({ ...product, priceId: getConfiguredPrice(product, env) }));
}

module.exports = {
  PRODUCTS,
  listProducts,
  getProduct,
  getConfiguredPrice,
  getConfiguredProducts,
  findProductByPriceId,
};
