export type AsukaPriceInput = number | string;

/**
 * Normalize price-like integration values to real numbers.
 *
 * Integrators often receive prices from CMS/API payloads as strings. This helper
 * accepts plain numeric strings, French decimal commas and simple currency
 * formatting (for example "2.5", "2,50", "1 234,56", "€12.99"). Percent
 * strings are intentionally rejected so pricing-rule percentages keep their
 * dedicated behavior.
 */
export function normalizePriceInput(value: unknown): number | undefined {
  if (typeof value === 'number') {
    return Number.isFinite(value) ? value : undefined;
  }

  if (typeof value !== 'string') return undefined;

  let raw = value.trim();
  if (!raw || raw.includes('%')) return undefined;

  // Keep only common price formatting around the numeric payload.
  raw = raw
    .replace(/[\u00a0\s]/g, '')
    .replace(/[\u20ac$\u00a3\u00a5]/g, '')
    .trim();

  if (!raw) return undefined;

  const hasComma = raw.includes(',');
  const hasDot = raw.includes('.');

  if (hasComma && hasDot) {
    // The last separator is the decimal separator; the other one is thousands.
    const lastComma = raw.lastIndexOf(',');
    const lastDot = raw.lastIndexOf('.');
    if (lastComma > lastDot) {
      raw = raw.replace(/\./g, '').replace(',', '.');
    } else {
      raw = raw.replace(/,/g, '');
    }
  } else if (hasComma) {
    raw = raw.replace(',', '.');
  }

  if (!/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(raw)) return undefined;

  const normalized = Number(raw);
  return Number.isFinite(normalized) ? normalized : undefined;
}

export function normalizePriceInputOrZero(value: unknown): number {
  return normalizePriceInput(value) ?? 0;
}

export interface AsukaElementPriceBreakdown {
  base: number;
  variation: number;
  total: number;
}

const findVariationPathById = (variations: any[], variationId: string, trail: any[] = []): any[] | null => {
  const wanted = String(variationId ?? '').trim();
  if (!wanted) return null;
  for (const variation of Array.isArray(variations) ? variations : []) {
    if (!variation || typeof variation !== 'object') continue;
    const nextTrail = [...trail, variation];
    if (String(variation?.id ?? '').trim() === wanted) return nextTrail;
    const found = findVariationPathById(variation?.variations, wanted, nextTrail);
    if (found) return found;
  }
  return null;
};

export function sumPriceInputs(items: unknown[]): number {
  return (Array.isArray(items) ? items : []).reduce<number>((total, item) => total + (normalizePriceInput(item) ?? 0), 0);
}

/** Resolve an element base price plus every priced node in the selected variation path. */
export function getElementPriceBreakdown(
  element: any,
  variation?: any | null,
  variationPath?: any[] | null,
): AsukaElementPriceBreakdown {
  const base = normalizePriceInput(element?.price) ?? 0;
  const explicitPath = Array.isArray(variationPath) ? variationPath.filter(Boolean) : [];
  const resolvedPath = explicitPath.length
    ? explicitPath
    : (findVariationPathById(element?.variations, String(variation?.id ?? '')) ?? (variation ? [variation] : []));

  const seen = new Set<string>();
  let anonymousIndex = 0;
  const variationTotal = resolvedPath.reduce((total, node) => {
    const id = String(node?.id ?? '').trim();
    const key = id ? `id:${id}` : `anonymous:${anonymousIndex++}`;
    if (seen.has(key)) return total;
    seen.add(key);
    return total + (normalizePriceInput(node?.price) ?? 0);
  }, 0);

  return { base, variation: variationTotal, total: base + variationTotal };
}
