/**
 * Package Pricing Helpers
 *
 * Packages carry explicit prices in all three currencies (INR / USD / AED) via
 * `pkg.prices`, alongside the legacy flat fields (`monthlyPrice`, `yearlyPrice`,
 * …) which remain the base-currency prices.
 *
 * Resolution order for any (cycle, currency) pair:
 *   1. `pkg.prices[currency][cycle]` when the admin has set it
 *   2. the flat field, when the requested currency IS the base currency
 *   3. null — caller falls back to exchange-rate conversion, exactly as before
 *
 * Step 3 is what keeps packages created before this change working unchanged.
 */

export type PriceCurrency = 'USD' | 'INR' | 'AED';
export type PriceCycle = 'monthly' | 'quarterly' | 'half_yearly' | 'yearly' | 'lifetime';

export const PRICE_CURRENCIES: PriceCurrency[] = ['USD', 'INR', 'AED'];

/** Billing cycle → key inside a currency price set, and the legacy flat field. */
const CYCLE_FIELDS: Record<PriceCycle, { setKey: string; flatKey: string }> = {
  monthly: { setKey: 'monthly', flatKey: 'monthlyPrice' },
  quarterly: { setKey: 'quarterly', flatKey: 'quarterlyPrice' },
  half_yearly: { setKey: 'halfYearly', flatKey: 'halfYearlyPrice' },
  yearly: { setKey: 'yearly', flatKey: 'yearlyPrice' },
  lifetime: { setKey: 'lifetime', flatKey: 'lifetimePrice' },
};

/** Mongoose subdocuments need toObject() before plain property access is safe. */
function plain(value: any): any {
  if (!value) return null;
  return typeof value.toObject === 'function' ? value.toObject() : value;
}

/**
 * The price a package charges for a cycle in a specific currency.
 * Returns null when no explicit price exists — the caller should then convert
 * from the base currency as it did before.
 */
export function getPackagePriceIn(
  pkg: any,
  cycle: PriceCycle,
  currency: string
): number | null {
  const fields = CYCLE_FIELDS[cycle];
  if (!fields) return null;

  const code = String(currency || '').toUpperCase() as PriceCurrency;

  const prices = plain(pkg?.prices);
  const set = plain(prices?.[code]);
  const explicit = set?.[fields.setKey];
  if (typeof explicit === 'number' && explicit > 0) {
    return explicit;
  }

  // Requested currency is the package's own — the flat field is authoritative.
  if (code === String(pkg?.currency || 'USD').toUpperCase()) {
    const flat = pkg?.[fields.flatKey];
    if (typeof flat === 'number') return flat;
  }

  return null;
}

/**
 * Mirror the flat base-currency fields into `prices[currency]` (and back), so a
 * package saved through any path ends up internally consistent.
 *
 * Called on create/update before saving. Mutates and returns `body`.
 */
export function syncPackagePrices(body: Record<string, any>, existing?: any): Record<string, any> {
  const baseCurrency = String(
    body.currency ?? existing?.currency ?? 'USD'
  ).toUpperCase() as PriceCurrency;

  const incoming = plain(body.prices) || {};
  const current = plain(existing?.prices) || {};

  const merged: Record<string, any> = {};
  for (const code of PRICE_CURRENCIES) {
    const incomingSet = plain(incoming[code]) || {};
    const currentSet = plain(current[code]) || {};
    merged[code] = {};
    for (const { setKey } of Object.values(CYCLE_FIELDS)) {
      const value = incomingSet[setKey] ?? currentSet[setKey] ?? 0;
      merged[code][setKey] = Number(value) || 0;
    }
  }

  // The base currency's set and the flat fields describe the same thing.
  // Whichever the request supplied wins; the other is brought into line.
  const baseSet = merged[baseCurrency];
  const suppliedPricesForBase = plain(incoming[baseCurrency]);

  for (const [cycle, { setKey, flatKey }] of Object.entries(CYCLE_FIELDS)) {
    const flatSupplied = body[flatKey];
    const setSupplied = suppliedPricesForBase?.[setKey];

    if (typeof setSupplied === 'number' && setSupplied > 0) {
      body[flatKey] = setSupplied;
    } else if (typeof flatSupplied === 'number') {
      baseSet[setKey] = flatSupplied;
    } else if (existing && typeof existing[flatKey] === 'number' && !baseSet[setKey]) {
      // Untouched by this request — keep the stored base price.
      baseSet[setKey] = existing[flatKey];
    }
    void cycle;
  }

  body.prices = merged;
  return body;
}

/**
 * Build the default `prices` block for a package that has none, from its flat
 * base-currency fields. Used when reading legacy documents.
 */
export function pricesFromFlatFields(pkg: any): Record<string, any> {
  const baseCurrency = String(pkg?.currency || 'USD').toUpperCase();
  const result: Record<string, any> = {};
  for (const code of PRICE_CURRENCIES) {
    result[code] = {};
    for (const { setKey, flatKey } of Object.values(CYCLE_FIELDS)) {
      result[code][setKey] = code === baseCurrency ? (Number(pkg?.[flatKey]) || 0) : 0;
    }
  }
  return result;
}
