/**
 * Loja Shopify: `/products.json` (a lista, de 50 em 50) e
 * `/products/<handle>.json` (um produto). Vem tudo: variantes com preço,
 * preço riscado, código, peso e a foto de cada variante.
 */

import { absoluto, dentroDoTeto, linha, menorPreco, precoDoDado, produtoVazio, semRepetir, TETOS, type ProdutoLido, type VarianteLida } from "../modelo.ts";
import { lista, objeto, primeiroTexto, texto } from "./dado.ts";

/* A variante única que toda loja Shopify tem, quando o produto não varia. */
const SEM_VARIACAO = /^default title$/i;

function produto(bruto: unknown, origem: string): ProdutoLido | null {
  const dado = objeto(bruto);
  if (!dado) return null;
  const nome = linha(dado.title, 300);
  const handle = texto(dado.handle).trim();
  if (!nome || !handle) return null;

  const referencia = `${origem}/products/${handle}`;
  const lido = produtoVazio(referencia, nome);
  lido.descricao = texto(dado.body_html);
  const tipo = linha(dado.product_type, 120);
  if (tipo) lido.categorias = [tipo];
  lido.marca = linha(dado.vendor, 120) || null;

  const fotos = lista(dado.images)
    .slice(0, TETOS.fotos * 4)
    .map((foto) => objeto(foto))
    .filter((foto): foto is Record<string, unknown> => Boolean(foto))
    .sort((a, b) => Number(a.position ?? 0) - Number(b.position ?? 0));
  lido.fotos = semRepetir(fotos.map((foto) => absoluto(foto.src, referencia)).filter((foto): foto is string => Boolean(foto)));
  const fotoDaVariante = (id: unknown) => {
    const achada = fotos.find((foto) => lista(foto.variant_ids).some((variante) => String(variante) === String(id)));
    return achada ? absoluto(achada.src, referencia) : null;
  };

  const nomesDasOpcoes = lista(dado.options)
    .map((opcao) => objeto(opcao))
    .filter((opcao): opcao is Record<string, unknown> => Boolean(opcao))
    .sort((a, b) => Number(a.position ?? 0) - Number(b.position ?? 0))
    .map((opcao) => linha(opcao.name, 60));

  const variantes: VarianteLida[] = [];
  for (const bruta of lista(dado.variants).slice(0, TETOS.variantes)) {
    const variante = objeto(bruta);
    if (!variante) continue;
    const opcoes: Record<string, string> = {};
    ["option1", "option2", "option3"].forEach((campo, indice) => {
      const valor = linha(variante[campo], 80);
      const nomeDaOpcao = nomesDasOpcoes[indice] || `Opção ${indice + 1}`;
      if (valor && !SEM_VARIACAO.test(valor)) opcoes[nomeDaOpcao] = valor;
    });
    const destaque = objeto(variante.featured_image);
    variantes.push({
      opcoes,
      preco: precoDoDado(variante.price),
      precoAntigo: precoDoDado(variante.compare_at_price),
      foto: absoluto(destaque?.src, referencia) ?? fotoDaVariante(variante.id),
      codigo: primeiroTexto(variante.sku) || null,
    });
    if (lido.pesoKg === null && Number(variante.grams) > 0) lido.pesoKg = Math.round(Number(variante.grams)) / 1000;
  }

  lido.preco = menorPreco(variantes.map((variante) => variante.preco));
  const maisBarata = variantes.find((variante) => variante.preco === lido.preco);
  if (maisBarata?.precoAntigo && lido.preco !== null && maisBarata.precoAntigo > lido.preco) lido.precoAntigo = maisBarata.precoAntigo;

  const unica = variantes.length === 1 ? objeto(lista(dado.variants)[0]) : null;
  if (unica) {
    lido.codigo = primeiroTexto(unica.sku) || null;
    lido.codigoDeBarras = primeiroTexto(unica.barcode) || null;
  }

  /* Variante sem nenhuma opção de verdade é o produto sem variação. */
  const comOpcoes = variantes.filter((variante) => Object.keys(variante.opcoes).length > 0);
  if (comOpcoes.length > 1) {
    lido.variantes = comOpcoes;
    lido.atributos = nomesDasOpcoes
      .map((nomeDaOpcao) => ({
        nome: nomeDaOpcao,
        valores: semRepetir(comOpcoes.map((variante) => variante.opcoes[nomeDaOpcao]).filter((valor): valor is string => Boolean(valor))),
      }))
      .filter((atributo) => atributo.nome && atributo.valores.length);
  }
  return dentroDoTeto(lido);
}

/** A lista de `/products.json` ou o produto de `/products/<handle>.json`. */
export function lerShopify(dado: unknown, origem: string): ProdutoLido[] {
  const raiz = objeto(dado);
  const brutos = raiz?.products !== undefined ? lista(raiz.products) : raiz?.product !== undefined ? [raiz.product] : [];
  return brutos.map((bruto) => produto(bruto, origem)).filter((lido): lido is ProdutoLido => Boolean(lido));
}

/** O handle de um endereço de produto Shopify (`/products/<handle>`), ou null. */
export function handleDoShopify(endereco: string): string | null {
  try {
    const caminho = new URL(endereco).pathname;
    const achado = /\/products\/([^/?#]+)/.exec(caminho);
    return achado ? decodeURIComponent(achado[1]).replace(/\.(json|js)$/, "") : null;
  } catch {
    return null;
  }
}
