/**
 * Pure checkout-parameter builder for Stripe Checkout sessions.
 *
 * This module contains NO Stripe API calls and NO I/O: given a user, a
 * client-supplied plan identifier and the server environment, it returns the
 * exact parameters for `stripe.checkout.sessions.create`. That makes the price
 * decision fully testable and keeps the API route a thin shell.
 *
 * Pricing security rule: the client only ever sends a plan identifier. All
 * amounts, trials, intervals and modes come from the shared catalog in
 * ./pricing.ts — never from the request body. An env Price ID (when set) wins;
 * otherwise `price_data.unit_amount` is taken verbatim from the catalog.
 */

import type Stripe from "stripe";
import { getPlanConfig, type PlanId } from "./pricing";

/** Minimal shape of the authenticated user the checkout runs for. */
export interface CheckoutUser {
  id: string;
}

/** Server environment lookup, injectable so tests need no real env vars. */
export type CheckoutEnv = Record<string, string | undefined>;

export interface CheckoutParams {
  lineItems: Stripe.Checkout.SessionCreateParams.LineItem[];
  mode: Stripe.Checkout.SessionCreateParams.Mode;
  subscriptionData: Stripe.Checkout.SessionCreateParams.SubscriptionData | undefined;
  metadata: { plan: PlanId; userId: string };
  clientReferenceId: string;
}

/**
 * Display strings for `price_data.product_data`. These are labels only — the
 * price itself (`unit_amount`) always comes from the catalog's
 * `unitAmountMinor`, so no magic number lives in this file or the route.
 */
const PRODUCT_INFO: Record<PlanId, { name: string; description: string }> = {
  weekly: {
    name: "Receipt Scanner Pro - Wochen-Pass (3 Tage Trial)",
    description: "Unbegrenzte Belege, Dual-Sheet Excel & CSV",
  },
  annual: {
    name: "Receipt Scanner Pro - Jahres-Pass",
    description:
      "Volle 12 Monate unbegrenzte Belege, Dual-Sheet Excel & Prioritäts-Support",
  },
  lifetime: {
    name: "Receipt Scanner Pro - Lifetime Lizenz",
    description:
      "Lebenslanger unbegrenzter Zugriff ohne Folgekosten inklusive aller Updates",
  },
};

/**
 * Builds the full Stripe Checkout session params from server-side facts only.
 *
 * - Unknown or missing plan → `resolvePlan` fallback (annual), so a bogus plan
 *   can never silently change what the customer is charged.
 * - Env Price ID (`config.priceIdEnv`) wins when set; otherwise `price_data`
 *   with `unit_amount` exactly `PLAN_CONFIGS[plan].unitAmountMinor`.
 * - Trial days, billing interval and mode all come from the catalog; only the
 *   weekly pass carries a trial.
 * - Metadata always carries the resolved plan and the authenticated user id.
 */
export function buildCheckoutParams(
  user: CheckoutUser,
  plan: string | null | undefined,
  env: CheckoutEnv
): CheckoutParams {
  const config = getPlanConfig(plan);
  const priceId = env[config.priceIdEnv];

  const lineItems: Stripe.Checkout.SessionCreateParams.LineItem[] = priceId
    ? [{ price: priceId, quantity: 1 }]
    : [
        {
          price_data: {
            currency: "eur",
            product_data: PRODUCT_INFO[config.id],
            unit_amount: config.unitAmountMinor,
            ...(config.interval ? { recurring: { interval: config.interval } } : {}),
          },
          quantity: 1,
        },
      ];

  const subscriptionData: Stripe.Checkout.SessionCreateParams.SubscriptionData | undefined =
    config.mode === "subscription"
      ? {
          ...(config.trialPeriodDays
            ? { trial_period_days: config.trialPeriodDays }
            : {}),
          metadata: { plan: config.id, userId: user.id },
        }
      : undefined;

  return {
    lineItems,
    mode: config.mode,
    subscriptionData,
    metadata: { plan: config.id, userId: user.id },
    clientReferenceId: user.id,
  };
}
