// Wallet storefront logic, kept pure and out of the .svelte screen so it unit-tests in the node
// environment: money/price formatting, the spendable-segment selection, and the web-spend warning
// trigger. The server owns the real store-compliance gate; these helpers drive display only.

import type { Wallet, WalletSegment } from './model';
import { insideVK } from './vk';
import { insideTelegram } from './telegram';

/** The client's execution context for the wallet UI, mirroring the server's platform kind. */
export type SpendContext = 'vk' | 'telegram' | 'direct';

/**
 * executionContext reports the client's best-effort context: a VK Mini App, a Telegram Mini App,
 * else a web/native (direct) session. It only drives the display-only web-spend warning; the
 * server enforces the real gate on every spend (fail-closed), never trusting this.
 */
export function executionContext(): SpendContext {
  if (insideVK()) return 'vk';
  if (insideTelegram()) return 'telegram';
  return 'direct';
}

/**
 * majorAmount converts a money amount in a currency's minor units to its major unit — the rouble
 * has 100 kopecks; Votes and Stars are whole units (scale 1).
 */
export function majorAmount(minor: number, currency: string): number {
  return minor / (currency === 'RUB' ? 100 : 1);
}

/**
 * formatAmount renders a money price in major units: two fractional digits for the rouble, a whole
 * number for the whole-unit currencies (Votes / Stars). The currency label is added by the caller
 * (it is localized), so this stays a pure number formatter.
 */
export function formatAmount(minor: number, currency: string): string {
  return currency === 'RUB' ? (minor / 100).toFixed(2) : String(minor);
}

/**
 * spendableChips totals the chips the player can actually spend in the current context — the sum of
 * the segments the server marked spendable (zero for a VK-iOS-frozen or untrusted wallet, where no
 * segment is spendable).
 */
export function spendableChips(w: Wallet): number {
  return w.segments.reduce((sum, s) => sum + (s.spendable ? s.chips : 0), 0);
}

// drawPriority is the segment draw order on the web, mirroring the backend spend: the home direct
// segment first, then the store-funded segments.
const drawPriority: readonly string[] = ['direct', 'vk', 'telegram'];

/**
 * directPurchaseNeedsEmail reports whether the money storefront must ask for an email before it can
 * sell anything. The direct rail funds only an account with a confirmed email — the recovery anchor,
 * without which a paying customer who loses the account loses the chips with it — and the server
 * refuses the order without one. Inside VK or Telegram the purchase settles on that store's own rail,
 * which carries its own identity, so nothing is asked for there and spending already-earned chips is
 * never affected.
 */
export function directPurchaseNeedsEmail(context: SpendContext, email: string): boolean {
  return context === 'direct' && email === '';
}

/**
 * needsWebSpendWarning reports whether buying a chip-priced value in the current context would draw
 * store-funded (vk / telegram) chips — the case the UI must warn about, because the benefit it buys
 * (origin=direct) is then usable on the web/native only, by the store-compliance rule. It fires only
 * in a direct context and only when the priority draw (direct→vk→tg) actually reaches a store
 * segment to cover the price; a spend that the direct segment covers alone, or a spend in a VK/
 * Telegram context (which stays within the same store segment), never warns.
 */
export function needsWebSpendWarning(
  context: SpendContext,
  segments: WalletSegment[],
  chipPrice: number,
): boolean {
  if (context !== 'direct') return false;
  let remaining = chipPrice;
  let reachesStore = false;
  for (const src of drawPriority) {
    if (remaining <= 0) break;
    const seg = segments.find((s) => s.source === src && s.spendable);
    if (!seg || seg.chips <= 0) continue;
    const take = Math.min(seg.chips, remaining);
    if (src !== 'direct' && take > 0) reachesStore = true;
    remaining -= take;
  }
  return remaining <= 0 && reachesStore;
}
