function text(value) {
  return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
}

function numberFrom(value) {
  if (typeof value === 'number') return Number.isFinite(value) ? value : null;
  const raw = text(String(value ?? '')).replace(/[^\d,.-]/g, '');
  if (!raw) return null;
  const normalized = raw.includes(',')
    ? raw.replace(/\./g, '').replace(',', '.')
    : raw;
  const parsed = Number(normalized);
  return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
}

function gramsFrom(value) {
  const candidates = [...text(value).matchAll(/\b(\d+(?:[.,]\d+)?)\s*(kg|g)\b/gi)]
    .map((match) => Number(match[1].replace(',', '.')) * (match[2].toLowerCase() === 'kg' ? 1000 : 1))
    .filter((grams) => grams >= 100 && grams <= 10000);
  return candidates.length ? Math.max(...candidates) : null;
}

function allJsonLd(documentRef) {
  return [...documentRef.querySelectorAll('script[type="application/ld+json"]')].flatMap((node) => {
    try {
      const parsed = JSON.parse(node.textContent || '');
      return Array.isArray(parsed) ? parsed : [parsed];
    } catch {
      return [];
    }
  });
}

function productNodes(value, output = []) {
  if (!value || typeof value !== 'object') return output;
  const type = value['@type'];
  if (type === 'Product' || type === 'ProductGroup' || (Array.isArray(type) && type.includes('Product'))) {
    output.push(value);
  }
  for (const child of Object.values(value)) {
    if (child && typeof child === 'object') productNodes(child, output);
  }
  return output;
}

function firstOffer(product) {
  const offers = [
    product?.offers,
    ...(Array.isArray(product?.hasVariant) ? product.hasVariant.map((variant) => variant.offers) : []),
  ].flat(2).filter(Boolean);
  return offers.find((offer) => /InStock$/i.test(offer?.availability ?? '')) ?? offers[0] ?? null;
}

function meta(documentRef, key) {
  return text(documentRef.querySelector(`meta[property="${key}"], meta[name="${key}"]`)?.content);
}

function gtinFrom(product) {
  for (const value of [product?.gtin14, product?.gtin13, product?.gtin12, product?.gtin8, product?.gtin]) {
    const digits = String(value ?? '').replace(/\D/g, '');
    if ([8, 12, 13, 14].includes(digits.length)) return digits;
  }
  return null;
}

function selectedText(documentRef, selectors) {
  for (const selector of selectors) {
    const value = text(documentRef.querySelector(selector)?.textContent);
    if (value) return value;
  }
  return '';
}

function moneyNear(value, labels = '(?:pix|boleto|à vista|a vista)') {
  const source = text(value);
  const amount = '(R\\$\\s*[\\d.]+,\\d{2})';
  const before = new RegExp(`${amount}.{0,80}${labels}`, 'i').exec(source);
  const after = new RegExp(`${labels}.{0,80}${amount}`, 'i').exec(source);
  return numberFrom(before?.[1] ?? after?.[1]);
}

function parseWheyProduct(documentRef) {
  const product = allJsonLd(documentRef).flatMap((entry) => productNodes(entry))[0] ?? null;
  const offer = firstOffer(product);
  const name = text(product?.name)
    || meta(documentRef, 'og:title')
    || selectedText(documentRef, ['h1', '[data-testid*="product-name"]', '.productName']);
  const cashPriceText = selectedText(documentRef, [
    '.pix-titulo-valor',
    '.boleto-titulo-valor',
    '[class*="preco-pix"]',
    '[class*="price-pix"]',
    '[class*="pix"][class*="valor"]',
    '[class*="pix"][class*="price"]',
    '[class*="boleto"][class*="valor"]',
  ]);
  const genericPriceText = selectedText(documentRef, [
    '[data-testid*="selling-price"]',
    '[class*="sellingPrice"]',
    '[class*="bestPrice"]',
    '[class*="sale-price"]',
    '[class*="sales-price"]',
    '[class*="current-price"]',
    '[itemprop="price"]',
  ]);
  const bodyText = `${name} ${text(product?.description)} ${text(documentRef.body?.innerText).slice(0, 20000)}`;
  const isWhey = /\bwhey\b|prote[ií]na\s+(?:do|de)\s+soro/i.test(`${name} ${text(product?.description)}`);
  if (!isWhey) return null;
  const cashPrice = numberFrom(cashPriceText) ?? moneyNear(bodyText);
  const structuredPrice = numberFrom(offer?.price ?? offer?.lowPrice ?? meta(documentRef, 'product:price:amount'));
  const genericPrice = numberFrom(genericPriceText);
  const price = cashPrice ?? structuredPrice ?? genericPrice;
  const packageWeightGrams = gramsFrom(name) ?? gramsFrom(bodyText);
  const imageValue = Array.isArray(product?.image) ? product.image[0] : product?.image;
  const imageUrl = text(typeof imageValue === 'object' ? imageValue?.url ?? imageValue?.contentUrl : imageValue)
    || meta(documentRef, 'og:image');
  const brand = text(product?.brand?.name ?? product?.brand) || null;
  const productType = /hidrolisad/i.test(name) ? 'Hidrolisado'
    : /isolad/i.test(name) ? 'Isolado'
      : /blend|3w/i.test(name) ? 'Blend'
        : /concentrad/i.test(name) ? 'Concentrado'
          : null;

  if (!name || price == null || packageWeightGrams == null) {
    return {
      complete: false,
      name: name || null,
      price,
      packageWeightGrams,
      imageUrl: imageUrl || null,
      brand,
      gtin: gtinFrom(product),
      productType,
      pixPrice: cashPrice,
      cardPrice: structuredPrice && structuredPrice !== cashPrice ? structuredPrice : null,
      currency: text(offer?.priceCurrency ?? meta(documentRef, 'product:price:currency')) || 'BRL',
    };
  }

  return {
    complete: true,
    name,
    price,
    packageWeightGrams,
    pricePer100g: price / (packageWeightGrams / 100),
    imageUrl: imageUrl || null,
    brand,
    gtin: gtinFrom(product),
    productType,
    pixPrice: cashPrice,
    cardPrice: structuredPrice && structuredPrice !== cashPrice ? structuredPrice : null,
    currency: text(offer?.priceCurrency ?? meta(documentRef, 'product:price:currency')) || 'BRL',
  };
}

globalThis.CustoWheyParser = { parseWheyProduct, numberFrom, gramsFrom, moneyNear };
