import type { Metadata } from "next";
import { clienteDoServidor, createAdminClient } from "@/lib/supabase-admin";
import { VideoFlutuante } from "@/components/produto/VideoDoProduto";
import { videoDoProduto } from "@/lib/video-do-produto";
import { notFound, redirect } from "next/navigation";
import { lerVisaoDaLoja, enderecoDeLogin } from "@/lib/loja/visao.server";
import {
  estaEsgotado,
  regrasDaLoja,
  semEsgotados,
  semPrecos,
  semPrecosNaLista,
  semPrecosNosCombos,
  semPrecosNosServicos,
  semPrecosNosStories,
} from "@/lib/loja/regras-da-loja";
import { ArrowLeft, Flame, ShoppingBag } from "lucide-react";
import Link from "next/link";
import Image from "next/image";
import StoreLayout from "@/components/StoreLayout";
import FontesDaLoja from "@/components/FontesDaLoja";
import { miniatura, produtoComFotoMenor, storiesComFotoMenor, TAMANHO } from "@/lib/foto";
import StoreContent from "@/components/StoreContent";
import { resolveCatalogAppearance } from "@/lib/catalog-appearance";
import AddToCartButton from "@/components/AddToCartButton";
import ProductMobileNavShell from "@/components/ProductMobileNavShell";
import ClientPortalFavoriteButton from "@/components/ClientPortalFavoriteButton";
import ProductPageDesktopHeaderCart from "@/components/ProductPageDesktopHeaderCart";
import { sanitizeDescriptionForRender } from "@/lib/product-description-rich-text";
import ProductShowcase from "@/components/ProductShowcase";
import { carregarServicosDoProduto } from "@/lib/servicos-da-loja.server";
import { VendorShell } from "@/templates/vendor/VendorShell";
import { comEstoqueV2 } from "@/lib/estoque-v2-da-loja.server";
import { aparenciaDoVendor } from "@/templates/vendor/aparencia";
import { VendorProduto } from "@/templates/vendor/VendorProduto";
import { prazoDoVendor, previsaoDoVendor } from "@/templates/vendor/formato";
import ProductReviewsSection from "@/components/ProductReviewsSection";
import CatalogAnalyticsTracker from "@/components/CatalogAnalyticsTracker";
import GuardarVisto from "@/components/home/GuardarVisto";
import { buildCatalogMetadata } from "@/lib/catalog-metadata";
import { buildProductStructuredData } from "@/lib/catalog-structured-data";
import StructuredData from "@/components/StructuredData";
import { ensureColorContrast, getReadableTextColor } from "@/lib/color-contrast";
import { normalizeStoriesGroups } from "@/lib/catalog-stories";
import { carregarLoja, lembrar } from "@/lib/loja/dados-da-loja";
import { ProvedorDaNavegacao } from "@/components/navegacao/ContextoDaNavegacao";
import PaginaDoProdutoDigital from "@/components/digitais/PaginaDoProdutoDigital";
import type { InfoDaLive } from "@/components/digitais/VendaDaLive";
import { montarInfoDaLive } from "@/lib/live/servidor/venda";
import type { AcaoDeCompra } from "@/components/digitais/ProdutoDigital";
import { VendorPaginaDoDigital } from "@/templates/vendor/VendorPaginaDoDigital";
import { BoutiqueShell } from "@/templates/boutique/BoutiqueShell";
import { aparenciaDaBoutique } from "@/templates/boutique/aparencia";
import { BoutiqueProduto } from "@/templates/boutique/BoutiqueProduto";
import { prazoDaBoutique, previsaoDaBoutique } from "@/templates/boutique/formato";
import { BoutiquePaginaDoDigital } from "@/templates/boutique/BoutiquePaginaDoDigital";
import { lerVitrineDoDigital, type VitrineDoDigital } from "@/lib/digitais/servidor/vitrine";
import { codigosAVenda } from "@/lib/digitais/servidor/compra";
import { codigoDeExemplo, type CertificadoNaVenda } from "@/lib/digitais/certificado";
import { cargaHorariaEmMinutos, textoDaCargaHoraria } from "@/lib/digitais/curso";
import { ENTREGA_NA_HORA, entregaNaVitrine, lerConfigDigitalDaLoja, type EntregaNaVitrine } from "@/lib/digitais/produto";
import { lerAjustesDaLoja } from "@/lib/digitais/ajustes";
import { normalizeWhatsappForLink } from "@/lib/phone";
import { ehFamiliaVendor } from "@/themes/registry";

export const revalidate = 0; // Trigger refresh
export const dynamic = "force-dynamic";

async function resolveClientAvatarUrl(rawValue: string | null | undefined) {
  const value = String(rawValue || "").trim();
  if (!value) return null;
  if (value.startsWith("data:")) return value;

  const parseStorageTarget = (input: string): { bucket: string; path: string } | null => {
    const normalized = input.replace(/^\/+/, "");

    if (/^https?:\/\//i.test(input)) {
      try {
        const parsed = new URL(input);
        const publicMarker = "/storage/v1/object/public/";
        const signMarker = "/storage/v1/object/sign/";
        const marker = parsed.pathname.includes(signMarker) ? signMarker : publicMarker;
        if (!parsed.pathname.includes(marker)) return null;
        const rest = parsed.pathname.slice(parsed.pathname.indexOf(marker) + marker.length);
        const [bucket, ...parts] = rest.split("/");
        return bucket && parts.length ? { bucket, path: parts.join("/") } : null;
      } catch {
        return null;
      }
    }

    const clean = normalized.replace(/^storage\/v1\/object\/public\//, "").replace(/^storage\/v1\/object\/sign\//, "");
    const [bucket, ...parts] = clean.split("/");
    return bucket && parts.length ? { bucket, path: parts.join("/") } : null;
  };

  const target = parseStorageTarget(value);
  if (!target) return value;

  try {
    const admin = createAdminClient();
    const { data: signedData, error: signedError } = await admin.storage
      .from(target.bucket)
      .createSignedUrl(target.path, 60 * 60 * 24 * 30);

    if (!signedError && signedData?.signedUrl) return signedData.signedUrl;

    const { data: publicData } = admin.storage.from(target.bucket).getPublicUrl(target.path);
    return publicData?.publicUrl || value;
  } catch {
    return value;
  }
}

type PublicProductAttributeOption =
  | string
  | {
      id?: string | null;
      label?: string | null;
      stock?: number | null;
      [key: string]: unknown;
    };

type PublicProductAttribute = {
  name?: string | null;
  options?: PublicProductAttributeOption[] | null;
  [key: string]: unknown;
};

type PublicProductRow = {
  track_inventory?: boolean | null;
  inventory_scope?: "product" | "option" | "variant" | null;
  product_attributes?: PublicProductAttribute[] | null;
  [key: string]: unknown;
};

type ProductOptionStockRow = {
  option_id?: string | null;
  attribute_name?: string | null;
  option_label?: string | null;
  stock?: number | null;
};

type BasicCategory = {
  id: string;
  name: string;
  slug?: string | null;
  parent_id?: string | null;
};

type ProductTrustItem = {
  icon: string;
  title: string;
};

type ProductReviewStatus = "pending" | "approved" | "rejected";

type ProductReviewRow = {
  id: string;
  client_id: string;
  rating: number;
  comment?: string | null;
  status: ProductReviewStatus;
  created_at: string;
  updated_at?: string | null;
};

type ProductReviewDisplay = {
  id: string;
  client_id: string;
  rating: number;
  comment: string;
  status: ProductReviewStatus;
  created_at: string;
  updated_at?: string | null;
  client_name: string;
  client_avatar_url: string | null;
};


type RelatedProduct = {
  id: string;
  name: string;
  price: number;
  image_url?: string | null;
  price_on_request?: boolean | null;
  use_variation_price_only?: boolean | null;
  category_id?: string | null;
  user_id: string;
  stock: number;
  track_inventory?: boolean | null;
  unit_type?: string | null;
  has_variations?: boolean | null;
  min_sale_qty_enabled?: boolean | null;
  min_sale_qty?: number | null;
  max_sale_qty_enabled?: boolean | null;
  max_sale_qty?: number | null;
  allowed_sale_qty_enabled?: boolean | null;
  allowed_sale_quantities?: number[] | null;
  product_price_tiers?: Array<{
    min_qty: number;
    max_qty: number | null;
    unit_price: number;
    sort_order?: number | null;
    active?: boolean | null;
  }> | null;
  product_attributes?: Array<{
    name?: string | null;
    is_required?: boolean | null;
    options?: Array<string | { label?: string | null; price?: number | null }> | null;
  }> | null;
  custom_fields_config?: Array<{
    id?: string | null;
    title?: string | null;
    active?: boolean | null;
  }> | null;
};

function resolveRelatedPriceDisplay(product: RelatedProduct) {
  const formatMoney = (value: number) =>
    new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(value);
  if (product.price_on_request) {
    return { text: "Sob consulta", hasWholesalePricing: false, isPremiumMeasuredPricing: false, helperText: "" };
  }
  if (product.unit_type === "square_meter") {
    return { text: "Preço sob medida", hasWholesalePricing: false, isPremiumMeasuredPricing: true, helperText: "Informe as medidas para calcular" };
  }
  if (product.use_variation_price_only) {
    const attributes = Array.isArray(product.product_attributes) ? product.product_attributes : [];
    const hasRequired = attributes.some((attribute) => Boolean(attribute?.is_required));
    const relevantAttributes = hasRequired
      ? attributes.filter((attribute) => Boolean(attribute?.is_required))
      : attributes;
    let minimumVariationPrice = 0;
    for (const attribute of relevantAttributes) {
      const options = Array.isArray(attribute?.options) ? attribute.options : [];
      const optionPrices = options
        .map((option) => (typeof option === "string" ? 0 : Number(option?.price) || 0))
        .filter((value) => Number.isFinite(value) && value >= 0);
      if (!optionPrices.length) continue;
      minimumVariationPrice += Math.min(...optionPrices);
    }
    return {
      text: `A partir de ${formatMoney(Math.max(0, minimumVariationPrice))}`,
      hasWholesalePricing: true,
      isPremiumMeasuredPricing: false,
      helperText: "",
    };
  }
  const basePrice = Number(product.price) || 0;
  const tierPrices = (product.product_price_tiers || [])
    .filter((tier) => tier.active !== false)
    .map((tier) => Number(tier.unit_price))
    .filter((value) => Number.isFinite(value) && value > 0);
  const lowestTier = tierPrices.length > 0 ? Math.min(...tierPrices) : null;
  const showcasePrice = lowestTier !== null ? Math.min(basePrice, lowestTier) : basePrice;
  if (lowestTier !== null && lowestTier < basePrice) {
    return {
      text: `A partir de ${formatMoney(showcasePrice)}`,
      hasWholesalePricing: true,
      isPremiumMeasuredPricing: false,
      helperText: "",
    };
  }
  return {
    text: formatMoney(showcasePrice),
    hasWholesalePricing: tierPrices.length > 0,
    isPremiumMeasuredPricing: false,
    helperText: "",
  };
}

function normalizeProductTrustItems(input: unknown): ProductTrustItem[] {
  const defaults: ProductTrustItem[] = [
    { icon: "truck", title: "Entrega Rápida e Segura" },
    { icon: "shieldCheck", title: "Garantia de Qualidade" },
  ];
  const source = Array.isArray(input) ? input : [];
  const items = source
    .map((item) => {
      const rec = item as Partial<ProductTrustItem> | null;
      return {
        icon: typeof rec?.icon === "string" ? rec.icon : "",
        title: typeof rec?.title === "string" ? rec.title : "",
      };
    })
    .filter((item) => item.icon || item.title)
    .slice(0, 2);
  while (items.length < 2) items.push(defaults[items.length]);
  return items;
}

export async function generateMetadata(
  props: { params: Promise<{ slug: string; productId: string }> }
): Promise<Metadata> {
  const params = await props.params;
  const { slug, productId } = params;
  const supabase = clienteDoServidor();

  const dados = await carregarLoja(slug);
  const profile = dados?.profile;
  if (!profile) {
    return { title: "Produto" };
  }

  /* Só o que o título e a descrição precisam, lembrado com o resto da loja. */
  const product = await lembrar(`${slug}/produto/${productId}/meta`, async () => {
    const { data } = await supabase
      .from("products")
      .select("name,description,image_url")
      .eq("id", productId)
      .eq("user_id", profile.id)
      .eq("active", true)
      .eq("show_in_catalog", true)
      .maybeSingle();
    return data;
  });

  const baseTitle = profile.catalog_name || profile.store_name || "Loja";
  const productName = String(product?.name || "").trim();
  const pageTitle = productName ? `${productName} | ${baseTitle}` : baseTitle;
  const cleanDescription = String(product?.description || "").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
  /* Loja que esconde preco nao promete preco na previa do link. */
  const falaDePreco = regrasDaLoja(profile).modoDePreco === "public";
  const pageDescription =
    cleanDescription ||
    (productName
      ? falaDePreco
        ? `Veja detalhes e preços de ${productName} em ${baseTitle}.`
        : `Veja detalhes de ${productName} em ${baseTitle}.`
      : `Conheça os produtos de ${baseTitle}.`);

  /* Escopo "page": o titulo e a descricao do PRODUTO vencem o SEO da loja.
     Antes o SEO da loja sobrescrevia tudo e o link compartilhado mostrava a
     foto do produto com o titulo e a descricao da loja. */
  return buildCatalogMetadata({
    profile,
    scope: "page",
    pageTitle,
    pageDescription,
    image: String(product?.image_url || "").trim() || null,
    imageAlt: productName || null,
    path: `/p/${productId}`,
  });
}

function applyOptionStocksToAttributes(
  product: PublicProductRow,
  optionStocks: ProductOptionStockRow[]
): PublicProductRow {
  const attributes = Array.isArray(product.product_attributes) ? product.product_attributes : [];
  if (!attributes.length || !optionStocks.length) return product;

  const byId = new Map<string, ProductOptionStockRow>();
  const byAttrLabel = new Map<string, ProductOptionStockRow>();
  for (const row of optionStocks) {
    const optionId = typeof row.option_id === "string" ? row.option_id : "";
    const attrName = typeof row.attribute_name === "string" ? row.attribute_name.trim().toLowerCase() : "";
    const label = typeof row.option_label === "string" ? row.option_label.trim().toLowerCase() : "";
    if (optionId) byId.set(optionId, row);
    if (attrName && label) byAttrLabel.set(`${attrName}::${label}`, row);
  }

  const nextAttributes = attributes.map((attribute) => {
    const attributeName = typeof attribute.name === "string" ? attribute.name.trim().toLowerCase() : "";
    const options = Array.isArray(attribute.options) ? attribute.options : [];
    const nextOptions = options.map((option) => {
      if (!option || typeof option === "string") return option;
      const optionId = typeof option.id === "string" ? option.id : "";
      const optionLabel = typeof option.label === "string" ? option.label.trim().toLowerCase() : "";
      const directMatch = optionId ? byId.get(optionId) : undefined;
      const fallbackMatch = !directMatch && attributeName && optionLabel
        ? byAttrLabel.get(`${attributeName}::${optionLabel}`)
        : undefined;
      const matched = directMatch || fallbackMatch;
      if (!matched) return option;
      const nextStock = Number.isFinite(Number(matched.stock)) ? Number(matched.stock) : option.stock;
      return { ...option, stock: nextStock };
    });
    return { ...attribute, options: nextOptions };
  });

  return {
    ...product,
    product_attributes: nextAttributes,
  };
}

export default async function ProductPage(props: {
  params: Promise<{ slug: string; productId: string }>;
  searchParams?: Promise<{ tema?: string; na_live?: string }>;
}) {
  const params = await props.params;
  const { slug, productId } = params;
  const supabase = clienteDoServidor();

  /* A loja inteira, lembrada por dois minutos (lib/loja/dados-da-loja.ts):
     perfil, categorias, produtos enxutos, stories e cupom. */
  const dados = await carregarLoja(slug);
  const profile = dados?.profile;
  if (!dados || !profile) return notFound();

  /* `?tema=` só existe fora de produção: serve para abrir a MESMA loja em dois
     templates lado a lado e comparar o que cada um mostra. Na loja publicada o
     tema é sempre o que está gravado no perfil. */
  const buscados = (await props.searchParams) || {};
  const temaPedido = String(buscados?.tema || "").trim();
  const temaDaPagina =
    process.env.NODE_ENV !== "production" && ["vendor", "vitrine", "default", "boutique"].includes(temaPedido)
      ? temaPedido
      : profile.catalog_theme;
  let admin: ReturnType<typeof createAdminClient> | null = null;
  try {
    admin = createAdminClient();
  } catch {
    admin = null;
  }

  /* Quem esta olhando e o que as regras da loja deixam ver. */
  const { visao, cliente } = await lerVisaoDaLoja(slug, profile);
  if (visao.lojaFechada) redirect(`/${slug}`);
  if (visao.exigeLoginNaLoja) redirect(enderecoDeLogin(slug, `/${slug}/p/${productId}`));
  const loggedClient: { id: string; name: string; avatar_url: string | null; favorites?: string[] | null } | null = cliente
    ? { id: cliente.id, name: cliente.name, avatar_url: await resolveClientAvatarUrl(cliente.avatar_url), favorites: cliente.favorites }
    : null;
  const semPreco = !visao.mostrarPreco;

  const baseSelect = "*, product_attributes(*), product_variations(*)";
  const selectWithExtras = `${baseSelect}, product_price_tiers(min_qty,max_qty,unit_price,sort_order,active), product_images(id,image_url,sort_order,is_primary)`;

  /* O produto, as avaliações aprovadas, os combos e os serviços vêm juntos,
     numa ida só ao banco, e ficam lembrados por dois minutos na mesma
     lembrança da loja (o aviso do admin apaga tudo dela de uma vez). Só o
     que é do visitante, a avaliação dele, fica de fora. Antes eram seis
     idas em sequência, a 200 ms cada. */
  const carregarProduto = () =>
    lembrar(`${slug}/produto/${productId}`, async () => {
      let { data: product, error: productError } = await supabase
        .from("products")
        .select(selectWithExtras)
        .eq("id", productId)
        .eq("user_id", profile.id)
        .eq("active", true)
        .eq("show_in_catalog", true)
        .order("position", { ascending: true, foreignTable: "product_attributes" })
        .single();

      if (productError) {
        const message = String((productError as { message?: unknown } | null)?.message ?? "").toLowerCase();
        const missingTiers = message.includes("product_price_tiers");
        const missingImages = message.includes("product_images");

        if (missingTiers || missingImages) {
          const fallbackFields = [
            baseSelect,
            missingTiers ? "" : "product_price_tiers(min_qty,max_qty,unit_price,sort_order,active)",
            missingImages ? "" : "product_images(id,image_url,sort_order,is_primary)",
          ]
            .filter(Boolean)
            .join(", ");

          const fallback = await supabase
            .from("products")
            .select(fallbackFields)
            .eq("id", productId)
            .eq("user_id", profile.id)
            .eq("active", true)
            .eq("show_in_catalog", true)
            .order("position", { ascending: true, foreignTable: "product_attributes" })
            .single();
          product = fallback.data;
          productError = fallback.error;
        }
      }

      if (!product) return null;

      if ((product as PublicProductRow).track_inventory !== false && (product as PublicProductRow).inventory_scope === "option") {
        const { data: optionStocksData, error: optionStocksError } = await supabase
          .from("product_option_stocks")
          .select("option_id,attribute_name,option_label,stock")
          .eq("product_id", productId)
          .eq("active", true);
        if (!optionStocksError && Array.isArray(optionStocksData) && optionStocksData.length > 0) {
          product = applyOptionStocksToAttributes(
            product as PublicProductRow,
            optionStocksData as ProductOptionStockRow[]
          ) as typeof product;
        }
      }
      /* Estoque v2: a marca de quem decide o saldo (o banco, por combinação);
         a página pergunta a cada escolha. Fora do v2 o produto volta intacto. */
      const [marcado] = await comEstoqueV2(supabase, String(profile.id), [product]);
      return marcado ?? product;
    });

  const carregarAvaliacoes = () =>
    lembrar(`${slug}/produto/${productId}/avaliacoes`, async (): Promise<ProductReviewDisplay[]> => {
      if (!admin) return [];
      try {
        const { data } = await admin
          .from("product_reviews")
          .select("id,client_id,rating,comment,status,created_at,updated_at")
          .eq("product_id", productId)
          .eq("status", "approved")
          .order("created_at", { ascending: false });
        const approvedRows = (data || []) as ProductReviewRow[];
        const clientIds = Array.from(
          new Set(approvedRows.map((review) => review.client_id).filter((value): value is string => Boolean(value)))
        );
        const clientsById = new Map<string, { name: string; avatar_url: string | null }>();
        if (clientIds.length > 0) {
          const { data: clientRows } = await admin
            .from("clients")
            .select("id,name,avatar_url")
            .in("id", clientIds);
          const resolvedClients = await Promise.all(
            ((clientRows || []) as Array<{ id: string; name?: string | null; avatar_url?: string | null }>).map(async (client) => ({
              id: client.id,
              name: String(client.name || "Cliente").trim() || "Cliente",
              avatar_url: await resolveClientAvatarUrl(client.avatar_url),
            }))
          );
          resolvedClients.forEach((client) => {
            clientsById.set(client.id, { name: client.name, avatar_url: client.avatar_url });
          });
        }
        return approvedRows.map((review) => {
          const client = clientsById.get(review.client_id);
          return {
            id: review.id,
            client_id: review.client_id,
            rating: Number(review.rating || 0),
            comment: String(review.comment || "").trim(),
            status: review.status,
            created_at: review.created_at,
            updated_at: review.updated_at || null,
            client_name: client?.name || "Cliente",
            client_avatar_url: client?.avatar_url || null,
          };
        });
      } catch {
        return [];
      }
    });

  const carregarCombos = () =>
    lembrar(`${slug}/produto/${productId}/combos`, async () => {
      const { data } = await supabase
        .from("product_combos")
        .select(`
      id,
      title,
      discount_type,
      discount_value,
      main_product_id,
      combo_product:products!combo_product_id (
        id,
        name,
        price,
        image_url,
        price_on_request,
        use_variation_price_only,
        shipping_weight_kg,
        shipping_width_cm,
        shipping_height_cm,
        shipping_length_cm
      )
    `)
        .eq("main_product_id", productId)
        .eq("active", true);
      /* O tipo do embed do Supabase vem como lista; o widget de combos sabe o formato. */
      return (data || []) as unknown as never[];
    });

  const carregarServicos = () =>
    lembrar(`${slug}/produto/${productId}/servicos`, () => carregarServicosDoProduto(supabase, profile.id, productId));

  const [produtoCru, approvedReviews, combosData, servicosDaLoja] = await Promise.all([
    carregarProduto(),
    carregarAvaliacoes(),
    carregarCombos(),
    carregarServicos(),
  ]);

  if (!produtoCru) return notFound();

  /* A galeria e a foto principal dividem a mesma URL: o navegador baixa uma
     vez e usa nos dois lugares. Sem preco para este visitante, o produto, os
     combos e os servicos chegam ao navegador sem valor nenhum. */
  const product = produtoComFotoMenor(semPreco ? semPrecos(produtoCru) : produtoCru, TAMANHO.produto);
  const combosDaPagina = semPreco ? semPrecosNosCombos(combosData) : combosData;
  const servicosDaPagina = semPreco ? semPrecosNosServicos(servicosDaLoja) : servicosDaLoja;

  const storeName = profile.catalog_name || profile.store_name || "Loja";
  const logoUrl = miniatura(profile.catalog_logo_url || profile.logo_url, TAMANHO.logo);

  const categoriesResult = { data: (dados.categories || []) as BasicCategory[] };
  const categoryResult = {
    data: product.category_id
      ? categoriesResult.data.find((categoria) => categoria.id === product.category_id) ?? null
      : null,
  };

  let categoryName = "Produto";
  let selectedCategory: BasicCategory | null = null;
  const category = categoryResult?.data as BasicCategory | null;
  if (category) {
    categoryName = category.name;
    selectedCategory = category;
  }

  const categories = ((categoriesResult?.data as BasicCategory[] | null) || []) as BasicCategory[];
  const descendantsByParent = new Map<string, string[]>();
  categories.forEach((category) => {
    const parentId = String(category.parent_id || "");
    if (!parentId) return;
    const list = descendantsByParent.get(parentId) || [];
    list.push(category.id);
    descendantsByParent.set(parentId, list);
  });
  const categoryScope = new Set<string>();
  if (selectedCategory?.id) {
    const isSubcategory = Boolean(selectedCategory.parent_id);
    if (isSubcategory) {
      categoryScope.add(selectedCategory.id);
    } else {
      categoryScope.add(selectedCategory.id);
      const stack = [selectedCategory.id];
      while (stack.length > 0) {
        const current = stack.pop() as string;
        const children = descendantsByParent.get(current) || [];
        children.forEach((childId) => {
          if (categoryScope.has(childId)) return;
          categoryScope.add(childId);
          stack.push(childId);
        });
      }
    }
  }

  /* Os relacionados saem da lista enxuta da loja, já lembrada: nada de
     consulta nova só para oito cards. Primeiro os da mesma categoria, no
     máximo 24, como a consulta antiga limitava (a lista inteira ia junto no
     HTML e pesava 800 KB numa loja de 216 produtos). */
  const listaDaLoja = visao.regras.esgotado === "hide" ? semEsgotados(dados.products) : dados.products || [];
  const relatedProductsData = ((semPreco ? semPrecosNaLista(listaDaLoja) : listaDaLoja) as RelatedProduct[])
    .filter((item) => item.id !== product.id)
    .filter((item) => (categoryScope.size ? Boolean(item.category_id && categoryScope.has(item.category_id)) : true))
    .slice(0, 24);

  const relatedProducts = relatedProductsData
    .filter((item) => item.id !== product.id)
    .filter((item) => {
      if (!categoryScope.size) return true;
      return item.category_id ? categoryScope.has(item.category_id) : false;
    })
    .slice(0, 8)
    .map((item) => produtoComFotoMenor(item, TAMANHO.card));

  const primaryColor = profile.primary_color || "#4F46E5";
  const aparencia = resolveCatalogAppearance(profile);
  const cardPriceColor = ensureColorContrast(primaryColor, "#FFFFFF", {
    dark: "#111827",
    light: "#FFFFFF",
    minRatio: 4.5,
  });
  const backControlColor = ensureColorContrast("#FFFFFF", primaryColor, {
    dark: "#0f172a",
    light: "#FFFFFF",
    minRatio: 3.2,
  });
  const isBackControlLight = backControlColor.toLowerCase() === "#ffffff";
  const productTrustItems = normalizeProductTrustItems(profile.catalog_product_trust_items);
  const productDescriptionHtml = sanitizeDescriptionForRender(product.description);

  /* Produto digital: a compra é rápida, fora do carrinho (components/digitais
     e templates/vendor/VendorProdutoDigital; o curso tem a página de venda
     dele, VendaDoCurso e VendorVendaDoCurso). O resto da página é o mesmo. */
  const ehDigital = (produtoCru as { product_kind?: string | null }).product_kind === "digital";
  let vitrineDigital: VitrineDoDigital | null = null;
  let acaoDigital: AcaoDeCompra = { tipo: "fechado", motivo: null };
  let entregaDigital: EntregaNaVitrine = ENTREGA_NA_HORA;
  let certificadoDoCurso: CertificadoNaVenda | null = null;
  let imagensDoDigital: string[] = [];
  let infoDaLive: InfoDaLive | null = null;
  if (ehDigital) {
    const configCru = (produtoCru as { digital_config?: unknown }).digital_config;
    vitrineDigital = admin ? await lerVitrineDoDigital(admin, String(profile.id), configCru).catch(() => null) : null;
    if (!vitrineDigital) {
      const configDoDigital = lerConfigDigitalDaLoja(configCru);
      vitrineDigital = { config: configDoDigital, prazoDias: configDoDigital.prazoDias ?? null, meios: [], ajustes: lerAjustesDaLoja(null) };
    }
    const numeroDaLoja = normalizeWhatsappForLink(profile.whatsapp);
    const conversa = numeroDaLoja
      ? `https://wa.me/${numeroDaLoja}?text=${encodeURIComponent(`Olá! Quero comprar: ${product.name}`)}`
      : null;
    const precoDoDigital = Number((produtoCru as { price?: number | null }).price) || 0;
    if (visao.precisaEntrar) acaoDigital = { tipo: "entrar", href: enderecoDeLogin(slug, `/${slug}/p/${productId}`) };
    else if (!visao.podeComprar || visao.lojaPausada) acaoDigital = { tipo: "fechado", motivo: visao.motivoDoBloqueio };
    else if (!visao.mostrarPreco || (precoDoDigital > 0 && !vitrineDigital.meios.length)) acaoDigital = { tipo: "whatsapp", href: conversa };
    else acaoDigital = { tipo: "comprar", href: `/${slug}/comprar/${productId}` };
    /* Códigos e licenças: sem código livre, esgotado (a loja adiciona mais em Produtos digitais). */
    if (vitrineDigital.config.tipo === "codigo" && admin && (acaoDigital.tipo === "comprar" || acaoDigital.tipo === "whatsapp")) {
      const aVenda = await codigosAVenda(admin, String(profile.id), String(product.id)).catch(() => 1);
      if (aVenda <= 0) acaoDigital = { tipo: "fechado", motivo: "Esgotado" };
    }
    /* Live: a página dela, com o botão que a data e as vagas decidem. */
    if (vitrineDigital.config.tipo === "live" && admin) {
      infoDaLive = await montarInfoDaLive(admin, {
        slug,
        lojaId: String(profile.id),
        produtoId: String(product.id),
        nome: product.name,
        nomeDaLoja: String(profile.catalog_name || profile.store_name || "Loja"),
        acaoDaLoja: acaoDigital,
      }).catch(() => null);
    }
    entregaDigital = entregaNaVitrine(vitrineDigital.config);
    /* Curso com certificado (nas Configurações e no curso): a página mostra a folha. */
    const cursoDoDigital = vitrineDigital.config.curso;
    if (vitrineDigital.config.tipo === "curso" && cursoDoDigital?.certificado.emitir && vitrineDigital.ajustes.certificado.emitir) {
      const nomeDaLojaNoCertificado = String(profile.catalog_name || profile.store_name || "Loja");
      certificadoDoCurso = {
        loja: nomeDaLojaNoCertificado,
        logo: String(profile.catalog_logo_url || profile.logo_url || "") || null,
        cargaHoraria: textoDaCargaHoraria(cargaHorariaEmMinutos(cursoDoDigital.certificado, cursoDoDigital.conteudo.duracaoS)),
        codigo: codigoDeExemplo(nomeDaLojaNoCertificado),
      };
    }
    const fotos = ((product as { product_images?: Array<{ image_url?: string | null; is_primary?: boolean | null; sort_order?: number | null }> })
      .product_images || [])
      .filter((foto) => foto?.image_url)
      .sort((a, b) => Number(b.is_primary === true) - Number(a.is_primary === true) || (Number(a.sort_order) || 0) - (Number(b.sort_order) || 0))
      .map((foto) => String(foto.image_url));
    imagensDoDigital = Array.from(new Set([...(product.image_url ? [String(product.image_url)] : []), ...fotos]));
  }
  /* A avaliação do próprio visitante (pendente ou recusada) é dele: fica
     fora da lembrança e só é lida quando há cliente logado. */
  let ownReview: ProductReviewDisplay | null = null;
  if (admin && loggedClient?.id) {
    try {
      const { data: ownRow } = await admin
        .from("product_reviews")
        .select("id,client_id,rating,comment,status,created_at,updated_at")
        .eq("product_id", product.id)
        .eq("client_id", loggedClient.id)
        .in("status", ["pending", "rejected"])
        .order("created_at", { ascending: false })
        .limit(1)
        .maybeSingle();
      if (ownRow) {
        const row = ownRow as ProductReviewRow;
        ownReview = {
          id: row.id,
          client_id: row.client_id,
          rating: Number(row.rating || 0),
          comment: String(row.comment || "").trim(),
          status: row.status,
          created_at: row.created_at,
          updated_at: row.updated_at || null,
          client_name: loggedClient.name || "Cliente",
          client_avatar_url: loggedClient.avatar_url || null,
        };
      }
    } catch {
      ownReview = null;
    }
  }

  const selectedAnnouncementCoupon = dados.coupon ?? null;
  const storiesGroups = storiesComFotoMenor(
    normalizeStoriesGroups(semPreco ? semPrecosNosStories(dados.stories) : dados.stories || [])
  );

  /* Os serviços que este produto oferece: a biblioteca da loja já filtrada
     pelo que o produto liga ou desliga. */

  const storeSlogan = profile.catalog_slogan || "";

  const categoriesForStoreContent = categories.map(c => ({
    id: c.id,
    name: c.name,
    slug: c.slug || undefined,
    parent_id: c.parent_id
  }));

  const storeContentProps = {
    products: relatedProductsData as any[],
    categories: categoriesForStoreContent,
    banners: [],
    sections: [],
    slideEnabled: false,
    infoStripEnabled: profile.catalog_info_strip_enabled ?? false,
    infoStripItems: (profile.catalog_info_strip_items as any[]) || [],
    slug,
    primaryColor,
    storeName,
    storeSlogan,
    logoUrl,
    showStoreNameInHeader: profile.catalog_show_name_in_header,
    showStoreSloganInHeader: profile.catalog_show_slogan_in_header,
    storeNameColor: profile.catalog_name_color,
    storeSloganColor: profile.catalog_slogan_color,
    catalogHeaderIconColor: profile.catalog_header_icon_color,
    logoEffectEnabled: profile.catalog_logo_effect_enabled,
    logoEffectType: profile.catalog_logo_effect,
    logoEffectSpeed: profile.catalog_logo_effect_speed,
    logoEffectIntensity: profile.catalog_logo_effect_intensity,
    whatsapp: profile.whatsapp,
    /* A escolha da loja sobre o botao flutuante vale na pagina do produto
       tambem; antes ele aparecia aqui mesmo desligado. */
    whatsappFloat: profile.catalog_whatsapp_float,
    phone: profile.phone,
    companyPhone: profile.company_phone,
    companyWhatsapp: profile.company_whatsapp,
    address: profile.address,
    cnpj: profile.cnpj,
    catalogShowDocument: profile.catalog_show_document,
    headerColor: aparencia.headerColor,
    headerTextColor: aparencia.headerTextColor,
    menuColor: aparencia.menuColor,
    menuTextColor: aparencia.menuTextColor,
    pageBgColor: aparencia.pageBgColor,
    cardButtonColor: aparencia.cardButtonColor,
    fontHeadingFamily: aparencia.fonts.heading,
    fontBodyFamily: aparencia.fonts.body,
    footerColor: profile.catalog_footer_color,
    footerTextColor: profile.catalog_footer_text_color,
    socialFacebook: profile.social_facebook,
    socialInstagram: profile.social_instagram,
    socialTwitter: profile.social_twitter,
    socialLinkedin: profile.social_linkedin,
    socialYoutube: profile.social_youtube,
    companySocialFacebook: profile.social_facebook,
    companySocialInstagram: profile.social_instagram,
    companySocialTwitter: profile.social_twitter,
    companySocialLinkedin: profile.social_linkedin,
    companySocialYoutube: profile.social_youtube,
    announcementEnabled: profile.catalog_announcement_enabled ?? false,
    announcementText: profile.catalog_announcement_text,
    announcementCountdownEnabled: profile.catalog_announcement_countdown_enabled,
    announcementCountdownEndAt: profile.catalog_announcement_countdown_end_at,
    announcementShowCouponBadge: profile.catalog_announcement_show_coupon_badge,
    announcementCouponBadgeColor: profile.catalog_announcement_coupon_badge_color,
    announcementBackgroundColor: profile.catalog_announcement_background_color,
    announcementTextColor: profile.catalog_announcement_text_color,
    announcementButtonLabel: profile.catalog_announcement_button_label,
    announcementButtonUrl: profile.catalog_announcement_button_url,
    announcementSticky: profile.catalog_announcement_sticky,
    announcementCoupon: selectedAnnouncementCoupon,
    announcementCouponId: profile.catalog_announcement_coupon_id,
    socialProofEnabled: profile.catalog_social_proof_enabled,
    socialProofIntervalSeconds: profile.catalog_social_proof_interval_seconds,
    socialProofPosition: profile.catalog_social_proof_position,
    socialProofDurationSeconds: profile.catalog_social_proof_duration_seconds,
    socialProofMaxVisible: profile.catalog_social_proof_max_visible,
    vitrineProductLayout: profile.catalog_vitrine_product_layout,
    storiesGroups: storiesGroups,
    catalogStoriesEnabled: false,
    loggedClient: loggedClient,
  };
  const averageRating = approvedReviews.length
    ? approvedReviews.reduce((sum, review) => sum + Number(review.rating || 0), 0) / approvedReviews.length
    : 0;

  /* JSON-LD: e isso que faz o Google mostrar preco, foto grande e estrelas no
     resultado da busca. As tags Open Graph resolvem so a previa de link. */
  const dadosEstruturados = buildProductStructuredData({
    slug,
    storeName,
    productId: product.id,
    name: product.name,
    description: product.description,
    imageUrl: product.image_url,
    price: Number(product.price) || 0,
    priceOnRequest: product.price_on_request,
    /* Com "Permitir estoque negativo" o produto se vende sem saldo: para o
       Google ele esta disponivel. */
    inStock: !estaEsgotado(product),
    ratingAverage: averageRating,
    ratingCount: approvedReviews.length,
    /* Sem slug nao da para montar a URL da categoria, e um breadcrumb com link
       quebrado e pior que nenhum breadcrumb. */
    category:
      selectedCategory?.slug && selectedCategory?.name
        ? { name: selectedCategory.name, slug: selectedCategory.slug }
        : null,
  });

  /* O vídeo do produto (migration 183): primeiro item da galeria e, se a
     loja ligou, a janelinha flutuante. Arquivo só entra quando está pronto. */
  const produtoComVideo = produtoCru as { id: string; video_file_id?: string | null; video_link?: unknown; video_flutuante?: boolean | null; video_opcoes?: unknown };
  let videoPronto = false;
  if (!ehDigital && produtoComVideo.video_file_id) {
    const { data: arquivoDoVideo } = await (admin ?? createAdminClient())
      .from("digital_files")
      .select("status,categoria")
      .eq("id", produtoComVideo.video_file_id)
      .eq("user_id", profile.id)
      .maybeSingle();
    videoPronto = (arquivoDoVideo as { status?: string; categoria?: string } | null)?.status === "pronto" && (arquivoDoVideo as { categoria?: string }).categoria === "video";
  }
  /* Aberta por trás da live (`?na_live=1`, o "Comprar" do liveshop): o vídeo
     do produto não toca sozinho nem abre a janelinha, para não brigar com o
     som da live. Continua na galeria, esperando o play. */
  const naLive = buscados?.na_live === "1";
  const videoCru = ehDigital ? null : videoDoProduto({ ...produtoComVideo, id: product.id }, videoPronto);
  const videoDaPagina =
    videoCru && naLive
      ? { ...videoCru, flutuante: false, opcoes: { ...videoCru.opcoes, galeria: { ...videoCru.opcoes.galeria, sozinho: false, comSom: false } } }
      : videoCru;
  const capaDoVideo =
    (((product as { product_images?: Array<{ image_url: string; is_primary?: boolean | null }> }).product_images || []).find((i) => i.is_primary)?.image_url ??
      (product as { image_url?: string | null }).image_url) ||
    null;

  const productContent = (
    <>
      <StructuredData data={dadosEstruturados} />
      <CatalogAnalyticsTracker
        slug={slug}
        eventName="page_view"
        productId={product.id}
        categoryId={product.category_id || null}
        onceKey={`product_page_view:${product.id}`}
      />
      <CatalogAnalyticsTracker
        slug={slug}
        eventName="product_view"
        productId={product.id}
        categoryId={product.category_id || null}
        onceKey={`product_view:${product.id}`}
      />
      {/* Para o bloco "Vistos recentemente" da página inicial (só no navegador). */}
      <GuardarVisto slug={slug} produtoId={product.id} />
      {/* Respiro normal. A folga para a barra fixa de compra fica no rodape do
          catalogo, via `.catalog-shell:has(.catalog-product-page)` no
          globals.css — no fim da pagina, que e onde ela faz falta. */}
      <div className="catalog-product-page min-h-screen bg-gray-50/50 pb-8 md:pb-12">
        {profile.catalog_theme !== "vitrine" && (
          <header className="catalog-product-header sticky top-0 z-40 shadow-sm" style={{ backgroundColor: primaryColor, color: getReadableTextColor(primaryColor) }}>
            <div className="max-w-7xl mx-auto px-4 h-20 relative flex items-center justify-center">
              <Link
                href={`/${slug}`}
                className="absolute left-4 z-10 inline-flex items-center gap-1.5 text-sm font-medium transition-colors"
                style={{
                  color: backControlColor,
                  textShadow: isBackControlLight ? "0 1px 2px rgba(0,0,0,0.35)" : "none",
                }}
              >
                <ArrowLeft size={15} />
                Voltar
              </Link>
              {logoUrl ? (
                <Link href={`/${slug}`} className="inline-flex items-center justify-center">
                  <Image
                    src={logoUrl}
                    alt={storeName}
                    className="h-11 w-auto object-contain"
                    width={96}
                    height={44}
                    unoptimized
                  />
                </Link>
              ) : null}
              <div className="absolute right-4 z-10 hidden md:flex">
                <ProductPageDesktopHeaderCart backControlColor={backControlColor} isBackControlLight={isBackControlLight} />
              </div>
            </div>
          </header>
        )}

        <main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 md:py-12">
          {/*
            A trilha mudou de lugar: era uma faixa entre o menu e o produto,
            e roubava a primeira dobra inteira de quem entra pela busca ou por
            um anúncio. Agora vive dentro da coluna do produto, encostada no
            título — o padrão de quem vende no varejo.
          */}
          {ehDigital && vitrineDigital ? (
            <PaginaDoProdutoDigital
              slug={slug}
              produto={{
                id: product.id,
                nome: product.name,
                preco: Number(product.price) || 0,
                imagens: imagensDoDigital,
                descricaoHtml: productDescriptionHtml,
              }}
              categoria={categoryName !== "Produto" ? { nome: categoryName, slug: selectedCategory?.slug || null } : null}
              config={vitrineDigital.config}
              prazoDias={vitrineDigital.prazoDias}
              meios={vitrineDigital.meios}
              acao={acaoDigital}
              entrega={entregaDigital}
              certificado={certificadoDoCurso}
              live={infoDaLive}
              mostrarPreco={visao.mostrarPreco}
              textoSemPreco={visao.textoNoLugarDoPreco}
              corDaMarca={primaryColor}
              area={{ nome: vitrineDigital.ajustes.area.nome || storeName, cor: vitrineDigital.ajustes.area.cor || primaryColor }}
              barra={aparencia.buyBar}
              notaMedia={averageRating}
              quantasAvaliacoes={approvedReviews.length}
              isVitrine={temaDaPagina === "vitrine"}
              avaliacoes={
                /* No Vitrine as avaliações já vêm logo abaixo, para todos os produtos. */
                temaDaPagina !== "vitrine" && visao.regras.avaliacoes ? (
                  <ProductReviewsSection
                    slug={slug}
                    productId={product.id}
                    loggedClient={loggedClient}
                    approvedReviews={approvedReviews}
                    ownReview={ownReview}
                    reviewCount={approvedReviews.length}
                    averageRating={averageRating}
                    loginHref={`/${slug}/cliente/login`}
                  />
                ) : null
              }
            />
          ) : (
          <ProductShowcase
            product={product}
            primaryColor={primaryColor}
          storeSlug={slug}
            whatsappNumber={profile.whatsapp}
            buyBarColors={aparencia.buyBar}
            categoryName={categoryName}
            categorySlug={selectedCategory?.slug || null}
            productDescriptionHtml={productDescriptionHtml}
            productTrustItems={productTrustItems}
            combosData={combosDaPagina || undefined}
            gruposDeServico={servicosDaPagina.grupos}
            servicosDoProduto={servicosDaPagina.servicos}
            loggedClient={loggedClient}
            isVitrine={temaDaPagina === "vitrine"}
            video={videoDaPagina}
            averageRating={averageRating}
            reviewCount={approvedReviews.length}
          />
          )}

          {relatedProducts.length > 0 ? (
            <section className="catalog-related-products mt-14 border-t border-zinc-200 pt-10 md:mt-16 md:pt-12">
              <div className="mb-5 flex items-end justify-between gap-4">
                <h2 className="text-[1.9rem] font-semibold tracking-[-0.04em] text-zinc-950">Relacionados</h2>
                <span className="text-sm text-zinc-500">{relatedProducts.length} itens</span>
              </div>

              <div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4">
                {relatedProducts.map((item) => {
                  const priceDisplay = resolveRelatedPriceDisplay(item);
                  return (
                    <article
                      key={item.id}
                      className="catalog-related-product-card group relative overflow-hidden rounded-[24px] border border-zinc-200 bg-white transition-all duration-300 hover:-translate-y-0.5 hover:border-zinc-300"
                    >
                      <Link href={`/${slug}/p/${item.id}`} className="block relative aspect-square overflow-hidden bg-zinc-50">
                        {item.image_url ? (
                          <Image
                            src={item.image_url}
                            alt={item.name}
                            className="object-cover w-full h-full transition-transform duration-700 group-hover:scale-[1.03]"
                            fill
                            sizes="(max-width: 640px) 50vw, (max-width: 1024px) 25vw, 20vw"
                            unoptimized
                          />
                        ) : (
                          <div className="w-full h-full flex items-center justify-center">
                            <ShoppingBag className="text-gray-200 w-10 h-10" />
                          </div>
                        )}
                        <div className="absolute inset-0 bg-gradient-to-t from-black/5 to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
                        {item.unit_type === "square_meter" && (
                          <div className="absolute left-3 top-3">
                            <span className="rounded-full border border-zinc-200 bg-white/95 px-2 py-0.5 text-[10px] font-semibold text-zinc-700">
                              m²
                            </span>
                          </div>
                        )}
                        {estaEsgotado(item) && (
                          <div className="absolute inset-0 bg-white/60 backdrop-blur-[1px] flex items-center justify-center">
                            <span className="rounded-full border border-zinc-200 bg-white px-3 py-1 text-xs font-medium text-zinc-800">Esgotado</span>
                          </div>
                        )}
                      </Link>
                      <ClientPortalFavoriteButton
                        productId={item.id}
                        clientId={loggedClient?.id}
                        initialFavorites={loggedClient?.favorites}
                        primaryColor={primaryColor}
                      />

                      <div className="flex flex-col gap-3 p-4">
                        <div className="flex-1 min-h-0">
                          <h3 className="line-clamp-2 min-h-[2.5rem] text-[14px] font-medium leading-[1.4] text-zinc-800" title={item.name}>
                            {item.name}
                          </h3>

                          <div className="mt-2 min-h-5 flex items-center">
                            {priceDisplay.hasWholesalePricing ? (
                              <span className="inline-flex items-center gap-1 rounded-full bg-orange-50 px-2 py-0.5 text-[10px] font-semibold text-orange-700">
                                <Flame size={9} className="fill-orange-500 text-orange-500" />
                                Atacado
                              </span>
                            ) : priceDisplay.isPremiumMeasuredPricing ? (
                              <span className="inline-flex items-center rounded-full bg-violet-50 px-2 py-0.5 text-[10px] font-semibold text-violet-700">
                                Sob medida
                              </span>
                            ) : null}
                          </div>
                        </div>

                        <div className="mt-auto flex items-end justify-between gap-3 border-t border-zinc-100 pt-3">
                          <div className="min-w-0 flex-1">
                            {priceDisplay.hasWholesalePricing && (
                              <span className="mb-1 block text-[10px] font-medium leading-none text-zinc-400">A partir de</span>
                            )}
                            <p className="text-[18px] font-semibold leading-none tracking-[-0.03em]" style={{ color: cardPriceColor }}>
                              {priceDisplay.hasWholesalePricing ? priceDisplay.text.replace("A partir de ", "") : priceDisplay.text}
                            </p>
                            {priceDisplay.isPremiumMeasuredPricing && (
                              <span className="mt-1 block text-[10px] leading-tight text-zinc-500">{priceDisplay.helperText}</span>
                            )}
                          </div>
                          <div className="shrink-0 self-end">
                            <AddToCartButton
                              product={{
                                ...item,
                                image_url: item.image_url || undefined,
                                has_variations: item.has_variations === true,
                              }}
                              primaryColor={cardPriceColor}
                              slug={slug}
                              variant="icon"
                            />
                          </div>
                        </div>
                      </div>
                    </article>
                  );
                })}
              </div>
            </section>
          ) : null}

          {temaDaPagina === "vitrine" && visao.regras.avaliacoes ? (
            <ProductReviewsSection
              slug={slug}
              productId={product.id}
              loggedClient={loggedClient}
              approvedReviews={approvedReviews}
              ownReview={ownReview}
              reviewCount={approvedReviews.length}
              averageRating={averageRating}
              loginHref={`/${slug}/cliente/login`}
            />
          ) : null}
        </main>

        <ProductMobileNavShell
          slug={slug}
          profileId={profile.id}
          primaryColor={primaryColor}
          logoUrl={logoUrl}
          storeName={storeName}
          storeSlogan={profile.catalog_slogan}
          whatsappDigits={profile.company_whatsapp || profile.whatsapp || ""}
          categories={categories}
          announcementCouponId={profile.catalog_announcement_coupon_id}
          initialSelectedCategoryId={product.category_id || undefined}
          storiesGroups={storiesGroups}
          catalogStoriesEnabled={profile.catalog_stories_enabled ?? true}
        />
      </div>
    </>
  );

  return (
    <StoreLayout
      primaryColor={primaryColor}
      whatsappNumber={profile.whatsapp}
      cartScope={profile.id || slug}
      storeSlug={slug}
      theme={temaDaPagina}
      chatLigado={profile.store_chat_enabled === true}
      visao={visao}
    >
      <FontesDaLoja perfil={profile} />
      {temaDaPagina === "boutique" ? (
        /* A Boutique monta a página inteira com componentes próprios
           (templates/boutique). Nada do Padrão, do Vitrine nem do Vendor entra
           nesta árvore. Diferente do Vendor, leva o JSON-LD do produto (o
           Vendor não tem: lacuna 1.6.1 do PARIDADE). */
        <>
        <StructuredData data={dadosEstruturados} />
        <CatalogAnalyticsTracker
          slug={slug}
          eventName="page_view"
          productId={product.id}
          categoryId={product.category_id || null}
          onceKey={`product_page_view:${product.id}`}
        />
        <CatalogAnalyticsTracker
          slug={slug}
          eventName="product_view"
          productId={product.id}
          categoryId={product.category_id || null}
          onceKey={`product_view:${product.id}`}
        />
        <GuardarVisto slug={slug} produtoId={product.id} />
        <ProvedorDaNavegacao navegacao={dados.navegacao}>
        <BoutiqueShell
          slug={slug}
          nomeDaLoja={profile.catalog_name || profile.store_name || "Loja"}
          slogan={profile.catalog_slogan}
          /* A logo pela miniatura, como nas outras páginas (o Vendor passa a
             original: lacuna 1.6.10 do PARIDADE). */
          logoUrl={logoUrl}
          corDaMarca={primaryColor}
          aparencia={aparenciaDaBoutique(profile as never)}
          cupomDoAviso={selectedAnnouncementCoupon as never}
          whatsappFlutuante={profile.catalog_whatsapp_float}
          categoriaAtual={selectedCategory?.id || product.category_id || null}
          categorias={(categories || []) as never[]}
          contato={{
            whatsapp: profile.whatsapp,
            telefone: profile.company_phone || profile.phone,
            email: profile.email,
            endereco: profile.catalog_footer_address || profile.address,
            cnpj: profile.catalog_show_document ? profile.cnpj : null,
            instagram: profile.social_instagram,
            facebook: profile.social_facebook,
          }}
          clienteLogado={Boolean(loggedClient)}
        >
          {ehDigital && vitrineDigital ? (
            <BoutiquePaginaDoDigital
              slug={slug}
              produto={{ id: product.id, name: product.name, price: Number(product.price) || 0 }}
              imagens={imagensDoDigital.map((url, indice) => ({ id: `foto-${indice}`, image_url: url, is_primary: indice === 0 }))}
              categoriaNome={categoryName !== "Produto" ? categoryName : null}
              categoriaSlug={selectedCategory?.slug || null}
              descricaoHtml={productDescriptionHtml}
              config={vitrineDigital.config}
              prazoDias={vitrineDigital.prazoDias}
              meios={vitrineDigital.meios}
              acao={acaoDigital}
              entrega={entregaDigital}
              certificado={certificadoDoCurso}
              live={infoDaLive}
              mostrarPreco={visao.mostrarPreco}
              textoSemPreco={visao.textoNoLugarDoPreco}
              relacionados={relatedProducts as never[]}
              clienteId={loggedClient?.id || null}
              favoritos={loggedClient?.favorites || null}
              corDaMarca={primaryColor}
              notaMedia={averageRating}
              quantasAvaliacoes={approvedReviews.length}
              avaliacoes={
                !visao.regras.avaliacoes ? null : (
                <ProductReviewsSection
                  slug={slug}
                  productId={product.id}
                  loggedClient={loggedClient}
                  approvedReviews={approvedReviews}
                  ownReview={ownReview}
                  reviewCount={approvedReviews.length}
                  averageRating={averageRating}
                  loginHref={`/${slug}/cliente/login`}
                />
                )
              }
            />
          ) : (
          <BoutiqueProduto
            slug={slug}
            produto={product as never}
            imagens={((product as { product_images?: Array<{ id?: string; image_url: string; is_primary?: boolean | null }> }).product_images || []) as never[]}
            categoriaNome={categoryName !== "Produto" ? categoryName : null}
            categoriaSlug={selectedCategory?.slug || null}
            descricaoHtml={productDescriptionHtml}
            prazoTexto={prazoDaBoutique(
              (product as { production_time_value?: number | null }).production_time_value,
              (product as { production_time_unit?: "hours" | "days" | "business_days" | "weeks" | null }).production_time_unit
            )}
            previsaoTexto={previsaoDaBoutique(
              (product as { production_time_value?: number | null }).production_time_value,
              (product as { production_time_unit?: "hours" | "days" | "business_days" | "weeks" | null }).production_time_unit
            )}
            horaDoCorte={
              (profile as { production_cutoff_enabled?: boolean; production_cutoff_time?: string | null })
                .production_cutoff_enabled
                ? (profile as { production_cutoff_time?: string | null }).production_cutoff_time || null
                : null
            }
            especificacoes={[
              { rotulo: "Modelo", valor: product.name },
              /* O SKU e o prazo seguem as regras da loja; o tipo de preco
                 nao faz sentido para quem nao ve preco. */
              visao.regras.mostrarSku && (product as { sku?: string | null }).sku
                ? { rotulo: "SKU", valor: String((product as { sku?: string | null }).sku) }
                : null,
              categoryName !== "Produto" ? { rotulo: "Categoria", valor: categoryName } : null,
              visao.mostrarPreco
                ? {
                    rotulo: "Tipo de preço",
                    valor:
                      Array.isArray((product as { product_price_tiers?: unknown[] }).product_price_tiers) &&
                      ((product as { product_price_tiers?: unknown[] }).product_price_tiers || []).length > 0
                        ? product.unit_type === "square_meter"
                          ? "Por m², com desconto progressivo"
                          : "Por quantidade"
                        : product.price_on_request
                          ? visao.textoNoLugarDoPreco
                          : "Preço fixo",
                  }
                : null,
              {
                /* Metro linear tem nome próprio: no Vendor ele saía "Unidade"
                   (lacuna 1.6.5 do PARIDADE). */
                rotulo: "Unidade de venda",
                valor:
                  product.unit_type === "square_meter"
                    ? "Metro quadrado"
                    : product.unit_type === "linear_meter" || product.unit_type === "meter"
                      ? "Metro linear"
                      : "Unidade",
              },
              visao.regras.mostrarPrazo &&
              prazoDaBoutique(
                (product as { production_time_value?: number | null }).production_time_value,
                (product as { production_time_unit?: "hours" | "days" | "business_days" | "weeks" | null }).production_time_unit
              )
                ? {
                    rotulo: "Prazo de produção",
                    valor: String(
                      prazoDaBoutique(
                        (product as { production_time_value?: number | null }).production_time_value,
                        (product as { production_time_unit?: "hours" | "days" | "business_days" | "weeks" | null })
                          .production_time_unit
                      )
                    ),
                  }
                : null,
            ].filter(Boolean) as Array<{ rotulo: string; valor: string }>}
            relacionados={relatedProducts as never[]}
            corDaMarca={primaryColor}
            gruposDeServico={servicosDaPagina.grupos}
            servicosDoProduto={servicosDaPagina.servicos}
            combos={combosDaPagina || undefined}
            clienteId={loggedClient?.id || null}
            favoritos={loggedClient?.favorites || null}
            notaMedia={averageRating}
            quantasAvaliacoes={approvedReviews.length}
            selos={productTrustItems}
            whatsapp={profile.whatsapp}
            video={videoDaPagina}
            cupomDoAviso={selectedAnnouncementCoupon as never}
            avaliacoes={
              !visao.regras.avaliacoes ? null : (
              <ProductReviewsSection
                slug={slug}
                productId={product.id}
                loggedClient={loggedClient}
                approvedReviews={approvedReviews}
                ownReview={ownReview}
                reviewCount={approvedReviews.length}
                averageRating={averageRating}
                loginHref={`/${slug}/cliente/login`}
              />
              )
            }
          />
          )}
        </BoutiqueShell>
        </ProvedorDaNavegacao>
        </>
      ) : temaDaPagina === "vendor" ? (
        /* O Vendor monta a página inteira com componentes próprios. Nada do
           Padrão nem do Vitrine entra nesta árvore. */
        <>
        <CatalogAnalyticsTracker
          slug={slug}
          eventName="page_view"
          productId={product.id}
          categoryId={product.category_id || null}
          onceKey={`product_page_view:${product.id}`}
        />
        <CatalogAnalyticsTracker
          slug={slug}
          eventName="product_view"
          productId={product.id}
          categoryId={product.category_id || null}
          onceKey={`product_view:${product.id}`}
        />
        <GuardarVisto slug={slug} produtoId={product.id} />
        <ProvedorDaNavegacao navegacao={dados.navegacao}>
        <VendorShell
          slug={slug}
          nomeDaLoja={profile.catalog_name || profile.store_name || "Loja"}
          slogan={profile.catalog_slogan}
          logoUrl={profile.catalog_logo_url || profile.logo_url}
          corDaMarca={primaryColor}
          aparencia={aparenciaDoVendor(profile as never)}
          cupomDoAviso={selectedAnnouncementCoupon as never}
          whatsappFlutuante={profile.catalog_whatsapp_float}
          categoriaAtual={selectedCategory?.id || product.category_id || null}
          categorias={(categories || []) as never[]}
          contato={{
            whatsapp: profile.whatsapp,
            telefone: profile.company_phone || profile.phone,
            email: profile.email,
            endereco: profile.catalog_footer_address || profile.address,
            cnpj: profile.catalog_show_document ? profile.cnpj : null,
            instagram: profile.social_instagram,
            facebook: profile.social_facebook,
          }}
          clienteLogado={Boolean(loggedClient)}
        >
          {ehDigital && vitrineDigital ? (
            <VendorPaginaDoDigital
              slug={slug}
              produto={{ id: product.id, name: product.name, price: Number(product.price) || 0 }}
              imagens={imagensDoDigital.map((url, indice) => ({ id: `foto-${indice}`, image_url: url, is_primary: indice === 0 }))}
              categoriaNome={categoryName !== "Produto" ? categoryName : null}
              categoriaSlug={selectedCategory?.slug || null}
              descricaoHtml={productDescriptionHtml}
              config={vitrineDigital.config}
              prazoDias={vitrineDigital.prazoDias}
              meios={vitrineDigital.meios}
              acao={acaoDigital}
              entrega={entregaDigital}
              certificado={certificadoDoCurso}
              live={infoDaLive}
              mostrarPreco={visao.mostrarPreco}
              textoSemPreco={visao.textoNoLugarDoPreco}
              relacionados={relatedProducts as never[]}
              clienteId={loggedClient?.id || null}
              favoritos={loggedClient?.favorites || null}
              corDaMarca={primaryColor}
              notaMedia={averageRating}
              quantasAvaliacoes={approvedReviews.length}
              avaliacoes={
                !visao.regras.avaliacoes ? null : (
                <ProductReviewsSection
                  slug={slug}
                  productId={product.id}
                  loggedClient={loggedClient}
                  approvedReviews={approvedReviews}
                  ownReview={ownReview}
                  reviewCount={approvedReviews.length}
                  averageRating={averageRating}
                  loginHref={`/${slug}/cliente/login`}
                />
                )
              }
            />
          ) : (
          <VendorProduto
            slug={slug}
            produto={product as never}
            imagens={((product as { product_images?: Array<{ id?: string; image_url: string; is_primary?: boolean | null }> }).product_images || []) as never[]}
            categoriaNome={categoryName !== "Produto" ? categoryName : null}
            categoriaSlug={selectedCategory?.slug || null}
            descricaoHtml={productDescriptionHtml}
            prazoTexto={prazoDoVendor(
              (product as { production_time_value?: number | null }).production_time_value,
              (product as { production_time_unit?: "hours" | "days" | "business_days" | "weeks" | null }).production_time_unit
            )}
            previsaoTexto={previsaoDoVendor(
              (product as { production_time_value?: number | null }).production_time_value,
              (product as { production_time_unit?: "hours" | "days" | "business_days" | "weeks" | null }).production_time_unit
            )}
            horaDoCorte={
              (profile as { production_cutoff_enabled?: boolean; production_cutoff_time?: string | null })
                .production_cutoff_enabled
                ? (profile as { production_cutoff_time?: string | null }).production_cutoff_time || null
                : null
            }
            especificacoes={[
              { rotulo: "Modelo", valor: product.name },
              /* O SKU e o prazo seguem as regras da loja; o tipo de preco
                 nao faz sentido para quem nao ve preco. */
              visao.regras.mostrarSku && (product as { sku?: string | null }).sku
                ? { rotulo: "SKU", valor: String((product as { sku?: string | null }).sku) }
                : null,
              categoryName !== "Produto" ? { rotulo: "Categoria", valor: categoryName } : null,
              visao.mostrarPreco
                ? {
                    rotulo: "Tipo de preço",
                    valor:
                      Array.isArray((product as { product_price_tiers?: unknown[] }).product_price_tiers) &&
                      ((product as { product_price_tiers?: unknown[] }).product_price_tiers || []).length > 0
                        ? product.unit_type === "square_meter"
                          ? "Por m², com desconto progressivo"
                          : "Por quantidade"
                        : product.price_on_request
                          ? visao.textoNoLugarDoPreco
                          : "Preço fixo",
                  }
                : null,
              {
                rotulo: "Unidade de venda",
                valor: product.unit_type === "square_meter" ? "Metro quadrado" : "Unidade",
              },
              visao.regras.mostrarPrazo &&
              prazoDoVendor(
                (product as { production_time_value?: number | null }).production_time_value,
                (product as { production_time_unit?: "hours" | "days" | "business_days" | "weeks" | null }).production_time_unit
              )
                ? {
                    rotulo: "Prazo de produção",
                    valor: String(
                      prazoDoVendor(
                        (product as { production_time_value?: number | null }).production_time_value,
                        (product as { production_time_unit?: "hours" | "days" | "business_days" | "weeks" | null })
                          .production_time_unit
                      )
                    ),
                  }
                : null,
            ].filter(Boolean) as Array<{ rotulo: string; valor: string }>}
            relacionados={relatedProducts as never[]}
            corDaMarca={primaryColor}
            gruposDeServico={servicosDaPagina.grupos}
            servicosDoProduto={servicosDaPagina.servicos}
            combos={combosDaPagina || undefined}
            clienteId={loggedClient?.id || null}
            favoritos={loggedClient?.favorites || null}
            notaMedia={averageRating}
            quantasAvaliacoes={approvedReviews.length}
            selos={productTrustItems}
            whatsapp={profile.whatsapp}
            video={videoDaPagina}
            avaliacoes={
              !visao.regras.avaliacoes ? null : (
              <ProductReviewsSection
                slug={slug}
                productId={product.id}
                loggedClient={loggedClient}
                approvedReviews={approvedReviews}
                ownReview={ownReview}
                reviewCount={approvedReviews.length}
                averageRating={averageRating}
                loginHref={`/${slug}/cliente/login`}
              />
              )
            }
          />
          )}
        </VendorShell>
        </ProvedorDaNavegacao>
        </>
      ) : temaDaPagina === "vitrine" ? (
        <ProvedorDaNavegacao navegacao={dados.navegacao}>
        <StoreContent {...storeContentProps} isProductPage={true}>
          {productContent}
        </StoreContent>
        </ProvedorDaNavegacao>
      ) : (
        productContent
      )}
      {videoDaPagina?.flutuante ? (
        <VideoFlutuante
          video={videoDaPagina}
          produtoId={product.id}
          nome={product.name}
          capa={capaDoVideo}
          rodapeNoCelular={ehFamiliaVendor(temaDaPagina) ? 124 : 100}
          rodapeNoComputador={ehFamiliaVendor(temaDaPagina) ? 92 : 180}
        />
      ) : null}
    </StoreLayout>
  );
}
