"use client";

import { quantidadeDoItem, resumoDaMedida } from "@/lib/orders/quantidade-do-item";
import CaixaDeConfirmacao, { type Confirmacao } from "@/components/ui/caixa-de-confirmacao";
import { usePermissoes } from "@/lib/permissoes-do-usuario";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import type { RealtimeChannel } from "@supabase/supabase-js";
import Link from "next/link";
import { useParams, useRouter, useSearchParams } from "next/navigation";
import Image from "next/image";
import { getSupabaseClient } from "@/lib/supabase";
import { getCompanyContext } from "@/lib/company-context";
import { useConferenciaDoEstoque, useEstoqueV2Ligado } from "@/lib/use-estoque-da-venda";
import { ArrowDown, ArrowLeft, ArrowUp, Check, Copy, FileDown, Loader2, Lock, Plus, Save, Send, Trash2, MessageCircle, PackageCheck, Unlock, X } from "lucide-react";
import { BotaoDoDesignNoItem } from "@/components/design/botao-criar-arte";
import { artesDoOrcamento } from "@/lib/banner-editor/artes";
import { CamposDoFormulario, type ValoresEmEdicao } from "@/components/campos-proprios/campos-do-formulario";
import { useCamposProprios } from "@/components/campos-proprios/use-campos-proprios";
import { conferirValores, valoresDaLinha } from "@/lib/campos-proprios";
import { JanelaDasMetricas, useMetricasDoLink } from "@/components/quotes/metricas-do-link/janela";
import { JanelaDaChance, useChanceDeFechar } from "@/components/quotes/chance-de-fechar/janela";
import { FaixaDoOrcamento } from "@/components/quotes/metricas-do-link/faixa-do-orcamento";
import { registrarEnvioDoLink } from "@/lib/envio-do-link";
import { diasDeValidade, prazoDoLink } from "@/lib/prazo-do-link";
import { CustomSelect } from "@/components/ui/custom-select";
import { InfoTooltip } from "@/components/ui/info-tooltip";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Switch } from "@/components/ui/switch";
import { formatMoneyInput, parseMoneyInput, formatMeasureInput, parseMeasureInput, formatPercentInput, parsePercentInput } from "@/utils/formatters";
import { ShippingCalculator, ShippingOption } from "@/components/shipping-calculator";
import { checkSaleQuantityLimits, type ProductPriceTier } from "@/lib/price-tiers";
import { precoDaLinha, somarAdicionais, SEM_ADICIONAIS, valorMinimoDoItem, ROTULO_DO_MINIMO, COLUNAS_DO_VALOR_MINIMO, type OpcaoComPreco, type ResultadoDaLinha } from "@/lib/adicionais";
import { AvisoDoMinimo } from "@/components/quotes/aviso-do-minimo";
import { fetchWhatsAppTemplates, resolveTemplate } from "@/lib/whatsapp-templates-client";
import { ClientFormModal } from "@/components/clients/client-form-modal";
import CargaDoDia from "@/components/agenda/carga-do-dia";
import { ChaveDasCondicoes, InterruptorDasCondicoes, CondicoesComerciaisEditor, CondicoesParaImpressao } from "@/components/quotes/condicoes-comerciais";
import {
  erroDasCondicoes,
  normalizarCondicoes,
  padraoDasCondicoes,
  resumirCondicoes,
  type CondicoesComerciais,
} from "@/lib/condicoes-comerciais";
import { condicoesDoPadrao, lerModelos, type ModeloDeCondicoes } from "@/lib/modelos-de-condicoes";
import {
  BlocoListaDePreco,
  BotaoAdicionarItem,
  CARTAO_EM_ETAPAS,
  CORPO_DO_ITEM_EM_ETAPAS,
  CabecalhoDoCartao,
  CabecalhoDoItem,
  Chip,
  ContaDoItem,
  EtapaFinanceiro,
  Etapas,
  NavegacaoDasEtapas,
  OPCOES_DE_STATUS,
  RodapeDoItem,
  SeletorDeModelo,
  classesDoItemEmEtapas,
  descreverQuantidade,
  etapaDoErro,
  nomeDaUnidade,
  useItemAberto,
  useModeloDeOrcamento,
} from "@/components/quotes/orcamento-em-etapas";

const passthroughLoader = ({ src }: { src: string }) => src;

/* Telefone e WhatsApp: número que vem só com dígitos (com ou sem +55) ganha
   máscara (DD) 9XXXX-XXXX; o que já veio escrito fica como está. */
function formatarTelefone(valor: string | null | undefined) {
  const texto = (valor || "").trim();
  if (!texto || !/^\+?\d+$/.test(texto)) return texto;
  let digitos = texto.replace(/\D/g, "");
  if ((digitos.length === 12 || digitos.length === 13) && digitos.startsWith("55")) digitos = digitos.slice(2);
  if (digitos.length === 11) return digitos.replace(/(\d{2})(\d{5})(\d{4})/, "($1) $2-$3");
  if (digitos.length === 10) return digitos.replace(/(\d{2})(\d{4})(\d{4})/, "($1) $2-$3");
  return texto;
}

/* Os contatos da empresa numa linha só na folha do orçamento, sem repetir
   telefone e WhatsApp quando são o mesmo número. */
function contatosDaEmpresa(telefone?: string | null, whatsapp?: string | null, email?: string | null) {
  const tel = formatarTelefone(telefone);
  const zap = formatarTelefone(whatsapp);
  const partes: string[] = [];
  if (tel && zap && tel === zap) partes.push(`Tel./WhatsApp ${tel}`);
  else {
    if (tel) partes.push(`Tel. ${tel}`);
    if (zap) partes.push(`WhatsApp ${zap}`);
  }
  const mail = (email || "").trim();
  if (mail) partes.push(mail);
  return partes.join("  ·  ");
}

/* CPF e CNPJ com máscara na folha; qualquer outra coisa sai como veio. */
function formatarDocumento(valor: string | null | undefined) {
  const digitos = (valor || "").replace(/\D/g, "");
  if (digitos.length === 11) return `CPF ${digitos.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4")}`;
  if (digitos.length === 14) return `CNPJ ${digitos.replace(/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/, "$1.$2.$3/$4-$5")}`;
  const texto = (valor || "").trim();
  return texto ? `Documento ${texto}` : "";
}

/* Rótulos e títulos de seção da folha do orçamento. */
const ROTULO_DO_DOCUMENTO = "mb-1 text-[9.5px] font-bold uppercase tracking-[0.14em] text-zinc-500";
const TITULO_DE_SECAO_DO_DOCUMENTO =
  "mb-1.5 border-b border-zinc-300 pb-1 text-[10px] font-bold uppercase tracking-[0.14em] text-zinc-600";

type QuoteStatus = "draft" | "sent" | "approved" | "rejected" | "production" | "delivered";

interface ClientOption {
  id: string;
  name: string;
  company_name: string | null;
  whatsapp: string | null;
  document?: string | null;
  address_zip: string | null;
  is_deleted?: boolean | null;
}

interface QuoteRow {
  id: string;
  created_at: string;
  updated_at: string;
  user_id: string;
  client_id: string;
  number: number;
  status: QuoteStatus;
  title: string | null;
  currency: string;
  subtotal: number;
  discount: number;
  shipping: number;
  shipping_details?: ShippingOption | null;
  total: number;
  expires_at: string | null;
  delivery_date: string | null;
  notes: string | null;
  internal_notes: string | null;
  list_price_mode?: boolean | null;
  payment_terms?: unknown;
  share_enabled: boolean;
  share_token: string | null;
  share_expires_at: string | null;
  public_view_count: number | null;
  public_first_viewed_at: string | null;
  public_last_viewed_at: string | null;
  created_by?: string | null;
  orders?: {
    id: string;
    number: number;
  } | null;
}

type QuoteWithOrdersArray = QuoteRow & {
  orders: QuoteRow["orders"][] | QuoteRow["orders"] | null;
};

interface ProductOption {
  id: string;
  name: string;
  price: number;
  /* Produto digital: continua aqui para o item que veio da loja, mas não se
     escolhe para item novo (é vendido pela área Produtos digitais). */
  product_kind?: string | null;
  price_on_request?: boolean | null;
  use_variation_price_only?: boolean | null;
  cost_total: number;
  stock?: number | null;
  unit_type: string | null;
  image_url: string | null;
  product_price_tiers?: ProductPriceTier[] | 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;
  inventory_scope?: "product" | "option" | "variant" | null;
  inventory_attribute_name?: string | null;
  allow_negative_stock?: boolean | null;
  track_inventory?: boolean | null;
  product_attributes?: ProductAttributeValue[] | null;
  product_variations?: ProductVariationValue[] | null;
  valor_minimo_item?: number | null;
  valor_minimo_orcamentos?: boolean | null;
  valor_minimo_loja?: boolean | null;
  valor_minimo_pdv?: boolean | null;
}

interface ProductAttributeOptionValue {
  id?: string | null;
  label: string;
  price?: number;
  price_applies_per_quantity?: boolean | null;
  price_unit?: string | null;
  stock?: number;
  is_required?: boolean;
  position?: number;
}

interface ProductAttributeValue {
  id?: string | null;
  name: string;
  allow_multiple?: boolean | null;
  is_required?: boolean | null;
  position?: number | null;
  options: Array<string | ProductAttributeOptionValue>;
}

interface ProductVariationValue {
  id?: string | null;
  combination?: Record<string, string> | null;
  stock?: number | null;
}

interface QuoteItemRow {
  id: string;
  quote_id: string;
  position: number;
  product_id: string | null;
  unit_type: string | null;
  width_m: number | null;
  height_m: number | null;
  length_m: number | null;
  title: string;
  description: string | null;
  quantity: number;
  unit_price: number;
  unit_cost: number;
  line_total: number;
  selected_options?: Record<string, unknown> | null;
  valor_minimo_aplicado?: number | string | null;
  valor_sem_minimo?: number | string | null;
}

interface QuoteItemArt {
  id?: string;
  quote_id: string;
  quote_item_id: string;
  title: string;
  notes: string;
  image_urls: string[];
  approval_status?: string | null;
  approval_notes?: string | null;
  approved_at?: string | null;
  reviewed_at?: string | null;
  created_at?: string;
}

interface QuoteItemArtMessage {
  id: string;
  quote_id: string;
  quote_item_id: string;
  art_id: string;
  sender: "client" | "company";
  message: string;
  status?: string | null;
  created_at?: string | null;
  delivered_at?: string | null;
  read_at?: string | null;
}

interface QuoteItemDraft {
  id: string;
  product_id: string;
  title: string;
  description: string;
  unit_type: string;
  width_m: number;
  height_m: number;
  length_m: number;
  quantity: number;
  unit_price: number;
  unit_cost: number;
  unit_price_locked: boolean;
  selected_options?: Record<string, unknown> | null;
  /** O valor mínimo do item pegou nesta linha: a linha vale `minimo`. */
  minimo?: { minimo: number; semMinimo: number | null } | null;
}

/** O que a linha vale: o mínimo do item quando pegou; senão quantidade × preço. */
function totalDaLinhaDoRascunho(it: QuoteItemDraft, quantidadeEfetiva: number): number {
  if (it.minimo && !it.unit_price_locked) return it.minimo.minimo;
  return quantidadeEfetiva * (it.unit_price || 0);
}

function minimoDoResultado(linha: ResultadoDaLinha): QuoteItemDraft["minimo"] {
  if (linha.minimoAplicado == null) return null;
  return { minimo: linha.minimoAplicado, semMinimo: linha.totalSemMinimo };
}

type MeasureFieldKey = "width_m" | "height_m" | "length_m";
type MeasureDraft = Record<MeasureFieldKey, string>;
type OrderGenerationStockAlert = {
  itemLabel: string;
  message: string;
  allowNegative: boolean;
};

function normalizeDigits(value: string) {
  return value.replace(/\D/g, "");
}

function formatDateBr(value: string | null | undefined) {
  if (!value) return "";
  const normalized = value.split("T")[0];
  const parts = normalized.split("-");
  if (parts.length === 3 && parts[0].length === 4) {
    const [y, m, d] = parts;
    return `${d}/${m}/${y}`;
  }
  const dt = new Date(value);
  if (!Number.isFinite(dt.getTime())) return normalized;
  return dt.toLocaleDateString("pt-BR");
}

function formatDateTimeBr(value: string | null | undefined) {
  if (!value) return "";
  const date = new Date(value);
  if (!Number.isFinite(date.getTime())) return "";
  return date.toLocaleString("pt-BR", { dateStyle: "short", timeStyle: "short" });
}

function formatMoney(value: number) {
  return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(value || 0);
}

function normalizeAttributeOption(option: string | ProductAttributeOptionValue, index: number) {
  if (typeof option === "string") {
    return {
      id: null,
      label: option,
      price: 0,
      price_applies_per_quantity: true,
      price_unit: null,
      stock: null,
      is_required: false,
      position: index,
    };
  }
  return {
    id: option.id ?? null,
    label: option.label,
    price: Number(option.price) || 0,
    price_applies_per_quantity: option.price_applies_per_quantity !== false,
    price_unit: typeof option.price_unit === "string" ? option.price_unit : null,
    stock: Number.isFinite(Number(option.stock)) ? Number(option.stock) : null,
    is_required: Boolean(option.is_required),
    position: typeof option.position === "number" ? option.position : index,
  };
}

function getSortedAttributes(product: ProductOption | null) {
  return (product?.product_attributes || [])
    .filter((attribute) => Boolean(attribute?.name))
    .slice()
    .sort((a, b) => Number(a.position ?? 0) - Number(b.position ?? 0));
}

function getItemVariationMap(item: QuoteItemDraft) {
  const selectedOptions = item.selected_options as { options?: unknown } | null | undefined;
  const options = Array.isArray(selectedOptions?.options) ? selectedOptions.options : [];
  const map: Record<string, string[]> = {};
  for (const raw of options) {
    if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
    const row = raw as Record<string, unknown>;
    const attributeName = typeof row.attribute_name === "string" ? row.attribute_name : "";
    const label = typeof row.label === "string" ? row.label : "";
    if (!attributeName || !label) continue;
    const list = map[attributeName] || [];
    if (!list.includes(label)) list.push(label);
    map[attributeName] = list;
  }
  return map;
}

function findMatchedVariation(product: ProductOption, selectedMap: Record<string, string[]>) {
  const selectedFlat: Record<string, string> = {};
  for (const [attribute, labels] of Object.entries(selectedMap)) {
    const label = labels[0];
    if (!label) continue;
    selectedFlat[attribute.trim().toLowerCase()] = label.trim().toLowerCase();
  }
  const selectedKeys = Object.keys(selectedFlat).sort();
  if (!selectedKeys.length) return null;

  const variations = (product.product_variations || []).filter((variation) => variation && variation.combination);
  for (const variation of variations) {
    const combinationRaw = variation.combination || {};
    const combination: Record<string, string> = {};
    for (const [attribute, label] of Object.entries(combinationRaw)) {
      combination[attribute.trim().toLowerCase()] = (label || "").trim().toLowerCase();
    }
    const combinationKeys = Object.keys(combination).sort();
    if (combinationKeys.length !== selectedKeys.length) continue;
    const same = selectedKeys.every((key) => combination[key] === selectedFlat[key]);
    if (!same) continue;
    return variation;
  }
  return null;
}

function buildSelectedOptionsPayload(
  product: ProductOption,
  selectionMap: Record<string, string[]>
) {
  const attributes = getSortedAttributes(product);
  const inventoryScope = product.inventory_scope || "product";
  const inventoryAttributeName = (product.inventory_attribute_name || "").trim().toLowerCase();
  const payloadOptions: Array<{
    option_id: string | null;
    attribute_name: string;
    label: string;
    price: number;
    price_applies_per_quantity: boolean;
    price_unit?: string | null;
    affects_stock: boolean;
  }> = [];

  for (const attribute of attributes) {
    const normalizedOptions = (attribute.options || [])
      .map((option, index) => normalizeAttributeOption(option, index))
      .sort((a, b) => Number(a.position ?? 0) - Number(b.position ?? 0));
    const selectedLabels = selectionMap[attribute.name] || [];
    for (const selectedLabel of selectedLabels) {
      const selectedOption = normalizedOptions.find((option) => option.label === selectedLabel);
      if (!selectedOption) continue;
      payloadOptions.push({
        option_id: selectedOption.id || null,
        attribute_name: attribute.name,
        label: selectedOption.label,
        price: Number(selectedOption.price) || 0,
        price_applies_per_quantity: selectedOption.price_applies_per_quantity !== false,
        price_unit: selectedOption.price_unit ?? null,
        affects_stock:
          inventoryScope === "option" &&
          attribute.name.trim().toLowerCase() === inventoryAttributeName,
      });
    }
  }

  if (!payloadOptions.length) return null;

  return {
    inventory_scope: inventoryScope,
    inventory_attribute_name: inventoryScope === "option" ? product.inventory_attribute_name || null : null,
    options: payloadOptions,
  };
}

function getSelectedOptionsSummary(item: QuoteItemDraft) {
  const map = getItemVariationMap(item);
  const parts = Object.entries(map)
    .filter(([, labels]) => labels.length > 0)
    .map(([attribute, labels]) => `${attribute}: ${labels.join(", ")}`);
  return parts.join(" / ");
}

function getSelectedOptionsPriceBreakdown(item: QuoteItemDraft, product?: ProductOption | null) {
  if (!product) return SEM_ADICIONAIS;
  const selectedMap = getItemVariationMap(item);
  const attributes = getSortedAttributes(product);
  const escolhidas: OpcaoComPreco[] = [];
  for (const attribute of attributes) {
    const selectedLabels = selectedMap[attribute.name] || [];
    if (!selectedLabels.length) continue;
    const normalizedOptions = (attribute.options || [])
      .map((option, index) => normalizeAttributeOption(option, index))
      .sort((a, b) => Number(a.position ?? 0) - Number(b.position ?? 0));
    for (const selectedLabel of selectedLabels) {
      const selectedOption = normalizedOptions.find((option) => option.label === selectedLabel);
      if (!selectedOption) continue;
      escolhidas.push(selectedOption);
    }
  }
  return somarAdicionais(escolhidas, product.unit_type);
}

function getItemVariationStockMessage(item: QuoteItemDraft, product?: ProductOption | null) {
  if (!product) return null;
  if (product.track_inventory === false) return null;
  const inventoryScope = product.inventory_scope || "product";
  if (inventoryScope === "product") {
    const available = Number(product.stock) || 0;
    const required = effectiveQuantity(item);
    if (available <= 0) {
      return "Sem estoque para o produto selecionado.";
    }
    if (required > available + 0.000001) {
      return `Estoque insuficiente para o produto selecionado. Disponível: ${formatQuantityLimit(available)}.`;
    }
    return null;
  }
  if (inventoryScope === "variant") {
    const selectedMap = getItemVariationMap(item);
    const hasMulti = Object.values(selectedMap).some((labels) => labels.length > 1);
    if (hasMulti) {
      return "Estoque por combinação exige apenas uma opção por atributo.";
    }
    const expectedAttributes = getSortedAttributes(product).filter((attribute) => (attribute.options || []).length > 0);
    const missingAttributes = expectedAttributes.filter((attribute) => !(selectedMap[attribute.name] || [])[0]);
    if (missingAttributes.length > 0) {
      return `Selecione uma combinação completa (${missingAttributes.map((attribute) => attribute.name).join(", ")}).`;
    }
    const variation = findMatchedVariation(product, selectedMap);
    if (!variation) {
      return "A combinação selecionada não existe no cadastro de variações.";
    }
    const available = Number(variation.stock) || 0;
    const required = effectiveQuantity(item);
    if (available <= 0) {
      return "Sem estoque para a combinação selecionada.";
    }
    if (required > available + 0.000001) {
      return `Estoque insuficiente para a combinação selecionada. Disponível: ${formatQuantityLimit(available)}.`;
    }
    return null;
  }
  const inventoryAttributeName = (product.inventory_attribute_name || "").trim();
  if (!inventoryAttributeName) {
    return "Produto configurado com estoque por opção sem atributo de estoque definido.";
  }
  const attributes = getSortedAttributes(product);
  const stockAttribute =
    attributes.find((attribute) => attribute.name.trim().toLowerCase() === inventoryAttributeName.toLowerCase()) || null;
  if (!stockAttribute) {
    return `Atributo de estoque (${inventoryAttributeName}) não encontrado no produto.`;
  }
  const selectedMap = getItemVariationMap(item);
  const selectedEntry =
    Object.entries(selectedMap).find(([name]) => name.trim().toLowerCase() === stockAttribute.name.trim().toLowerCase()) ||
    null;
  const selectedLabels = selectedEntry?.[1] || [];
  if (selectedLabels.length === 0) {
    return `Selecione uma opção de ${stockAttribute.name} para controlar o estoque deste item.`;
  }

  const normalizedOptions = (stockAttribute.options || [])
    .map((option, index) => normalizeAttributeOption(option, index))
    .sort((a, b) => Number(a.position ?? 0) - Number(b.position ?? 0));
  const selectedOptions = selectedLabels
    .map((label) => normalizedOptions.find((option) => option.label === label) || null)
    .filter((option): option is NonNullable<(typeof normalizedOptions)[number]> => option !== null);
  if (!selectedOptions.length) {
    return `As opções selecionadas de ${stockAttribute.name} não existem mais no cadastro do produto.`;
  }

  const trackedStocks = selectedOptions
    .map((option) => (typeof option.stock === "number" ? option.stock : null))
    .filter((value): value is number => value !== null);
  if (!trackedStocks.length) return null;

  const available = Math.min(...trackedStocks);
  const required = effectiveQuantity(item);
  if (available <= 0) {
    return `Sem estoque para ${stockAttribute.name}: ${selectedLabels.join(", ")}.`;
  }
  if (required > available + 0.000001) {
    return `Estoque insuficiente para ${stockAttribute.name}: ${selectedLabels.join(", ")}. Disponível: ${formatQuantityLimit(available)}.`;
  }
  return null;
}

function effectiveQuantity(item: Pick<QuoteItemDraft, "quantity" | "unit_type" | "width_m" | "height_m" | "length_m">) {
  const quantity = item.quantity || 0;
  if (quantity <= 0) return 0;

  if (item.unit_type === "square_meter") {
    const area = (item.width_m || 0) * (item.height_m || 0);
    return area > 0 ? quantity * area : quantity;
  }

  if (item.unit_type === "linear_meter" || item.unit_type === "meter") {
    const length = item.length_m || 0;
    return length > 0 ? quantity * length : quantity;
  }

  return quantity;
}


function formatQuantityLimit(value: number | null) {
  if (value === null || !Number.isFinite(value)) return "0";
  const rounded = Number(value.toFixed(3));
  return Number.isInteger(rounded)
    ? String(rounded)
    : new Intl.NumberFormat("pt-BR", { minimumFractionDigits: 0, maximumFractionDigits: 3 }).format(rounded);
}

function getMinimumItemQuantity(product?: ProductOption | null) {
  if (!product) return 1;
  const allowedQuantities = getAllowedItemQuantities(product);
  if (Boolean(product.allowed_sale_qty_enabled) && allowedQuantities.length > 0) {
    return Number(allowedQuantities[0].toFixed(3));
  }
  if (Boolean(product.min_sale_qty_enabled)) {
    const minQty = Number(product.min_sale_qty);
    if (Number.isFinite(minQty) && minQty > 0) return Number(minQty.toFixed(3));
  }
  return 1;
}

function getAllowedItemQuantities(product?: ProductOption | null) {
  if (!product) return [] as number[];
  return Array.isArray(product.allowed_sale_quantities)
    ? product.allowed_sale_quantities
        .map((value) => Number(value))
        .filter((value) => Number.isFinite(value) && value > 0)
        .sort((a, b) => a - b)
    : [];
}

function generateShareToken() {
  const bytes = new Uint8Array(24);
  crypto.getRandomValues(bytes);
  return Array.from(bytes)
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

/* O prazo do link mora em lib/prazo-do-link.ts: a mesma conta da lista de
   orçamentos e da revisão rápida do Dashboard. */
const resolveShareExpiry = prazoDoLink;

function loadImageFromFile(file: File) {
  return new Promise<HTMLImageElement>((resolve, reject) => {
    const url = URL.createObjectURL(file);
    const img = new window.Image();
    img.onload = () => {
      URL.revokeObjectURL(url);
      resolve(img);
    };
    img.onerror = () => {
      URL.revokeObjectURL(url);
      reject(new Error("Falha ao carregar imagem"));
    };
    img.src = url;
  });
}

async function compressToWebp(file: File, maxSize = 2000, quality = 0.82) {
  const img = await loadImageFromFile(file);
  const scale = Math.min(1, maxSize / Math.max(img.width, img.height));
  const width = Math.max(1, Math.round(img.width * scale));
  const height = Math.max(1, Math.round(img.height * scale));
  const canvas = document.createElement("canvas");
  canvas.width = width;
  canvas.height = height;
  const ctx = canvas.getContext("2d");
  if (!ctx) throw new Error("Canvas indisponível");
  ctx.drawImage(img, 0, 0, width, height);
  const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, "image/webp", quality));
  if (!blob) throw new Error("Falha ao converter imagem");
  const baseName = file.name.replace(/\.[^/.]+$/, "");
  return new File([blob], `${baseName}.webp`, { type: "image/webp" });
}

interface ProfileRow {
  id: string;
  slug: string | null;
  store_name: string | null;
  catalog_name: string | null;
  logo_url: string | null;
  catalog_logo_url: string | null;
  cnpj: string | null;
  phone: string | null;
  company_phone: string | null;
  company_whatsapp: string | null;
  company_email: string | null;
  address: string | null;
  whatsapp: string | null;
  quote_share_link_expires_enabled?: boolean | null;
  quote_share_link_expires_days?: number | null;
  quote_default_notes_enabled?: boolean | null;
  quote_default_notes?: string | null;
  quote_default_payment_terms?: unknown;
  quote_payment_terms_templates?: unknown;
}

export default function QuoteDetailsPage() {
  const { pode } = usePermissoes();
  const podeMexerEmOrcamentos = pode("quotes.write");
  /* Custo e margem sao dado comercial. O preco de venda nao passa por
     aqui: definir preco e parte de fazer o orcamento. */
  const podeVerCustos = pode("pricing.read");

  const params = useParams<{ id: string }>();
  const router = useRouter();
  const searchParams = useSearchParams();
  const quoteId = params.id;

  // Make the page full-width while on this route
  useEffect(() => {
    const ksPage = document.querySelector('.ks-page') as HTMLElement | null;
    if (ksPage) {
      ksPage.style.maxWidth = 'none';
    }
    return () => {
      if (ksPage) {
        ksPage.style.maxWidth = '';
      }
    };
  }, []);

  const [clients, setClients] = useState<ClientOption[]>([]);
  const [products, setProducts] = useState<ProductOption[]>([]);
  const [quote, setQuote] = useState<QuoteRow | null>(null);
  const [items, setItems] = useState<QuoteItemDraft[]>([]);

  /*
    Estoque v2: o orçamento conferido no banco a cada mudança
    (estoque_conferir_itens — a mesma conta da loja e do PDV, por combinação
    e somando por célula). Na linha é só aviso; "Gerar pedido" para quando o
    banco recusaria (item que não permite negativo sem saldo, ou combinação
    sem célula). Fora do v2 tudo segue como sempre.
  */
  const supabaseDoOrcamento = useMemo(() => getSupabaseClient(), []);
  const estoqueV2 = useEstoqueV2Ligado(supabaseDoOrcamento, quote?.user_id);
  const linhasParaOEstoque = useMemo(
    () =>
      items.map((it) => ({
        product_id: it.product_id || null,
        quantity: it.quantity,
        selected_options: it.selected_options ?? null,
        width_m: it.width_m || null,
        height_m: it.height_m || null,
        title: it.title,
      })),
    [items]
  );
  const conferenciaDoEstoqueV2 = useConferenciaDoEstoque({
    supabase: supabaseDoOrcamento,
    dono: quote?.user_id,
    linhas: linhasParaOEstoque,
    ativo: estoqueV2 === true,
  });
  const [activeItemId, setActiveItemId] = useState<string | null>(null);
  const [quantityDrafts, setQuantityDrafts] = useState<Record<string, string>>({});
  const [manualProductSubmittingItemId, setManualProductSubmittingItemId] = useState<string | null>(null);
  const [manualProductErrors, setManualProductErrors] = useState<Record<string, string>>({});
  const [measureDrafts, setMeasureDrafts] = useState<Record<string, MeasureDraft>>({});
  const [itemArts, setItemArts] = useState<Record<string, QuoteItemArt>>({});
  /* O KtaloguDesign no item: os itens que já estão no banco (a arte só se liga a item guardado) e a arte mais nova de cada item. */
  const [itensGuardados, setItensGuardados] = useState<Set<string>>(() => new Set());
  const [artesDoDesign, setArtesDoDesign] = useState<Record<string, string>>({});
  /*
   * As imagens de arte que esta tela já conhece. As que aparecem no banco e
   * não estão aqui chegaram de fora (o "Enviar para o orçamento" do
   * KtaloguDesign, em outra aba): entram na tela e no salvar, sem trazer de
   * volta as que a pessoa tirou aqui.
   */
  const urlsConhecidas = useRef<Set<string>>(new Set());
  const [itemArtPdfVisibility, setItemArtPdfVisibility] = useState<Record<string, boolean>>({});
  const [artMessages, setArtMessages] = useState<Record<string, QuoteItemArtMessage[]>>({});
  const [artMessageDrafts, setArtMessageDrafts] = useState<Record<string, string>>({});
  const [artMessageSending, setArtMessageSending] = useState<Record<string, boolean>>({});
  const [chatOpen, setChatOpen] = useState(false);
  const [chatTabs, setChatTabs] = useState<Array<{ artId: string; itemId: string; label: string }>>([]);
  const [activeChatArtId, setActiveChatArtId] = useState<string | null>(null);
  const [profile, setProfile] = useState<ProfileRow | null>(null);
  /* As condições comerciais do orçamento (null = a proposta sai sem elas) e o
     padrão da empresa, que mora no perfil do dono. */
  const [condicoes, setCondicoes] = useState<CondicoesComerciais | null>(null);
  /* Campos próprios do orçamento (migration 187): valores em bruto, como a
     pessoa digitou; conferidos no salvar. */
  const { definicoes: camposDoOrcamento, ativas: camposAtivosDoOrcamento } = useCamposProprios("orcamento");
  const [camposValores, setCamposValores] = useState<ValoresEmEdicao>({});
  const [camposErros, setCamposErros] = useState<Record<string, string>>({});
  /* O que estava preenchido quando a chave foi desligada: ligar de novo devolve. */
  const condicoesGuardadas = useRef<CondicoesComerciais | null>(null);
  const alternarCondicoes = (ligar: boolean) => {
    if (ligar) {
      setCondicoes(condicoesGuardadas.current ?? condicoesDoPadrao(modelosDaEmpresa) ?? padraoDasCondicoes());
    } else {
      condicoesGuardadas.current = condicoes;
      setCondicoes(null);
    }
  };
  /* Os modelos da empresa; o marcado como padrão é o ponto de partida de
     todo orçamento novo (lib/modelos-de-condicoes.ts). */
  const [modelosDaEmpresa, setModelosDaEmpresa] = useState<ModeloDeCondicoes[]>([]);
  const [ehDonoDaEmpresa, setEhDonoDaEmpresa] = useState(false);
  const [profileCustomDomain, setProfileCustomDomain] = useState<string | null>(null);
  const [profileCustomDomainVerified, setProfileCustomDomainVerified] = useState(false);
  const [shippingDetails, setShippingDetails] = useState<ShippingOption | null>(null);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [sharing, setSharing] = useState(false);
  const [error, setError] = useState("");
  const [copied, setCopied] = useState(false);
  /* As métricas do link: o número do cartão vem daqui (só o cliente, sem as
     aberturas da própria equipe), e a janela pergunta de novo a cada 20 s
     enquanto está aberta. */
  const [metricasAbertas, setMetricasAbertas] = useState(false);
  const metricasDoLink = useMetricasDoLink(quote?.share_enabled ? quote.id : null, {
    ativo: Boolean(quote?.id && quote?.share_enabled),
    aoVivo: metricasAbertas,
  });
  const fecharMetricas = useCallback(() => setMetricasAbertas(false), []);
  /* A chance de fechar no geral (empresa, cliente, valor e tempo aberto), a
     da faixa. Recalcula quando o orçamento muda. */
  const [chanceAberta, setChanceAberta] = useState(false);
  const chanceDeFechar = useChanceDeFechar(
    quote?.id ?? null,
    `${quote?.updated_at ?? ""}|${quote?.status ?? ""}|${quote?.total ?? ""}|${quote?.orders ? "pedido" : ""}`
  );
  const fecharChance = useCallback(() => setChanceAberta(false), []);
  /* Copiado ou mandado agora, nesta tela: a faixa deixa de dizer "Ainda não
     enviado" sem esperar a volta das métricas. */
  const [linkEnviadoAqui, setLinkEnviadoAqui] = useState(false);
  const [generatingOrder, setGeneratingOrder] = useState(false);
  /* Excluir e cancelar passaram a usar a caixa do sistema. O `confirm()` do
     navegador some sem avisar depois que a página abre algumas caixas
     seguidas, e o Excluir virava um botão que não fazia nada. */
  const [confirmacao, setConfirmacao] = useState<Confirmacao | null>(null);
  const [excluindo, setExcluindo] = useState(false);
  const [erroDaExclusao, setErroDaExclusao] = useState<string | null>(null);
  const [duplicating, setDuplicating] = useState(false);
  const [showDuplicatedNotice, setShowDuplicatedNotice] = useState(false);
  const [clientModalOpen, setClientModalOpen] = useState(false);
  const [orderErrorModal, setOrderErrorModal] = useState<{ open: boolean; message: string }>({
    open: false,
    message: "",
  });
  const [orderNegativeStockModal, setOrderNegativeStockModal] = useState<{
    open: boolean;
    alerts: OrderGenerationStockAlert[];
  }>({
    open: false,
    alerts: [],
  });
  const [printAssetReady, setPrintAssetReady] = useState(false);
  const [printPreview, setPrintPreview] = useState(false);

  const [creatorName, setCreatorName] = useState<string | null>(null);
  const [artModalOpen, setArtModalOpen] = useState(false);
  const [activeArtItemId, setActiveArtItemId] = useState<string | null>(null);
  const [artDraft, setArtDraft] = useState<QuoteItemArt | null>(null);
  const [artUploading, setArtUploading] = useState(false);
  const [artUploadProgress, setArtUploadProgress] = useState<{ total: number; completed: number } | null>(null);
  const [artError, setArtError] = useState("");
  const pendingItemFocusRef = useRef<string | null>(null);
  const [previewOpen, setPreviewOpen] = useState(false);
  const [previewImageUrl, setPreviewImageUrl] = useState<string | null>(null);
  const artFileInputRef = useRef<HTMLInputElement | null>(null);
  const [autoOpenArtPicker, setAutoOpenArtPicker] = useState(false);
  const chatChannelRef = useRef<RealtimeChannel | null>(null);
  const readIdsRef = useRef<Set<string>>(new Set());
  const openedChatFromQueryRef = useRef(false);
  const existingItemIdsRef = useRef<Set<string>>(new Set());
  const [artNeedsNotify, setArtNeedsNotify] = useState<Record<string, boolean>>({});

  useEffect(() => {
    if (searchParams.get("duplicated") !== "1") return;
    setShowDuplicatedNotice(true);
    const timeout = window.setTimeout(() => setShowDuplicatedNotice(false), 3500);
    router.replace(`/dashboard/quotes/${quoteId}`);
    return () => window.clearTimeout(timeout);
  }, [searchParams, router, quoteId]);

  const isReadOnly = quote?.status === "rejected";
  const isQuoteFinalized =
    Boolean(quote?.orders) ||
    quote?.status === "approved" ||
    quote?.status === "production" ||
    quote?.status === "delivered";
  
  const [form, setForm] = useState({
    client_id: "",
    title: "",
    status: "draft" as QuoteStatus,
    expires_at: "",
    delivery_date: "",
    notes: "",
    internal_notes: "",
    discount: "0,00",
    shipping: "0,00",
    list_price_mode: false,
  });
  const [discountType, setDiscountType] = useState<"value" | "percent">("value");

  const publicBaseUrl = useMemo(() => {
    const origin = typeof window !== "undefined" ? window.location.origin : "https://ktalogu.com.br";
    const resolved = process.env.NEXT_PUBLIC_PUBLIC_CATALOG_BASE_URL || origin;
    const normalized = resolved.replace(":3002", ":3001").replace(/\/\/admin\./, "//");
    return normalized.replace(/\/$/, "");
  }, []);
  const logoUrl = profile?.logo_url || profile?.catalog_logo_url || "";
  const productsById = useMemo(() => new Map(products.map((product) => [product.id, product])), [products]);

  function resolveProductUnitPrice(item: QuoteItemDraft, product: ProductOption) {
    return resolveProductLine(item, product).precoPorMedida;
  }

  function resolveProductLine(item: QuoteItemDraft, product: ProductOption): ResultadoDaLinha {
    /* A mesma conta da loja: faixa pela área total, adicional por m² × área,
       por peça × peças, uma vez no pedido, e por último o valor mínimo do
       item. O preço unitário guardado é por medida, com peça e pedido rateados. */
    return precoDaLinha({
      precoBase: Number(product.price) || 0,
      faixas: product.product_price_tiers || [],
      pecas: Number(item.quantity) || 0,
      quantidadeEfetiva: effectiveQuantity(item),
      adicionais: getSelectedOptionsPriceBreakdown(item, product),
      soVariacao: Boolean(product.use_variation_price_only),
      sobConsulta: Boolean(product.price_on_request),
      valorMinimo: valorMinimoDoItem(product, "orcamentos"),
    });
  }

  function getItemQuantityRuleMessage(item: QuoteItemDraft, product?: ProductOption | null) {
    if (!product) return null;
    const quantityCheck = checkSaleQuantityLimits({
      min_sale_qty_enabled: product.min_sale_qty_enabled,
      min_sale_qty: product.min_sale_qty,
      max_sale_qty_enabled: product.max_sale_qty_enabled,
      max_sale_qty: product.max_sale_qty,
      allowed_sale_qty_enabled: product.allowed_sale_qty_enabled,
      allowed_sale_quantities: product.allowed_sale_quantities,
      effectiveQuantity: effectiveQuantity(item),
    });
    if (quantityCheck.isBelowMin && quantityCheck.minQty !== null) {
      return `Quantidade mínima: ${formatQuantityLimit(quantityCheck.minQty)}.`;
    }
    if (quantityCheck.isAboveMax && quantityCheck.maxQty !== null) {
      return `Quantidade máxima: ${formatQuantityLimit(quantityCheck.maxQty)}.`;
    }
    if (quantityCheck.isNotAllowedValue && quantityCheck.allowedQuantities.length > 0) {
      const values = quantityCheck.allowedQuantities.map((value) => formatQuantityLimit(value)).join(", ");
      return `Use uma quantidade permitida: ${values}.`;
    }
    return null;
  }

  useEffect(() => {
    if (!artModalOpen || !autoOpenArtPicker) return;
    if (artFileInputRef.current) {
      artFileInputRef.current.click();
      setAutoOpenArtPicker(false);
    }
  }, [artModalOpen, autoOpenArtPicker]);

  useEffect(() => {
    let cancelled = false;
    async function run() {
      try {
        setLoading(true);
        const supabase = getSupabaseClient();
        const {
          data: { user },
        } = await supabase.auth.getUser();
        if (!user) return;
        const ctx = await getCompanyContext(supabase, user);
        const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];

        const profileUserId = ctx.ownerId ?? user.id;
        const profileCandidates = Array.from(new Set([profileUserId, ctx.companyId, user.id].filter(Boolean))) as string[];
        const profileLookup = profileCandidates.length > 0 ? profileCandidates : [user.id];
        const [
          { data: clientsData, error: clientsErr },
          { data: productsData, error: productsErr },
          { data: profileData, error: profileErr },
          { data: quoteData, error: quoteErr },
          { data: itemsData, error: itemsErr },
          { data: artsData, error: artsErr },
          { data: messagesData, error: messagesErr },
          { data: domainData, error: domainErr },
        ] = await Promise.all([
          supabase
            .from("clients")
            .select("id,name,company_name,whatsapp,address_zip,is_deleted,document")
            .in("user_id", tenantIds)
            .order("company_name", { ascending: true })
            .order("name", { ascending: true }),
          supabase
            .from("products")
            .select(`id,name,price,price_on_request,use_variation_price_only,cost_total,unit_type,image_url,product_kind,min_sale_qty_enabled,min_sale_qty,max_sale_qty_enabled,max_sale_qty,allowed_sale_qty_enabled,allowed_sale_quantities,inventory_scope,inventory_attribute_name,allow_negative_stock,track_inventory,stock,product_attributes(name,allow_multiple,is_required,position,options),product_variations(combination,stock),product_price_tiers(min_qty,max_qty,unit_price,sort_order,active),${COLUNAS_DO_VALOR_MINIMO}`)
            .in("user_id", tenantIds)
            .eq("active", true)
            .order("name", { ascending: true }),
          supabase
            .from("profiles")
            .select(
              "id,slug,store_name,catalog_name,logo_url,catalog_logo_url,cnpj,phone,company_phone,company_whatsapp,company_email,address,whatsapp,quote_share_link_expires_enabled,quote_share_link_expires_days,quote_default_notes_enabled,quote_default_notes,quote_default_payment_terms,quote_payment_terms_templates"
            )
            .in("id", profileLookup),
          supabase
            .from("quotes")
            .select("*, orders(id, number)")
            .eq("id", quoteId)
            .in("user_id", tenantIds)
            .maybeSingle(),
          supabase.from("quote_items").select("*").eq("quote_id", quoteId).order("position", { ascending: true }),
          supabase.from("quote_item_arts").select("*").eq("quote_id", quoteId),
          supabase.from("quote_item_art_messages").select("*").eq("quote_id", quoteId).order("created_at", {
            ascending: true,
          }),
          supabase
            .from("profile_custom_domains")
            .select("domain,verified")
            .eq("profile_id", profileUserId)
            .maybeSingle(),
        ]);

        if (cancelled) return;
        if (clientsErr) throw clientsErr;
        let resolvedProductsData = (productsData as ProductOption[] | null) ?? null;
        if (productsErr) {
          const message = String((productsErr as { message?: unknown } | null)?.message ?? "").toLowerCase();
          if (message.includes("product_price_tiers") || message.includes("product_attributes")) {
            const fallback = await supabase
              .from("products")
              .select("id,name,price,price_on_request,use_variation_price_only,cost_total,unit_type,image_url,product_kind,min_sale_qty_enabled,min_sale_qty,max_sale_qty_enabled,max_sale_qty,allowed_sale_qty_enabled,allowed_sale_quantities,inventory_scope,inventory_attribute_name,allow_negative_stock,track_inventory,stock,product_attributes(name,allow_multiple,is_required,position,options),product_variations(combination,stock)")
              .in("user_id", tenantIds)
              .eq("active", true)
              .order("name", { ascending: true });
            if (fallback.error) throw fallback.error;
            resolvedProductsData = (fallback.data as ProductOption[] | null) ?? null;
          } else {
            throw productsErr;
          }
        }
        if (profileErr) throw profileErr;
        if (domainErr) {
          const domainErrorCode = (domainErr as { code?: string }).code;
          if (domainErrorCode !== "PGRST116" && domainErrorCode !== "42P01") throw domainErr;
        }
        if (quoteErr) throw quoteErr;
        if (itemsErr) throw itemsErr;
        const artsMissingTable =
          Boolean(artsErr) &&
          ((artsErr as { code?: string; message?: string }).code === "42P01" ||
            (typeof (artsErr as { message?: string }).message === "string" &&
              (artsErr as { message?: string }).message?.includes("quote_item_arts")));
        const resolvedArtsData = artsMissingTable ? [] : artsData;
        if (artsErr && !artsMissingTable) throw artsErr;
        const messagesMissingTable =
          Boolean(messagesErr) &&
          ((messagesErr as { code?: string; message?: string }).code === "42P01" ||
            (typeof (messagesErr as { message?: string }).message === "string" &&
              (messagesErr as { message?: string }).message?.includes("quote_item_art_messages")));
        const resolvedMessagesData = messagesMissingTable ? [] : messagesData;
        if (messagesErr && !messagesMissingTable) throw messagesErr;

        if (!quoteData) {
          console.error("Orçamento não encontrado ou acesso negado.");
          router.push("/dashboard/quotes");
          return;
        }

        setClients((clientsData as ClientOption[]) || []);
        setProducts(resolvedProductsData || []);

        const rawQuote = quoteData as QuoteWithOrdersArray;
        if (rawQuote.orders && Array.isArray(rawQuote.orders) && rawQuote.orders.length > 0) {
            rawQuote.orders = rawQuote.orders[0];
        } else if (Array.isArray(rawQuote.orders)) {
            rawQuote.orders = null;
        }

        setQuote(rawQuote as QuoteRow);
        setCamposValores({ ...valoresDaLinha((rawQuote as { campos_proprios?: unknown }).campos_proprios) });
        setShippingDetails((rawQuote as QuoteRow).shipping_details || null);
        setCondicoes(normalizarCondicoes((rawQuote as QuoteRow).payment_terms));
        const resolvedProfiles = (profileData as ProfileRow[]) || [];
        const resolvedProfile =
          resolvedProfiles.find((entry) => entry.logo_url || entry.catalog_logo_url) ||
          resolvedProfiles.find((entry) => entry.catalog_name || entry.store_name) ||
          resolvedProfiles[0] ||
          null;
        setProfile(resolvedProfile);
        setEhDonoDaEmpresa(!ctx.ownerId || ctx.ownerId === user.id);
        /* O padrão das condições mora no perfil do dono, que nem sempre é o
           perfil escolhido acima para logo e nome. */
        const perfilDoDono = resolvedProfiles.find((entry) => entry.id === profileUserId) || resolvedProfile;
        setModelosDaEmpresa(lerModelos(perfilDoDono?.quote_payment_terms_templates, perfilDoDono?.quote_default_payment_terms));
        setProfileCustomDomain((domainData as { domain?: string | null } | null)?.domain || null);
        setProfileCustomDomainVerified(Boolean((domainData as { verified?: boolean | null } | null)?.verified));

        // Resolver nome do criador se existir created_by
        if (rawQuote.created_by) {
          resolveCreatorName(rawQuote.created_by, rawQuote.user_id);
        }

        const q = rawQuote as QuoteRow;
        setForm({
          client_id: q.client_id,
          title: q.title || "",
          status: q.status,
          expires_at: q.expires_at || "",
          delivery_date: q.delivery_date || "",
          notes: q.notes || "",
          internal_notes: q.internal_notes || "",
          discount: new Intl.NumberFormat("pt-BR", { minimumFractionDigits: 2 }).format(q.discount ?? 0),
          shipping: new Intl.NumberFormat("pt-BR", { minimumFractionDigits: 2 }).format(q.shipping ?? 0),
          list_price_mode: Boolean(q.list_price_mode),
        });

        const mappedItems = ((itemsData as QuoteItemRow[]) || []).map((it) => ({
          id: it.id,
          product_id: it.product_id || "",
          title: it.title || "",
          description: it.description || "",
          unit_type: it.unit_type || "unit",
          width_m: Number(it.width_m) || 0,
          height_m: Number(it.height_m) || 0,
          length_m: Number(it.length_m) || 0,
          quantity: Number(it.quantity) || 0,
          unit_price: Number(it.unit_price) || 0,
          unit_cost: Number(it.unit_cost) || 0,
          unit_price_locked: false,
          selected_options: it.selected_options && typeof it.selected_options === "object" ? it.selected_options : null,
          minimo:
            it.valor_minimo_aplicado != null && Number(it.valor_minimo_aplicado) > 0
              ? {
                  minimo: Number(it.valor_minimo_aplicado),
                  semMinimo: it.valor_sem_minimo == null ? null : Number(it.valor_sem_minimo),
                }
              : null,
        }));
        setItems(mappedItems);
        existingItemIdsRef.current = new Set(mappedItems.map((it) => it.id));
        setItensGuardados(new Set(mappedItems.map((it) => it.id)));
        const artsMap = new Map<string, QuoteItemArt>();
        ((resolvedArtsData as QuoteItemArt[]) || []).forEach((art) => {
          if (art.quote_item_id) {
            artsMap.set(art.quote_item_id, {
              id: art.id,
              quote_id: art.quote_id,
              quote_item_id: art.quote_item_id,
              title: art.title || "",
              notes: art.notes || "",
              image_urls: Array.isArray(art.image_urls) ? art.image_urls : [],
              created_at: art.created_at,
              approval_status: art.approval_status || null,
              approval_notes: art.approval_notes || null,
              approved_at: art.approved_at || null,
              reviewed_at: art.reviewed_at || null,
            });
          }
        });
        const messagesMap = new Map<string, QuoteItemArtMessage[]>();
        ((resolvedMessagesData as QuoteItemArtMessage[]) || []).forEach((message) => {
          if (!message.art_id) return;
          const list = messagesMap.get(message.art_id) ?? [];
          list.push(message);
          messagesMap.set(message.art_id, list);
        });
        for (const art of artsMap.values()) for (const url of art.image_urls) urlsConhecidas.current.add(url);
        setItemArts(Object.fromEntries(artsMap));
        setArtMessages(Object.fromEntries(messagesMap));
      } catch (err) {
        console.error("Erro ao carregar orçamento:", err);
        if (typeof err === "object" && err !== null) {
          const errInfo = err as {
            message?: string;
            code?: string;
            details?: string;
            hint?: string;
          };
          console.error("Detalhes do erro:", {
            message: errInfo.message,
            code: errInfo.code,
            details: errInfo.details,
            hint: errInfo.hint,
          });
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    }
    run();
    return () => {
      cancelled = true;
    };
  }, [quoteId, router]);

  useEffect(() => {
    if (loading) return;
    if (!printPreview) return;
    if (!logoUrl) {
      setPrintAssetReady(true);
      return;
    }
    let active = true;
    const img = new window.Image();
    img.onload = () => {
      if (active) setPrintAssetReady(true);
    };
    img.onerror = () => {
      if (active) setPrintAssetReady(true);
    };
    img.src = logoUrl;
    const t = window.setTimeout(() => {
      if (active) setPrintAssetReady(true);
    }, 4000);
    return () => {
      active = false;
      window.clearTimeout(t);
    };
  }, [loading, printPreview, logoUrl]);

  useEffect(() => {
    if (!printPreview) return;
    if (!printAssetReady) return;
    const t = window.setTimeout(() => window.print(), 350);
    return () => window.clearTimeout(t);
  }, [printPreview, printAssetReady]);

  useEffect(() => {
    if (!printPreview) return;
    const handleAfterPrint = () => {
      setPrintPreview(false);
      setPrintAssetReady(false);
    };
    window.addEventListener("afterprint", handleAfterPrint);
    return () => window.removeEventListener("afterprint", handleAfterPrint);
  }, [printPreview]);

  useEffect(() => {
    if (searchParams.get("print") !== "1") return;
    setPrintPreview(true);
  }, [searchParams]);

  const totals = useMemo(() => {
    const normalizedItems = items.filter((i) => i.title.trim() || i.description.trim());
    const subtotal = normalizedItems.reduce((acc, it) => acc + totalDaLinhaDoRascunho(it, effectiveQuantity(it)), 0);
    const discountBase = Math.max(
      0,
      discountType === "percent" ? parsePercentInput(form.discount) : parseMoneyInput(form.discount)
    );
    const discount = discountType === "percent" ? (subtotal * discountBase) / 100 : discountBase;
    const shipping = parseMoneyInput(form.shipping);
    const total = Math.max(0, subtotal - discount + shipping);
    const cost = normalizedItems.reduce(
      (acc, it) => acc + effectiveQuantity(it) * (it.unit_cost || 0),
      0
    );
    const margin = total - cost;
    const marginPct = total > 0 ? (margin / total) * 100 : 0;
    return { subtotal, discount, shipping, total, cost, margin, marginPct };
  }, [items, form.discount, form.shipping, discountType]);
  const isListPriceMode = Boolean(form.list_price_mode);

  /* A página em etapas: o mesmo formulário e o mesmo salvar, mostrados em
     seis passos quando a pessoa prefere assim. Os blocos abaixo só ganham
     `hidden={fora(n)}`; nada é desmontado. */
  const paginaEmEtapas = useModeloDeOrcamento();
  const { emEtapas, etapa, fora, irPara } = paginaEmEtapas;
  const { itemAberto, abrirItem, alternarItem } = useItemAberto();
  const etapasFeitas = useMemo(() => {
    const preenchidos = items.filter((i) => i.title.trim() || i.description.trim());
    return {
      1: Boolean(form.client_id),
      2: preenchidos.length > 0,
      3: condicoes ? erroDasCondicoes(condicoes, totals.total) === null : false,
      4: preenchidos.length > 0 && preenchidos.every((i) => (i.unit_cost || 0) > 0),
      5: Boolean(form.delivery_date),
      6: form.internal_notes.trim().length > 0,
    };
  }, [items, form.client_id, form.delivery_date, form.internal_notes, condicoes, totals.total]);
  const ultimoErroLevadoAEtapa = useRef<string | null>(null);
  useEffect(() => {
    if (!error) {
      ultimoErroLevadoAEtapa.current = null;
      return;
    }
    if (!emEtapas || ultimoErroLevadoAEtapa.current === error) return;
    ultimoErroLevadoAEtapa.current = error;
    const destino = etapaDoErro(error);
    if (destino) irPara(destino);
    if (destino === 2) {
      /* A mensagem cita o item pelo nome; ele abre para a pessoa corrigir. */
      const alvo = items.find((i) => {
        const nome = i.title.trim() || productsById.get(i.product_id)?.name || "";
        return nome !== "" && error.includes(nome);
      });
      if (alvo) abrirItem(alvo.id);
    }
  }, [error, emEtapas, irPara, items, productsById, abrirItem]);

  const refreshClients = useCallback(async (preferredClientId?: string) => {
    try {
      const supabase = getSupabaseClient();
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) return;
      const ctx = await getCompanyContext(supabase, user);
      const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
      const { data: clientsData, error: clientsErr } = await supabase
        .from("clients")
        .select("id,name,company_name,whatsapp,address_zip,is_deleted,document")
        .in("user_id", tenantIds)
        .order("company_name", { ascending: true })
        .order("name", { ascending: true });
      if (clientsErr) throw clientsErr;
      const available = (clientsData as ClientOption[]) || [];
      setClients(available);
      if (preferredClientId && available.some((client) => client.id === preferredClientId)) {
        setForm((prev) => ({ ...prev, client_id: preferredClientId }));
      }
    } catch (err) {
      console.error("Erro ao recarregar clientes:", err);
    }
  }, []);

  const selectedClient = useMemo(() => clients.find((c) => c.id === form.client_id) || null, [clients, form.client_id]);
  const clientOptions = useMemo(
    () => [
      { value: "", label: "Selecione..." },
      ...clients
        .filter((c) => !c.is_deleted || c.id === form.client_id)
        .map((c) => ({
          value: c.id,
          label: c.company_name ? `${c.company_name} - ${c.name}` : c.name,
        })),
    ],
    [clients, form.client_id]
  );
  const clientFirstName = useMemo(() => {
    const name = selectedClient?.name?.trim();
    if (!name) return "Cliente";
    return name.split(/\s+/)[0] || "Cliente";
  }, [selectedClient]);
  const activeChatTab = useMemo(
    () => chatTabs.find((tab) => tab.artId === activeChatArtId) || null,
    [chatTabs, activeChatArtId]
  );
  const activeChatArt = activeChatTab ? itemArts[activeChatTab.itemId] || null : null;
  const activeChatMessages = useMemo(
    () => (activeChatTab?.artId ? artMessages[activeChatTab.artId] || [] : []),
    [activeChatTab?.artId, artMessages]
  );
  const activeChatMessagesSorted = useMemo(
    () =>
      [...activeChatMessages].sort((a, b) => new Date(a.created_at || 0).getTime() - new Date(b.created_at || 0).getTime()),
    [activeChatMessages]
  );
  const activeChatDraft = activeChatTab?.artId ? artMessageDrafts[activeChatTab.artId] || "" : "";
  const activeChatBusy = activeChatTab?.artId ? artMessageSending[activeChatTab.artId] || false : false;

  useEffect(() => {
    const wantsChat = searchParams.get("chat") === "1";
    const requestedArtId = searchParams.get("art");
    if (!wantsChat || !requestedArtId || openedChatFromQueryRef.current) return;
    if (!items.length || !Object.keys(itemArts).length) return;
    let targetItemId: string | null = null;
    let targetArt: QuoteItemArt | null = null;
    Object.entries(itemArts).some(([itemId, art]) => {
      if (art?.id === requestedArtId) {
        targetItemId = itemId;
        targetArt = art;
        return true;
      }
      return false;
    });
    if (!targetItemId || !targetArt) return;
    const itemIndex = items.findIndex((it) => it.id === targetItemId);
    const item = items[itemIndex] || null;
    if (!item || itemIndex < 0) return;
    openedChatFromQueryRef.current = true;
    openArtChat(item, itemIndex, targetArt);
  }, [items, itemArts, searchParams]);

  useEffect(() => {
    if (quote && selectedClient) {
      document.title = `Orc - ${selectedClient.name} - #${quote.number}`;
    }
  }, [quote, selectedClient]);

  useEffect(() => {
    if (!quote?.id) return;
    const supabase = getSupabaseClient();
    const channel = supabase.channel(`quote-chat-${quote.id}`);
    channel.on("broadcast", { event: "message" }, ({ payload }) => {
      const incoming = payload as QuoteItemArtMessage;
      if (!incoming?.id || !incoming?.art_id) return;
      setArtMessages((prev) => {
        const existing = prev[incoming.art_id] || [];
        if (existing.some((msg) => msg.id === incoming.id)) return prev;
        return {
          ...prev,
          [incoming.art_id]: [...existing, incoming],
        };
      });
    });
    channel.on("broadcast", { event: "receipt" }, ({ payload }) => {
      const receipt = payload as { id?: string; delivered_at?: string | null; read_at?: string | null };
      if (!receipt?.id) return;
      setArtMessages((prev) => {
        const updated: Record<string, QuoteItemArtMessage[]> = {};
        Object.entries(prev).forEach(([artId, messages]) => {
          updated[artId] = messages.map((message) =>
            message.id === receipt.id
              ? {
                  ...message,
                  delivered_at: receipt.delivered_at ?? message.delivered_at,
                  read_at: receipt.read_at ?? message.read_at,
                }
              : message
          );
        });
        return updated;
      });
    });
    channel.subscribe();
    chatChannelRef.current = channel;
    return () => {
      supabase.removeChannel(channel);
      chatChannelRef.current = null;
    };
  }, [quote?.id]);

  useEffect(() => {
    if (activeItemId && !items.some((item) => item.id === activeItemId)) {
      setActiveItemId(null);
    }
  }, [activeItemId, items]);

  useEffect(() => {
    const targetId = pendingItemFocusRef.current;
    if (!targetId || typeof window === "undefined") return;

    const timer = window.setTimeout(() => {
      const card = document.getElementById(`quote-item-${targetId}`);
      if (!card) return;

      const primaryField = card.querySelector(
        '[data-item-primary-field="true"] input, [data-item-primary-field="true"] button, [data-item-primary-field="true"] [role="combobox"]'
      ) as HTMLElement | null;
      const fallbackField = card.querySelector(
        'input:not([disabled]), textarea:not([disabled]), button:not([disabled]), [role="combobox"]'
      ) as HTMLElement | null;
      const focusTarget = primaryField || fallbackField;

      pendingItemFocusRef.current = null;
      setActiveItemId(targetId);
      if (focusTarget) {
        focusTarget.focus();
        return;
      }
      if (card instanceof HTMLElement) {
        card.focus();
      }
    }, 120);

    return () => window.clearTimeout(timer);
  }, [items]);

  useEffect(() => {
    if (!quote?.id) return;
    let active = true;
    async function markReceipts() {
      const supabase = getSupabaseClient();
      const updates: Array<{ id: string; delivered_at: string; read_at: string }> = [];
      Object.values(artMessages).forEach((messages) => {
        messages.forEach((message) => {
          if (message.sender !== "client") return;
          if (message.read_at) return;
          if (readIdsRef.current.has(message.id)) return;
          const now = new Date().toISOString();
          readIdsRef.current.add(message.id);
          updates.push({ id: message.id, delivered_at: now, read_at: now });
        });
      });
      for (const update of updates) {
        const { error } = await supabase
          .from("quote_item_art_messages")
          .update({ delivered_at: update.delivered_at, read_at: update.read_at })
          .eq("id", update.id);
        if (!active || error) continue;
        setArtMessages((prev) => {
          const updated: Record<string, QuoteItemArtMessage[]> = {};
          Object.entries(prev).forEach(([artId, messages]) => {
            updated[artId] = messages.map((msg) =>
              msg.id === update.id
                ? { ...msg, delivered_at: update.delivered_at, read_at: update.read_at }
                : msg
            );
          });
          return updated;
        });
        if (chatChannelRef.current) {
          await chatChannelRef.current.send({
            type: "broadcast",
            event: "receipt",
            payload: update,
          });
        }
      }
    }
    markReceipts();
    return () => {
      active = false;
    };
  }, [artMessages, quote?.id]);

  async function ensurePublicShare() {
    if (!quote) return null;
    if (!profile?.slug) {
      setError("Configure o link público da loja (slug) em Catálogo.");
      return null;
    }
    setSharing(true);
    setError("");
    try {
      const supabase = getSupabaseClient();
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) throw new Error("Usuário não autenticado");
      const ctx = await getCompanyContext(supabase, user);
      const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];

      let token = quote.share_token;
      if (!token) token = generateShareToken();

      let shareExpiresAt = resolveShareExpiry(
        profile?.quote_share_link_expires_enabled,
        profile?.quote_share_link_expires_days
      );
      const { error: upErr } = await supabase
        .from("quotes")
        .update({ share_enabled: true, share_token: token, share_expires_at: shareExpiresAt })
        .eq("id", quote.id)
        .in("user_id", tenantIds);

      if (upErr) {
        if ((upErr as { code?: string } | null)?.code === "23505") {
          token = generateShareToken();
          shareExpiresAt = resolveShareExpiry(
            profile?.quote_share_link_expires_enabled,
            profile?.quote_share_link_expires_days
          );
          const { error: retryErr } = await supabase
            .from("quotes")
            .update({ share_enabled: true, share_token: token, share_expires_at: shareExpiresAt })
            .eq("id", quote.id)
            .in("user_id", tenantIds);
          if (retryErr) throw retryErr;
        } else {
          throw upErr;
        }
      }

      /* O prazo também: a faixa do orçamento passa a dizer "Vence em" na hora. */
      setQuote((prev) => (prev ? { ...prev, share_enabled: true, share_token: token, share_expires_at: shareExpiresAt } : prev));
      const shareUrl =
        profileCustomDomain && profileCustomDomainVerified
          ? `https://${profileCustomDomain}/o/${token}`
          : `${publicBaseUrl}/${profile.slug}/o/${token}`;
      return { token, url: shareUrl };
    } catch (err) {
      console.error("Erro ao gerar link público:", err);
      setError("Erro ao gerar link público.");
      return null;
    } finally {
      setSharing(false);
    }
  }

  async function handleCopyLink() {
    const share = await ensurePublicShare();
    if (!share) return;
    try {
      let copied = false;
      if (navigator.clipboard && document.hasFocus()) {
        await navigator.clipboard.writeText(share.url);
        copied = true;
      } else {
        const textarea = document.createElement("textarea");
        textarea.value = share.url;
        textarea.setAttribute("readonly", "true");
        textarea.style.position = "fixed";
        textarea.style.opacity = "0";
        document.body.appendChild(textarea);
        textarea.select();
        textarea.setSelectionRange(0, textarea.value.length);
        copied = document.execCommand("copy");
        document.body.removeChild(textarea);
      }
      if (!copied) throw new Error("copy_failed");
      setCopied(true);
      window.setTimeout(() => setCopied(false), 1400);
      avisarEnvioDoLink("link");
    } catch (err) {
      console.error("Erro ao copiar:", err);
      setError("Não foi possível copiar o link. Clique na página e tente novamente.");
    }
  }

  /* Quando a empresa mandou o link: vira um ponto na linha do tempo das
     métricas ("tempo até o cliente abrir"). Se falhar, não atrapalha o envio. */
  function avisarEnvioDoLink(canal: "whatsapp" | "link") {
    setLinkEnviadoAqui(true);
    void registrarEnvioDoLink(quote?.id, canal).then(() => metricasDoLink.recarregar());
  }

  async function handleWhatsAppShare() {
    const share = await ensurePublicShare();
    if (!share) return;
    const digits = normalizeDigits(selectedClient?.whatsapp || "");
    if (!digits) {
      setError("Cliente sem WhatsApp cadastrado.");
      return;
    }
    const templates = await fetchWhatsAppTemplates();
    const validade = quote?.expires_at ? ` Validade: ${formatDateBr(quote.expires_at)}.` : "";
    const totalText = form.list_price_mode ? "" : formatMoney(totals.total);
    const message = resolveTemplate(
      templates.quote_share,
      "Olá! Segue o orçamento #{{numero_orcamento}}. Total: {{total}}.{{validade}}{{condicoes}} {{link}}",
      {
        numero_orcamento: String(quote?.number ?? ""),
        total: totalText,
        validade,
        condicoes: condicoes ? ` Pagamento: ${resumirCondicoes(condicoes, form.list_price_mode ? 0 : totals.total)}` : "",
        link: share.url,
      }
    );
    window.open(`https://wa.me/${digits}?text=${encodeURIComponent(message)}`, "_blank", "noopener,noreferrer");
    avisarEnvioDoLink("whatsapp");
  }

  function collectOrderGenerationStockAlerts() {
    const alerts: OrderGenerationStockAlert[] = [];
    for (const item of items) {
      if (!item.product_id) continue;
      const product = productsById.get(item.product_id);
      const message = getItemVariationStockMessage(item, product);
      if (!message) continue;
      const normalizedMessage = message.toLowerCase();
      const isStockShortage =
        normalizedMessage.includes("estoque insuficiente") || normalizedMessage.includes("sem estoque");
      alerts.push({
        itemLabel: item.title.trim() || product?.name || "Item",
        message,
        allowNegative: Boolean(product?.allow_negative_stock) && isStockShortage,
      });
    }
    return alerts;
  }

  async function generateOrderRequest() {
    if (!quote) return;
    setGeneratingOrder(true);
    try {
      const res = await fetch(`/api/quotes/${quote.id}/approve`, { method: "POST" });
      const json = (await res.json().catch(() => null)) as { orderId?: string; error?: string } | null;
      if (!res.ok) throw new Error(json?.error || "Erro ao gerar pedido.");
      const orderId = json?.orderId;
      if (!orderId) throw new Error("Resposta invalida ao gerar pedido.");
      router.push(`/dashboard/orders/${orderId}`);
    } catch (err) {
      console.error("Erro ao gerar pedido:", err);
      const msg = err instanceof Error ? err.message : "Erro desconhecido";
      setOrderErrorModal({ open: true, message: `Erro ao gerar pedido: ${msg}` });
    } finally {
      setGeneratingOrder(false);
      setOrderNegativeStockModal((prev) => ({ ...prev, open: false }));
    }
  }

  async function handleGenerateOrder() {
    if (!quote) return;
    if (quote.orders) {
      setOrderErrorModal({ open: true, message: "Este orçamento já possui um pedido gerado." });
      return;
    }
    if (estoqueV2 === true) {
      /* Estoque v2: confere agora no banco. Só aparece o que o banco
         recusaria (quem permite negativo ou não controla nunca falta). */
      const falta = await conferenciaDoEstoqueV2.conferirAgora();
      if (falta) {
        setOrderErrorModal({
          open: true,
          message: `Sem estoque para gerar o pedido. ${falta} Lance a entrada em Estoque ou ligue "permitir negativo" no item.`,
        });
        return;
      }
      setConfirmacao({
        titulo: "Gerar o pedido de produção?",
        texto: "O orçamento é aprovado e vira um pedido na produção. Depois disso ele não pode mais ser excluído, só cancelado.",
        confirmar: "Aprovar e gerar pedido",
        aoConfirmar: () => {
          setConfirmacao(null);
          return generateOrderRequest();
        },
      });
      return;
    }
    const stockAlerts = collectOrderGenerationStockAlerts();
    const blockingAlerts = stockAlerts.filter((alert) => !alert.allowNegative);
    const allowedAlerts = stockAlerts.filter((alert) => alert.allowNegative);
    if (blockingAlerts.length > 0) {
      const message = blockingAlerts
        .map((alert) => `${alert.itemLabel}: ${alert.message}`)
        .join(" ");
      // O conselho de liberar saldo negativo só faz sentido quando o problema
      // é saldo — para combinação incompleta/inexistente ele só confundia.
      const anyShortage = blockingAlerts.some((alert) => {
        const normalized = alert.message.toLowerCase();
        return normalized.includes("estoque insuficiente") || normalized.includes("sem estoque");
      });
      setOrderErrorModal({
        open: true,
        message: anyShortage
          ? `${message} Ative "Permitir estoque negativo" no produto para autorizar geração com saldo negativo.`
          : message,
      });
      return;
    }
    if (allowedAlerts.length > 0) {
      setOrderNegativeStockModal({ open: true, alerts: allowedAlerts });
      return;
    }
    setConfirmacao({
      titulo: "Gerar o pedido de produção?",
      texto: "O orçamento é aprovado e vira um pedido na produção. Depois disso ele não pode mais ser excluído, só cancelado.",
      confirmar: "Aprovar e gerar pedido",
      aoConfirmar: () => {
        setConfirmacao(null);
        return generateOrderRequest();
      },
    });
  }

  async function resolveCreatorName(userId: string, quoteUserId: string) {
    try {
      const supabase = getSupabaseClient();
      
      // Primeiro verifica se - o dono da empresa (user_id igual ao user_id do quote)
       if (userId === quoteUserId) {
         setCreatorName("Empresa (Admin)");
         return;
       }
      
      // Se não for o dono, busca informações do funcionário
      const { data: employee } = await supabase
        .from("employee_profiles")
        .select("full_name")
        .eq("user_id", userId)
        .maybeSingle();
      
      if (employee?.full_name) {
        setCreatorName(employee.full_name);
        return;
      }
      
      // Fallback: perfil geral
      const { data: profile } = await supabase
        .from("profiles")
        .select("full_name")
        .eq("id", userId)
        .maybeSingle();
      
      if (profile?.full_name) {
        setCreatorName(profile.full_name);
        return;
      }
      
      setCreatorName("Usuário");
    } catch (error) {
      console.warn("Erro ao resolver nome do criador:", error);
      setCreatorName("Usuário");
    }
  }

  function handlePrint() {
    setPrintAssetReady(false);
    setPrintPreview(true);
  }

  async function handleDuplicateQuote() {
    if (!quote || duplicating) return;
    setDuplicating(true);
    try {
      const response = await fetch(`/api/quotes/${quote.id}/duplicate`, { method: "POST" });
      const payload = (await response.json().catch(() => null)) as
        | { quoteId?: string; error?: string }
        | null;

      if (!response.ok || !payload?.quoteId) {
        throw new Error(payload?.error || "Não foi possível duplicar o orçamento.");
      }

      router.push(`/dashboard/quotes/${payload.quoteId}?duplicated=1`);
    } catch (err) {
      setErroDaExclusao(err instanceof Error ? err.message : "Não foi possível duplicar o orçamento.");
    } finally {
      setDuplicating(false);
    }
  }

  useEffect(() => {
    setMeasureDrafts((prev) => {
      const next: Record<string, MeasureDraft> = {};

      for (const item of items) {
        const fallback: MeasureDraft = {
          width_m: formatMeasureInput(item.width_m),
          height_m: formatMeasureInput(item.height_m),
          length_m: formatMeasureInput(item.length_m),
        };
        const current = prev[item.id];

        if (!current) {
          next[item.id] = fallback;
          continue;
        }

        next[item.id] = {
          width_m:
            Math.abs(parseMeasureInput(current.width_m) - (item.width_m || 0)) > 0.0005
              ? fallback.width_m
              : current.width_m,
          height_m:
            Math.abs(parseMeasureInput(current.height_m) - (item.height_m || 0)) > 0.0005
              ? fallback.height_m
              : current.height_m,
          length_m:
            Math.abs(parseMeasureInput(current.length_m) - (item.length_m || 0)) > 0.0005
              ? fallback.length_m
              : current.length_m,
        };
      }

      return next;
    });
  }, [items]);

  function updateItem(
    index: number,
    patch: Partial<QuoteItemDraft>,
    options?: { manualPrice?: boolean; recalculatePrice?: boolean }
  ) {
    setItems((prev) =>
      prev.map((it, i) => {
        if (i !== index) return it;
        let next: QuoteItemDraft = { ...it, ...patch };
        if (options?.manualPrice) {
          next = { ...next, unit_price_locked: true };
        }
        const shouldRecalculate =
          options?.recalculatePrice === true ||
          Object.prototype.hasOwnProperty.call(patch, "product_id") ||
          Object.prototype.hasOwnProperty.call(patch, "unit_type") ||
          Object.prototype.hasOwnProperty.call(patch, "quantity") ||
          Object.prototype.hasOwnProperty.call(patch, "width_m") ||
          Object.prototype.hasOwnProperty.call(patch, "height_m") ||
          Object.prototype.hasOwnProperty.call(patch, "length_m") ||
          Object.prototype.hasOwnProperty.call(patch, "selected_options") ||
          Object.prototype.hasOwnProperty.call(patch, "unit_price_locked");

        // Preço travado à mão não leva o mínimo do item.
        if (!next.product_id || next.unit_price_locked) return next.minimo ? { ...next, minimo: null } : next;
        if (!shouldRecalculate) return next;

        const product = productsById.get(next.product_id);
        if (!product) return next;

        const linha = resolveProductLine(next, product);
        return { ...next, unit_price: linha.precoPorMedida, minimo: minimoDoResultado(linha) };
      })
    );
  }

  function handleQuantityDraftChange(index: number, itemId: string, rawValue: string, minQty: number) {
    setQuantityDrafts((prev) => ({ ...prev, [itemId]: rawValue }));
    if (!rawValue.trim()) return;
    const parsed = Number(rawValue.replace(",", "."));
    if (!Number.isFinite(parsed)) return;
    updateItem(index, { quantity: Math.max(minQty, parsed) });
  }

  function commitQuantityDraft(index: number, itemId: string, minQty: number) {
    const draftValue = quantityDrafts[itemId];
    if (draftValue === undefined) return;
    const parsed = Number(draftValue.replace(",", "."));
    const nextQty = Number.isFinite(parsed) && parsed > 0 ? Math.max(minQty, parsed) : minQty;
    updateItem(index, { quantity: nextQty });
    setQuantityDrafts((prev) => {
      const next = { ...prev };
      delete next[itemId];
      return next;
    });
  }

  function canRegisterManualItem(item: QuoteItemDraft) {
    const hasName = item.title.trim().length > 0;
    const hasUnitType = Boolean(item.unit_type?.trim());
    const hasPrice = Number(item.unit_price) > 0;
    return hasName && hasUnitType && hasPrice;
  }

  async function registerManualItemAsProduct(itemId: string) {
    if (manualProductSubmittingItemId) return;
    const index = items.findIndex((entry) => entry.id === itemId);
    if (index < 0) return;
    const item = items[index];
    const name = item.title.trim();
    if (!canRegisterManualItem(item)) {
      setManualProductErrors((prev) => ({ ...prev, [item.id]: "Preencha nome, unidade e valor." }));
      return;
    }
    if (!name) return;

    setManualProductSubmittingItemId(item.id);
    setManualProductErrors((prev) => ({ ...prev, [item.id]: "" }));
    try {
      const unitPrice = Math.max(0, Number(item.unit_price) || 0);
      const unitCost = Math.max(0, Number(item.unit_cost) || 0);
      const response = await fetch("/api/products", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          name,
          description: item.description || "",
          price: unitPrice,
          price_on_request: false,
          use_variation_price_only: false,
          estimated_base_cost: unitCost,
          stock: 0,
          unit_type: item.unit_type || "unit",
          active: true,
          category_id: null,
          image_url: null,
          images: [],
          show_in_catalog: false,
          catalog_color: "#4F46E5",
          is_catalog_exclusive: false,
          min_sale_qty_enabled: false,
          min_sale_qty: null,
          max_sale_qty_enabled: false,
          max_sale_qty: null,
          allowed_sale_qty_enabled: false,
          allowed_sale_quantities: null,
          sku: null,
          barcode: null,
          track_inventory: false,
          inventory_scope: "product",
          inventory_attribute_name: null,
          allow_negative_stock: false,
          materials: [],
          processes: [],
          attributes: [],
          variations: [],
          price_tiers: [],
        }),
      });
      const json = (await response.json().catch(() => null)) as { id?: string; error?: string } | null;
      if (!response.ok || !json?.id) {
        setManualProductErrors((prev) => ({ ...prev, [item.id]: json?.error || "Não foi possível cadastrar o produto." }));
        return;
      }

      const createdProduct: ProductOption = {
        id: json.id,
        name,
        price: unitPrice,
        cost_total: unitCost,
        unit_type: item.unit_type || "unit",
        image_url: null,
        price_on_request: false,
        use_variation_price_only: false,
        product_price_tiers: [],
        min_sale_qty_enabled: false,
        min_sale_qty: null,
        max_sale_qty_enabled: false,
        max_sale_qty: null,
        allowed_sale_qty_enabled: false,
        allowed_sale_quantities: null,
        inventory_scope: "product",
        inventory_attribute_name: null,
        allow_negative_stock: false,
        track_inventory: false,
        product_attributes: [],
        product_variations: [],
      };
      setProducts((prev) =>
        [...prev, createdProduct].sort((a, b) => a.name.localeCompare(b.name, "pt-BR", { sensitivity: "base" }))
      );
      updateItem(index, {
        product_id: createdProduct.id,
        title: createdProduct.name,
        unit_type: createdProduct.unit_type || "unit",
        unit_price: unitPrice,
        unit_price_locked: false,
        selected_options: null,
      });
      setManualProductErrors((prev) => ({ ...prev, [item.id]: "" }));
    } catch {
      setManualProductErrors((prev) => ({ ...prev, [item.id]: "Não foi possível cadastrar o produto." }));
    } finally {
      setManualProductSubmittingItemId(null);
    }
  }

  function updateItemVariationSelection(
    index: number,
    product: ProductOption,
    attribute: ProductAttributeValue,
    optionLabel: string
  ) {
    const item = items[index];
    if (!item) return;
    const currentMap = getItemVariationMap(item);
    const currentValues = currentMap[attribute.name] || [];
    const allowMultiple = Boolean(attribute.allow_multiple);
    const nextValues = allowMultiple
      ? (currentValues.includes(optionLabel)
        ? currentValues.filter((value) => value !== optionLabel)
        : [...currentValues, optionLabel])
      : [optionLabel];
    const nextMap = { ...currentMap, [attribute.name]: nextValues };
    const payload = buildSelectedOptionsPayload(product, nextMap);
    updateItem(index, { selected_options: payload }, { recalculatePrice: true });
  }

  function toggleItemPriceLock(index: number) {
    setItems((prev) =>
      prev.map((it, i) => {
        if (i !== index) return it;
        const shouldLock = !it.unit_price_locked;
        if (shouldLock || !it.product_id) return { ...it, unit_price_locked: shouldLock, minimo: null };
        const product = productsById.get(it.product_id);
        if (!product) return { ...it, unit_price_locked: false, minimo: null };
        const linha = resolveProductLine(it, product);
        return { ...it, unit_price_locked: false, unit_price: linha.precoPorMedida, minimo: minimoDoResultado(linha) };
      })
    );
  }

  function scrollToItemCard(itemId: string) {
    if (typeof window === "undefined") return;
    window.setTimeout(() => {
      document.getElementById(`quote-item-${itemId}`)?.scrollIntoView({
        behavior: "smooth",
        block: "center",
      });
    }, 80);
  }

  function setItemInFocus(itemId: string, options?: { focusField?: boolean }) {
    setActiveItemId(itemId);
    scrollToItemCard(itemId);
    if (options?.focusField) {
      pendingItemFocusRef.current = itemId;
    }
  }

  function moveItemToPosition(index: number, targetIndex: number) {
    if (targetIndex < 0 || targetIndex >= items.length || targetIndex === index) return;
    const movedItemId = items[index]?.id;
    setItems((prev) => {
      const next = [...prev];
      const [movedItem] = next.splice(index, 1);
      if (!movedItem) return prev;
      next.splice(targetIndex, 0, movedItem);
      return next;
    });
    if (movedItemId) setItemInFocus(movedItemId);
  }

  function moveItem(index: number, direction: "up" | "down") {
    moveItemToPosition(index, direction === "up" ? index - 1 : index + 1);
  }

  function addItem() {
    const newItem = {
      id: crypto.randomUUID(),
      product_id: "",
      title: "",
      description: "",
      unit_type: "unit",
      width_m: 0,
      height_m: 0,
      length_m: 0,
      quantity: 1,
      unit_price: 0,
      unit_cost: 0,
      unit_price_locked: false,
    };

    setItems((prev) => [...prev, newItem]);
    setItemInFocus(newItem.id, { focusField: true });
    abrirItem(newItem.id);
  }

  function duplicateItem(index: number) {
    const source = items[index];
    if (!source) return;
    const newItem: QuoteItemDraft = {
      ...source,
      id: crypto.randomUUID(),
    };
    setItems((prev) => {
      const next = [...prev];
      next.splice(index + 1, 0, newItem);
      return next;
    });
    setItemInFocus(newItem.id, { focusField: true });
    abrirItem(newItem.id);
  }

  function removeItem(index: number) {

    const removedId = items[index]?.id;
    const fallbackItemId =
      removedId && removedId === activeItemId
        ? items[index + 1]?.id ?? items[index - 1]?.id ?? null
        : activeItemId;
    const removedArt = removedId ? itemArts[removedId] : null;
    setItems((prev) => prev.filter((_, i) => i !== index));
    if (removedId) {
      setQuantityDrafts((prev) => {
        const next = { ...prev };
        delete next[removedId];
        return next;
      });
      setManualProductErrors((prev) => {
        const next = { ...prev };
        delete next[removedId];
        return next;
      });
      setItemArts((prev) => {
        const next = { ...prev };
        delete next[removedId];
        return next;
      });
      setItemArtPdfVisibility((prev) => {
        const next = { ...prev };
        delete next[removedId];
        return next;
      });
    }
    if (removedArt) {
      deleteArtRecord(removedArt).catch((err) => {
        console.error("Erro ao excluir arte removida:", err);
      });
    }
    if (fallbackItemId) {
      setItemInFocus(fallbackItemId, { focusField: removedId === activeItemId });
    } else {
      setActiveItemId(null);
    }
  }

  function openArtModal(itemId: string) {
    if (!quote) return;
    setArtError("");
    setArtUploadProgress(null);
    setActiveArtItemId(itemId);
    const existing = itemArts[itemId];
    const itemTitle = items.find((it) => it.id === itemId)?.title || "";
    setArtDraft(
      existing
        ? { ...existing }
        : {
            quote_id: quote.id,
            quote_item_id: itemId,
            title: itemTitle,
            notes: "",
            image_urls: [],
          }
    );
    setArtModalOpen(true);
  }

  function closeArtModal() {
    setArtModalOpen(false);
    setActiveArtItemId(null);
    setArtDraft(null);
    setArtError("");
    setArtUploading(false);
    setArtUploadProgress(null);
  }

  function openArtPreview(url: string) {
    setPreviewImageUrl(url);
    setPreviewOpen(true);
  }

  function handleArtThumbnailClick(itemId: string, url: string, status: string) {
    if (!isReadOnly && status === "rejected") {
      setAutoOpenArtPicker(true);
      openArtModal(itemId);
      return;
    }
    openArtPreview(url);
  }

  function extractQuoteArtPath(url: string) {
    const marker = "/quote-arts/";
    const index = url.indexOf(marker);
    if (index === -1) return null;
    const path = url.slice(index + marker.length);
    return path.split("?")[0];
  }

  async function deleteQuoteArtFiles(urls: string[]) {
    const paths = urls.map(extractQuoteArtPath).filter((path): path is string => Boolean(path));
    if (!paths.length) return;
    const supabase = getSupabaseClient();
    const { error } = await supabase.storage.from("quote-arts").remove(paths);
    if (error) throw error;
  }

  async function persistArtDraft(
    next: QuoteItemArt,
    resetApproval: boolean,
    approvalOverride?: {
      status: QuoteItemArt["approval_status"];
      approvedAt: string | null;
      reviewedAt: string | null;
      approvalNotes: string | null;
    }
  ) {
    if (!quote || !next.quote_item_id) return next;
    if (!existingItemIdsRef.current.has(next.quote_item_id)) return next;
    const supabase = getSupabaseClient();
    const payload: Partial<QuoteItemArt> & { quote_id: string; quote_item_id: string } = {
      quote_id: quote.id,
      quote_item_id: next.quote_item_id,
      title: (next.title ?? "").trim(),
      notes: (next.notes ?? "").trim(),
      image_urls: next.image_urls || [],
    };
    if (resetApproval) {
      payload.approval_status = "pending";
      payload.approval_notes = null;
      payload.approved_at = null;
      payload.reviewed_at = null;
    }
    if (approvalOverride) {
      payload.approval_status = approvalOverride.status ?? payload.approval_status;
      payload.approval_notes = approvalOverride.approvalNotes ?? payload.approval_notes;
      payload.approved_at = approvalOverride.approvedAt ?? payload.approved_at;
      payload.reviewed_at = approvalOverride.reviewedAt ?? payload.reviewed_at;
    }
    const { data, error } = await supabase
      .from("quote_item_arts")
      .upsert(payload, { onConflict: "quote_item_id" })
      .select("*")
      .maybeSingle();
    if (error) throw error;
    return (data as QuoteItemArt | null) ?? next;
  }

  async function deleteArtRecord(art: QuoteItemArt) {
    if (!quote || !art.id) return;
    if (!existingItemIdsRef.current.has(art.quote_item_id)) return;
    const supabase = getSupabaseClient();
    const { error } = await supabase.from("quote_item_arts").delete().eq("id", art.id);
    if (error) throw error;
    if (art.image_urls.length) await deleteQuoteArtFiles(art.image_urls);
  }

  async function saveArtDraft() {
    if (!artDraft || !activeArtItemId || !quote) return;
    const title = (artDraft.title ?? "").trim();
    const notes = (artDraft.notes ?? "").trim();
    const hasContent = title || notes || artDraft.image_urls.length > 0;
    if (!hasContent) {
      const current = { ...artDraft, title, notes };
      setItemArts((prev) => {
        const next = { ...prev };
        delete next[activeArtItemId];
        return next;
      });
      if (current.id) {
        setArtNeedsNotify((prev) => {
          const next = { ...prev };
          delete next[current.id as string];
          return next;
        });
      }
      deleteArtRecord(current).catch((err) => {
        console.error("Erro ao excluir arte:", err);
      });
      closeArtModal();
      return;
    }
    const shouldNotify = Boolean(artDraft.id && artNeedsNotify[artDraft.id]);
    const shouldResetApproval = shouldNotify && !isQuoteFinalized;
    const now = new Date().toISOString();
    const nextApprovalStatus = isQuoteFinalized
      ? artDraft.approval_status ?? "approved"
      : shouldResetApproval
        ? "pending"
        : artDraft.approval_status;
    const nextApprovedAt =
      isQuoteFinalized && nextApprovalStatus === "approved"
        ? artDraft.approved_at ?? now
        : shouldResetApproval
          ? null
          : artDraft.approved_at;
    const nextReviewedAt = isQuoteFinalized ? artDraft.reviewed_at ?? now : shouldResetApproval ? null : artDraft.reviewed_at;
    const nextDraft: QuoteItemArt = {
      ...artDraft,
      title,
      notes,
      quote_id: quote.id,
      quote_item_id: activeArtItemId,
      approval_status: nextApprovalStatus,
      approval_notes: shouldResetApproval ? null : artDraft.approval_notes,
      approved_at: nextApprovedAt,
      reviewed_at: nextReviewedAt,
    };
    setItemArts((prev) => ({
      ...prev,
      [activeArtItemId]: nextDraft,
    }));
    if (nextDraft.id && existingItemIdsRef.current.has(nextDraft.quote_item_id)) {
      try {
        const persisted = await persistArtDraft(
          nextDraft,
          shouldResetApproval,
          isQuoteFinalized
            ? {
                status: nextApprovalStatus,
                approvedAt: nextApprovedAt ?? null,
                reviewedAt: nextReviewedAt ?? null,
                approvalNotes: nextDraft.approval_notes ?? null,
              }
            : undefined
        );
        setItemArts((prev) => ({
          ...prev,
          [persisted.quote_item_id]: persisted,
        }));
        if (shouldResetApproval && chatChannelRef.current) {
          await chatChannelRef.current.send({
            type: "broadcast",
            event: "art_updated",
            payload: {
              art_id: persisted.id,
              quote_item_id: persisted.quote_item_id,
              title: persisted.title ?? null,
              notes: persisted.notes ?? null,
              image_urls: persisted.image_urls ?? [],
              approval_status: persisted.approval_status ?? "pending",
              approval_notes: persisted.approval_notes ?? null,
              approved_at: persisted.approved_at ?? null,
              reviewed_at: persisted.reviewed_at ?? null,
            },
          });
          setArtNeedsNotify((prev) => ({ ...prev, [persisted.id as string]: false }));
        }
      } catch (err) {
        console.error("Erro ao salvar arte:", err);
      }
    }
    closeArtModal();
  }

  async function handleArtFiles(files: FileList | null) {
    if (!files?.length || !quote || !activeArtItemId) return;
    setArtUploading(true);
    setArtError("");
    setArtUploadProgress({ total: files.length, completed: 0 });
    try {
      const supabase = getSupabaseClient();
      const uploadedUrls: string[] = [];
      const baseDraft = artDraft;
      const shouldReplace = baseDraft?.image_urls?.length
        ? (baseDraft?.approval_status || "pending") === "rejected"
        : false;
      const retainedUrls = shouldReplace ? [] : baseDraft?.image_urls ?? [];
      if (shouldReplace && baseDraft?.image_urls?.length) {
        await deleteQuoteArtFiles(baseDraft.image_urls);
      }
      let completed = 0;
      for (const file of Array.from(files)) {
        const webp = await compressToWebp(file);
        const path = `${quote.user_id}/quotes/${quote.id}/items/${activeArtItemId}/${crypto.randomUUID()}.webp`;
        const { error: uploadError } = await supabase.storage.from("quote-arts").upload(path, webp, {
          contentType: "image/webp",
        });
        if (uploadError) throw uploadError;
        const { data } = supabase.storage.from("quote-arts").getPublicUrl(path);
        if (data.publicUrl) uploadedUrls.push(data.publicUrl);
        completed += 1;
        setArtUploadProgress((prev) => (prev ? { ...prev, completed } : prev));
      }
      const now = new Date().toISOString();
      const nextApprovalStatus = isQuoteFinalized
        ? baseDraft?.approval_status ?? "approved"
        : shouldReplace
          ? "pending"
          : baseDraft?.approval_status;
      const nextApprovedAt =
        isQuoteFinalized && nextApprovalStatus === "approved"
          ? baseDraft?.approved_at ?? now
          : shouldReplace
            ? null
            : baseDraft?.approved_at ?? null;
      const nextReviewedAt = isQuoteFinalized ? baseDraft?.reviewed_at ?? now : shouldReplace ? null : baseDraft?.reviewed_at ?? null;
      const nextDraft =
        baseDraft && activeArtItemId
          ? {
              ...baseDraft,
              image_urls: [...retainedUrls, ...uploadedUrls],
              approval_status: nextApprovalStatus,
              approval_notes: shouldReplace ? null : baseDraft.approval_notes,
              approved_at: nextApprovedAt,
              reviewed_at: nextReviewedAt,
            }
          : baseDraft;
      if (nextDraft) {
        const persisted = await persistArtDraft(
          nextDraft,
          shouldReplace && !isQuoteFinalized,
          isQuoteFinalized
            ? {
                status: nextApprovalStatus ?? "approved",
                approvedAt: nextApprovedAt ?? null,
                reviewedAt: nextReviewedAt ?? null,
                approvalNotes: nextDraft.approval_notes ?? null,
              }
            : undefined
        );
        setArtDraft(persisted);
        if (persisted.quote_item_id) {
          setItemArts((prev) => ({
            ...prev,
            [persisted.quote_item_id]: persisted,
          }));
        }
        if (shouldReplace && persisted.id && !isQuoteFinalized) {
          setArtNeedsNotify((prev) => ({ ...prev, [persisted.id as string]: true }));
        }
      }
    } catch (err) {
      console.error("Erro ao enviar artes:", err);
      const msg = err instanceof Error ? err.message : "";
      const normalized = msg.toLowerCase();
      const bucketHint = normalized.includes("quote-arts") || normalized.includes("bucket") || normalized.includes("storage");
      setArtError(
        bucketHint
          ? "Falha ao enviar artes. Verifique o bucket quote-arts e as políticas no Supabase."
          : "Não foi possível enviar a arte."
      );
    } finally {
      setArtUploading(false);
    }
  }

  async function handleSendArtMessage(art: QuoteItemArt, status: "updated" | null = null) {
    if (!quote || !art.id) return;
    if (isQuoteFinalized) return;
    const text = (artMessageDrafts[art.id] ?? "").trim();
    if (!text) return;
    setArtMessageSending((prev) => ({ ...prev, [art.id as string]: true }));
    try {
      const supabase = getSupabaseClient();
      const payload: {
        quote_id: string;
        quote_item_id: string;
        art_id: string;
        sender: "company";
        message: string;
        status: "updated" | null;
      } = {
        quote_id: quote.id,
        quote_item_id: art.quote_item_id,
        art_id: art.id,
        sender: "company",
        message: text,
        status,
      };
      const { data, error } = await supabase.from("quote_item_art_messages").insert(payload).select("*").maybeSingle();
      if (error) throw error;
      const created = (data as QuoteItemArtMessage | null) || {
        ...payload,
        id: crypto.randomUUID(),
        created_at: new Date().toISOString(),
        delivered_at: null,
        read_at: null,
      };
      setArtMessages((prev) => ({
        ...prev,
        [art.id as string]: [...(prev[art.id as string] || []), created],
      }));
      if (chatChannelRef.current) {
        await chatChannelRef.current.send({
          type: "broadcast",
          event: "message",
          payload: created,
        });
      }
      if (status === "updated" && chatChannelRef.current) {
        await chatChannelRef.current.send({
          type: "broadcast",
          event: "art_updated",
          payload: {
            art_id: art.id,
            quote_item_id: art.quote_item_id,
            title: art.title ?? null,
            notes: art.notes ?? null,
            image_urls: art.image_urls ?? [],
            approval_status: art.approval_status ?? "pending",
            approval_notes: art.approval_notes ?? null,
            approved_at: art.approved_at ?? null,
            reviewed_at: art.reviewed_at ?? null,
          },
        });
        setArtNeedsNotify((prev) => ({ ...prev, [art.id as string]: false }));
      }
      setArtMessageDrafts((prev) => ({ ...prev, [art.id as string]: "" }));
    } catch (err) {
      console.error("Erro ao enviar mensagem da arte:", err);
      setError("Não foi possível enviar a mensagem.");
    } finally {
      setArtMessageSending((prev) => ({ ...prev, [art.id as string]: false }));
    }
  }

  function openArtChat(item: QuoteItemDraft, itemIndex: number, art: QuoteItemArt) {
    if (!art.id) {
      setError("Salve o orçamento para abrir o chat desta arte.");
      return;
    }
    const label = `Item ${itemIndex + 1} - ${item.title || "Item sem título"}`;
    setChatTabs((prev) => {
      if (prev.some((tab) => tab.artId === art.id)) return prev;
      return [...prev, { artId: art.id as string, itemId: item.id, label }];
    });
    setActiveChatArtId(art.id as string);
    setChatOpen(true);
  }

  function removeArtImage(url: string) {
    setArtDraft((prev) => {
      if (!prev) return prev;
      const next = {
        ...prev,
        image_urls: prev.image_urls.filter((img) => img !== url),
      };
      if (existingItemIdsRef.current.has(prev.quote_item_id)) {
        persistArtDraft(next, false)
          .then((persisted) => {
            setItemArts((itemsPrev) => ({
              ...itemsPrev,
              [persisted.quote_item_id]: persisted,
            }));
          })
          .catch((err) => {
            console.error("Erro ao atualizar arte:", err);
          });
      }
      deleteQuoteArtFiles([url]).catch((err) => {
        console.error("Erro ao excluir imagem da arte:", err);
      });
      return next;
    });
  }

  /* Toda imagem que passa pela tela fica conhecida (as que a pessoa sobe aqui também). */
  useEffect(() => {
    for (const art of Object.values(itemArts)) for (const url of art.image_urls) urlsConhecidas.current.add(url);
  }, [itemArts]);

  /* Ao voltar para esta aba (a arte foi feita no KtaloguDesign, em outra): a arte de cada item e as imagens que chegaram. */
  const quoteIdCarregado = quote?.id ?? null;
  useEffect(() => {
    if (!quoteIdCarregado) return;
    let vivo = true;
    const atualizar = async () => {
      const lista = await artesDoOrcamento(quoteIdCarregado);
      const porItem: Record<string, string> = {};
      for (const arte of lista) if (arte.quote_item_id && !porItem[arte.quote_item_id]) porItem[arte.quote_item_id] = arte.id;
      if (vivo) setArtesDoDesign(porItem);
      const chegaram = await artesQueChegaramDeFora();
      if (vivo && Object.keys(chegaram).length) setItemArts((atuais) => juntarArtesDeFora(atuais, chegaram));
    };
    void atualizar();
    const aoVoltar = () => {
      if (document.visibilityState === "visible") void atualizar();
    };
    document.addEventListener("visibilitychange", aoVoltar);
    window.addEventListener("focus", aoVoltar);
    return () => {
      vivo = false;
      document.removeEventListener("visibilitychange", aoVoltar);
      window.removeEventListener("focus", aoVoltar);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps -- as funções leem o orçamento aberto; recarregar só quando ele muda
  }, [quoteIdCarregado]);

  /* As imagens da arte que chegaram de fora desde que a tela abriu (só as novas; as que a pessoa tirou aqui continuam fora). */
  async function artesQueChegaramDeFora(): Promise<Record<string, QuoteItemArt>> {
    if (!quote) return {};
    const { data, error } = await getSupabaseClient().from("quote_item_arts").select("*").eq("quote_id", quote.id);
    if (error || !data) return {};
    const chegaram: Record<string, QuoteItemArt> = {};
    for (const art of data as QuoteItemArt[]) {
      const novas = (Array.isArray(art.image_urls) ? art.image_urls : []).filter((url) => !urlsConhecidas.current.has(url));
      if (!novas.length || !art.quote_item_id) continue;
      for (const url of novas) urlsConhecidas.current.add(url);
      chegaram[art.quote_item_id] = { ...art, title: art.title || "", notes: art.notes || "", image_urls: novas };
    }
    return chegaram;
  }
  function juntarArtesDeFora(atuais: Record<string, QuoteItemArt>, chegaram: Record<string, QuoteItemArt>) {
    const juntas = { ...atuais };
    for (const [itemId, deFora] of Object.entries(chegaram)) {
      const aqui = juntas[itemId];
      juntas[itemId] = aqui
        ? { ...aqui, id: aqui.id ?? deFora.id, image_urls: [...aqui.image_urls, ...deFora.image_urls], approval_status: deFora.approval_status ?? aqui.approval_status, approval_notes: deFora.approval_notes ?? aqui.approval_notes, approved_at: deFora.approved_at ?? aqui.approved_at, reviewed_at: deFora.reviewed_at ?? aqui.reviewed_at }
        : deFora;
    }
    return juntas;
  }

  async function handleSave(e: React.FormEvent) {
    e.preventDefault();
    setSaving(true);
    setError("");
    try {
      const supabase = getSupabaseClient();
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) throw new Error("Usuário não autenticado");
      if (!quote) throw new Error("Orçamento não carregado");
      const ctx = await getCompanyContext(supabase, user);
      const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];

      if (!form.client_id) {
        setError("Selecione um cliente.");
        return;
      }

      for (const item of items) {
        if (!item.product_id) continue;
        const product = productsById.get(item.product_id);
        if (product) {
          const attributes = getSortedAttributes(product);
          if (attributes.length > 0) {
            const selectedMap = getItemVariationMap(item);
            for (const attribute of attributes) {
              if (!attribute.is_required) continue;
              const selectedValues = selectedMap[attribute.name] || [];
              if (!selectedValues.length) {
                setError(`Selecione uma opção obrigatória em ${attribute.name} para o item ${item.title || product.name}.`);
                return;
              }
            }
          }
        }
        const quantityRuleMessage = getItemQuantityRuleMessage(item, product);
        if (quantityRuleMessage) {
          const itemLabel = item.title.trim() || product?.name || "Item";
          setError(`${itemLabel}: ${quantityRuleMessage}`);
          return;
        }
      }

      const meaningfulItems = items
        .map((it) => ({
          id: it.id,
          product_id: it.product_id || null,
          unit_type: it.unit_type || "unit",
          width_m: it.width_m > 0 ? it.width_m : null,
          height_m: it.height_m > 0 ? it.height_m : null,
          length_m: it.length_m > 0 ? it.length_m : null,
          title: it.title.trim(),
          description: it.description.trim() || null,
          quantity: Math.max(1, it.quantity || 1),
          unit_price: Math.max(0, it.unit_price || 0),
          unit_cost: Math.max(0, it.unit_cost || 0),
          selected_options: it.selected_options && typeof it.selected_options === "object" ? it.selected_options : null,
          minimo: it.minimo && !it.unit_price_locked ? it.minimo : null,
        }))
        .filter((it) => it.title || it.description);

      if (meaningfulItems.length === 0) {
        setError("Adicione ao menos 1 item no orçamento.");
        return;
      }

      if (condicoes) {
        const erroDasCondicoesComerciais = erroDasCondicoes(condicoes, totals.total);
        if (erroDasCondicoesComerciais) {
          setError(erroDasCondicoesComerciais);
          return;
        }
      }

      /* Só os campos ativos são mexidos; o valor de campo desativado fica. */
      let camposProprios: Record<string, unknown> | null = null;
      if (camposAtivosDoOrcamento.length > 0) {
        const novos: Record<string, unknown> = {};
        for (const def of camposAtivosDoOrcamento) novos[def.chave] = camposValores[def.chave] ?? null;
        const conferido = conferirValores(camposDoOrcamento, novos, {
          atuais: valoresDaLinha((quote as { campos_proprios?: unknown }).campos_proprios),
          exigirObrigatorios: true,
        });
        if (!conferido.ok) {
          setCamposErros(conferido.erros);
          setError(`Confira as informações extras: ${Object.values(conferido.erros)[0]}`);
          return;
        }
        camposProprios = conferido.valores;
      }

      const now = new Date().toISOString();
      const expires = form.expires_at ? form.expires_at : null;

      const { error: upErr } = await supabase
        .from("quotes")
        .update({
          client_id: form.client_id,
          status: form.status,
          title: form.title.trim() || null,
          subtotal: totals.subtotal,
          discount: totals.discount,
          shipping: totals.shipping,
          shipping_details: shippingDetails,
          list_price_mode: form.list_price_mode,
          payment_terms: condicoes,
          total: totals.total,
          expires_at: expires,
          delivery_date: form.delivery_date || null,
          notes: form.notes.trim() || null,
          internal_notes: form.internal_notes.trim() || null,
          ...(camposProprios ? { campos_proprios: camposProprios } : {}),
          updated_at: now,
        })
        .eq("id", quote.id)
        .in("user_id", tenantIds);

      if (upErr) throw upErr;

      const itemsPayload = meaningfulItems.map((it, idx) => ({
        id: it.id,
        quote_id: quote.id,
        position: idx + 1,
        product_id: it.product_id,
        unit_type: it.unit_type,
        width_m: it.width_m,
        height_m: it.height_m,
        length_m: it.length_m,
        title: it.title || "Item",
        description: it.description,
        quantity: it.quantity,
        unit_price: it.unit_price,
        unit_cost: it.unit_cost,
        selected_options: it.selected_options || null,
        // O valor mínimo do item: a linha vale exatamente o mínimo.
        valor_minimo_aplicado: it.minimo ? it.minimo.minimo : null,
        valor_sem_minimo: it.minimo && it.minimo.semMinimo != null ? Math.round(it.minimo.semMinimo * 100) / 100 : null,
        /* Arredondado em centavos na hora de gravar. Sem isto, 30 x 16,90 vira
           506.99999999999994 no banco, e depois o pedido gerado a partir daqui
           aparece como "alterado" para sempre, comparando com o proprio 507. */
        line_total: it.minimo
          ? Math.round(it.minimo.minimo * 100) / 100
          : Math.round(
            effectiveQuantity({
              quantity: it.quantity,
              unit_type: it.unit_type,
              width_m: it.width_m || 0,
              height_m: it.height_m || 0,
              length_m: it.length_m || 0,
            }) *
              it.unit_price *
              100
          ) / 100,
      }));

      const idsList = itemsPayload.map((it) => `"${it.id}"`).join(",");
      if (idsList) {
        const { error: delErr } = await supabase
          .from("quote_items")
          .delete()
          .eq("quote_id", quote.id)
          .not("id", "in", `(${idsList})`);
        if (delErr) throw delErr;
      } else {
        const { error: delErr } = await supabase.from("quote_items").delete().eq("quote_id", quote.id);
        if (delErr) throw delErr;
      }

      const { error: upsertErr } = await supabase.from("quote_items").upsert(itemsPayload, { onConflict: "id" });
      if (upsertErr) throw upsertErr;

      const { error: delArtsErr } = await supabase.from("quote_item_arts").delete().eq("quote_id", quote.id);
      if (delArtsErr) throw delArtsErr;

      const chegaram = await artesQueChegaramDeFora();
      const artesParaGravar = Object.keys(chegaram).length ? juntarArtesDeFora(itemArts, chegaram) : itemArts;
      if (artesParaGravar !== itemArts) setItemArts(artesParaGravar);
      const artsPayload = Object.values(artesParaGravar)
        .filter((art) => meaningfulItems.some((it) => it.id === art.quote_item_id))
        .map((art) => ({
          quote_id: quote.id,
          quote_item_id: art.quote_item_id,
          title: art.title.trim() || null,
          notes: art.notes.trim() || null,
          image_urls: art.image_urls || [],
        }))
        .filter((art) => art.title || art.notes || art.image_urls.length > 0);

      if (artsPayload.length > 0) {
        const { error: artsErr } = await supabase.from("quote_item_arts").insert(artsPayload);
        if (artsErr) throw artsErr;
      }

      existingItemIdsRef.current = new Set(itemsPayload.map((it) => it.id));
      setItensGuardados(new Set(itemsPayload.map((it) => it.id)));
      setQuote((prev) =>
        prev ? { ...prev, updated_at: now, total: totals.total, list_price_mode: form.list_price_mode } : prev
      );
    } catch (err) {
      console.error("Erro ao salvar orçamento:", err);
      setError("Erro ao salvar orçamento.");
    } finally {
      setSaving(false);
    }
  }

  /**
   * Excluir o orçamento, aqui de dentro dele.
   *
   * Dois problemas moravam nesta função. O primeiro: ela usava o `confirm()`
   * do navegador, e o Chrome deixa de mostrar essas caixas depois que a mesma
   * página abre algumas seguidas — a partir daí clicar em Excluir não fazia
   * absolutamente nada, sem erro nenhum na tela. O segundo: quando o orçamento
   * já tinha virado pedido, ela tentava apagar assim mesmo. O banco recusa,
   * porque `orders.quote_id` é RESTRICT, e o usuário recebia só um
   * "Erro ao excluir orçamento." que não explicava nada e não dava saída.
   *
   * A lista de orçamentos já fazia certo. Agora esta tela faz igual: quando
   * existe pedido, ela nem tenta e oferece cancelar, que é o caminho real.
   */
  function pedirParaExcluir() {
    if (!quote) return;

    if (quote.orders) {
      setConfirmacao({
        titulo: "Este orçamento não pode ser excluído",
        texto: `Ele gerou o pedido #${quote.orders.number}, e apagar o orçamento deixaria o pedido sem origem. Você pode cancelar o orçamento: ele sai da lista ativa e vai para a aba de cancelados, e o pedido continua como está.`,
        confirmar: "Cancelar orçamento",
        cancelar: "Deixar como está",
        aoConfirmar: () => {
          setConfirmacao(null);
          return handleCancelQuote();
        },
      });
      return;
    }

    setConfirmacao({
      titulo: `Excluir o orçamento #${quote.number}?`,
      texto: "Os itens, as artes e as mensagens de arte saem junto. Isso não tem volta.",
      confirmar: "Excluir orçamento",
      tom: "perigo",
      aoConfirmar: () => {
        setConfirmacao(null);
        return handleDelete();
      },
    });
  }

  async function handleDelete() {
    if (!quote) return;
    setExcluindo(true);
    setErroDaExclusao(null);
    try {
      const supabase = getSupabaseClient();
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) throw new Error("Usuário não autenticado");
      const ctx = await getCompanyContext(supabase, user);
      const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
      const { data: apagados, error } = await supabase
        .from("quotes")
        .delete()
        .eq("id", quote.id)
        .in("user_id", tenantIds)
        .select("id");

      if (error) {
        /* 23503 é violação de chave estrangeira: sobrou algo apontando para
           este orçamento. Hoje só o pedido faz isso. */
        if (error.code === "23503") {
          throw new Error(
            "Este orçamento tem registros ligados a ele que impedem a exclusão. Cancele o orçamento em vez de excluir."
          );
        }
        throw error;
      }

      /* Sem erro e sem linha apagada quer dizer que a regra do banco barrou em
         silêncio, por permissão. Antes isso mandava a pessoa de volta para a
         lista como se tivesse dado certo. */
      if (!apagados || apagados.length === 0) {
        throw new Error("Você não tem permissão para excluir orçamentos. Fale com quem administra a conta.");
      }

      router.push("/dashboard/quotes");
    } catch (err) {
      setErroDaExclusao(err instanceof Error ? err.message : "Não foi possível excluir o orçamento.");
    } finally {
      setExcluindo(false);
    }
  }

  /* Cancelar é o caminho de quem já virou pedido: o orçamento sai da lista
     ativa sem quebrar o que veio depois dele. */
  async function handleCancelQuote() {
    if (!quote) return;
    setExcluindo(true);
    setErroDaExclusao(null);
    try {
      const supabase = getSupabaseClient();
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) throw new Error("Usuário não autenticado");
      const ctx = await getCompanyContext(supabase, user);
      const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];

      const { data: alterados, error } = await supabase
        .from("quotes")
        .update({ status: "rejected" })
        .eq("id", quote.id)
        .in("user_id", tenantIds)
        .select("id");

      if (error) throw error;
      if (!alterados || alterados.length === 0) {
        throw new Error("Você não tem permissão para cancelar orçamentos. Fale com quem administra a conta.");
      }

      router.push("/dashboard/quotes");
    } catch (err) {
      setErroDaExclusao(err instanceof Error ? err.message : "Não foi possível cancelar o orçamento.");
    } finally {
      setExcluindo(false);
    }
  }

  return (
    <div className="relative -mx-5 md:-mx-8 px-5 md:px-8">
      <div className="pointer-events-none absolute inset-x-0 -top-14 h-64 bg-gradient-to-b from-[var(--ks-color-primary-soft)] via-transparent to-transparent blur-2xl opacity-80" />

      {erroDaExclusao ? (
        <div className="relative mb-4 flex items-start justify-between gap-3 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
          <span>{erroDaExclusao}</span>
          <button
            type="button"
            onClick={() => setErroDaExclusao(null)}
            className="font-semibold underline-offset-2 hover:underline"
          >
            Fechar
          </button>
        </div>
      ) : null}

      <CaixaDeConfirmacao pedido={confirmacao} ocupado={excluindo} aoFechar={() => setConfirmacao(null)} />
      {quote?.id ? (
        <JanelaDasMetricas
          aberta={metricasAbertas}
          aoFechar={fecharMetricas}
          quoteId={quote.id}
          metricas={metricasDoLink.metricas}
          erro={metricasDoLink.erro}
          carregando={metricasDoLink.carregando}
          aoRecarregar={metricasDoLink.recarregar}
          aoRenovarLink={podeMexerEmOrcamentos ? handleCopyLink : undefined}
          aoReenviarLink={podeMexerEmOrcamentos && selectedClient?.whatsapp ? handleWhatsAppShare : undefined}
          podeMarcarEquipe={podeMexerEmOrcamentos}
        />
      ) : null}
      <JanelaDaChance
        aberta={chanceAberta}
        aoFechar={fecharChance}
        numero={quote?.number ?? null}
        cliente={selectedClient ? selectedClient.company_name || selectedClient.name || null : null}
        chance={chanceDeFechar.chance}
        erro={chanceDeFechar.erro}
        carregando={chanceDeFechar.carregando}
        aoRecarregar={chanceDeFechar.recarregar}
      />
      {activeItemId && (
        <button
          type="button"
          aria-label="Fechar foco do item"
          onClick={() => setActiveItemId(null)}
          className="fixed inset-0 z-40 bg-slate-950/55 backdrop-blur-[2px]"
        />
      )}
      <div className="space-y-6 relative">
      <AnimatePresence>
        {showDuplicatedNotice ? (
          <motion.div
            initial={{ opacity: 0, y: -10, scale: 0.98 }}
            animate={{ opacity: 1, y: 0, scale: 1 }}
            exit={{ opacity: 0, y: -8, scale: 0.98 }}
            transition={{ duration: 0.2 }}
            className="no-print fixed right-5 top-5 z-[100] rounded-xl border border-emerald-200 bg-emerald-50 pl-4 pr-10 py-3 text-emerald-800 shadow-lg"
          >
            <button
              type="button"
              onClick={() => setShowDuplicatedNotice(false)}
              className="absolute right-2 top-2 inline-flex h-6 w-6 items-center justify-center rounded-md text-emerald-700/80 transition hover:bg-emerald-100 hover:text-emerald-900"
              aria-label="Fechar aviso"
            >
              <X size={14} />
            </button>
            <div className="flex items-center gap-2 text-sm font-semibold">
              <Check size={16} />
              Orçamento duplicado com sucesso.
            </div>
          </motion.div>
        ) : null}
      </AnimatePresence>
      {quote && quote.orders && (
        <div className="bg-blue-50 border border-blue-100 rounded-xl p-4 flex items-center justify-between gap-4 no-print print:hidden">
          <div className="flex items-center gap-3">
            <div className="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600">
              <PackageCheck size={20} />
            </div>
            <div>
              <h3 className="text-blue-900 font-semibold">Este orçamento gerou o Pedido #{quote.orders.number}</h3>
              <p className="text-blue-700 text-sm">O orçamento está vinculado e não pode gerar novos pedidos.</p>
            </div>
          </div>
          <Link
            href={`/dashboard/orders/${quote.orders.id}`}
            className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors flex items-center gap-2"
          >
            Ver Pedido
            <ArrowLeft className="rotate-180" size={16} />
          </Link>
        </div>
      )}

      {isReadOnly && (
        <div className="bg-red-50 border border-red-100 rounded-xl p-4 flex items-start gap-3 text-red-700 no-print">
           <Trash2 className="shrink-0 mt-0.5" size={18} />
           <div>
              <h3 className="font-semibold text-sm">Orçamento Cancelado</h3>
              <p className="text-sm opacity-90 mt-0.5">Este orçamento foi recusado/cancelado e não pode ser editado. Você ainda pode imprimir ou visualizá-lo.</p>
           </div>
        </div>
      )}

      <div className="no-print bg-white/80 backdrop-blur rounded-3xl border border-[var(--ks-color-border-subtle)] px-5 py-4 flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
        <div className="min-w-0">
          <div className="flex items-center gap-2">
            <Link
              href="/dashboard/quotes"
              className="p-2 rounded-2xl border border-[var(--ks-color-border-subtle)] text-[var(--ks-color-text-primary)] hover:bg-[var(--ks-color-bg-soft)] transition-colors"
              aria-label="Voltar"
            >
              <ArrowLeft size={18} />
            </Link>
            <h1 className="text-2xl font-bold text-[var(--ks-color-text-primary)] truncate">
              {quote ? `Orçamento #${quote.number}` : "Orçamento"}
            </h1>
          </div>
          <p className="text-[var(--ks-color-text-muted)] mt-1">Edite itens, status e margem.</p>
        </div>
        {/* No celular, o status (em etapas) e o seletor de modelo ficam numa
            linha abaixo do título; no computador, junto das ações. Fora do
            fieldset, o status precisa da mesma trava de quem só consulta. */}
        <div className="flex items-center gap-2 md:hidden">
          {emEtapas && (
            <div className="min-w-0 flex-1" title="Status do orçamento">
              <CustomSelect
                options={OPCOES_DE_STATUS}
                value={form.status}
                onChange={(value) => setForm((p) => ({ ...p, status: value as QuoteStatus }))}
                disabled={isReadOnly || !podeMexerEmOrcamentos}
              />
            </div>
          )}
          <SeletorDeModelo modelo={paginaEmEtapas.modelo} aoTrocar={paginaEmEtapas.trocarModelo} />
        </div>
        {/* O grupo cresce só no espaço que sobra e quebra linha por dentro,
            para o título nunca virar reticências por causa dos botões. */}
        <div className="hidden md:flex md:min-w-0 md:flex-1 md:flex-wrap md:justify-end items-center gap-2">
          {emEtapas && (
            <div className="w-44" title="Status do orçamento">
              <CustomSelect
                options={OPCOES_DE_STATUS}
                value={form.status}
                onChange={(value) => setForm((p) => ({ ...p, status: value as QuoteStatus }))}
                disabled={isReadOnly || !podeMexerEmOrcamentos}
              />
            </div>
          )}
          <SeletorDeModelo modelo={paginaEmEtapas.modelo} aoTrocar={paginaEmEtapas.trocarModelo} />
          <div className="p-1.5 rounded-2xl border border-[var(--ks-color-border-subtle)] bg-white/70 backdrop-blur-xl shadow-sm flex items-center gap-1.5">
            <button
              type="button"
              onClick={handleWhatsAppShare}
              disabled={sharing || !selectedClient?.whatsapp}
              className={[
                "px-3.5 py-2.5 rounded-xl text-sm font-semibold text-white transition inline-flex items-center gap-2",
                "focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/35 disabled:opacity-60 disabled:cursor-not-allowed",
                "bg-gradient-to-r from-[#25D366] to-[#128C7E] shadow-md shadow-[var(--ks-color-primary)]/20",
                "hover:brightness-105 active:brightness-95",
              ].join(" ")}
            >
              <Send size={16} />
              WhatsApp
            </button>

            <button
              type="button"
              onClick={handlePrint}
              className={[
                "px-3.5 py-2.5 rounded-xl text-sm font-semibold text-white transition inline-flex items-center gap-2",
                "focus:outline-none focus:ring-2 focus:ring-fuchsia-500/30",
                "bg-[linear-gradient(45deg,#feda75_0%,#fa7e1e_25%,#d62976_50%,#962fbf_75%,#4f5bd5_100%)] shadow-md shadow-fuchsia-500/20",
                "hover:brightness-105 active:brightness-95",
              ].join(" ")}
            >
              <FileDown size={16} />
              Baixar PDF
            </button>

            <button
              type="button"
              onClick={handleDuplicateQuote}
              disabled={duplicating}
              className={[
                "px-3.5 py-2.5 rounded-xl text-sm font-semibold transition inline-flex items-center gap-2",
                "focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/30",
                "border border-[var(--ks-color-border-subtle)] bg-white text-[var(--ks-color-text-primary)] hover:bg-[var(--ks-color-bg-soft)] disabled:opacity-60 disabled:cursor-not-allowed",
              ].join(" ")}
            >
              <Copy size={16} />
              {duplicating ? "Duplicando..." : "Criar outro a partir deste"}
            </button>

            {(quote?.status === "draft" || quote?.status === "sent" || quote?.status === "approved" || quote?.status === "rejected") && !quote?.orders && !isReadOnly && (
              <button
                type="button"
                onClick={handleGenerateOrder}
                disabled={generatingOrder || saving}
                className={[
                  "px-3.5 py-2.5 rounded-xl text-sm font-semibold text-white transition inline-flex items-center gap-2",
                  "focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/30",
                  "bg-[var(--ks-color-primary)] hover:bg-[var(--ks-color-primary-strong)] shadow-md shadow-[var(--ks-color-primary)]/20 disabled:opacity-60 disabled:cursor-not-allowed",
                ].join(" ")}
              >
                <PackageCheck size={16} />
                {generatingOrder ? "Gerando..." : "Gerar Pedido"}
              </button>
            )}

          </div>
          {!isReadOnly && (
            <button
              type="button"
              onClick={pedirParaExcluir}
              disabled={excluindo}
              className="px-4 py-2.5 rounded-2xl border border-red-200 text-red-700 bg-white hover:bg-red-50 active:bg-red-100 transition inline-flex items-center gap-2 font-semibold focus:outline-none focus:ring-2 focus:ring-red-500/30 disabled:opacity-50"
            >
              <Trash2 size={16} />
              Excluir
            </button>
          )}
        </div>
      </div>

      {quote ? (
        <FaixaDoOrcamento
          criadoPor={creatorName}
          criadoEm={quote.created_at}
          chance={chanceDeFechar.chance}
          chanceComErro={Boolean(chanceDeFechar.erro)}
          aoAbrirChance={() => {
            setChanceAberta(true);
            void chanceDeFechar.recarregar();
          }}
          linkAtivo={Boolean(quote.share_enabled)}
          linkVenceEm={quote.share_expires_at}
          contadorAntigo={quote.public_view_count ?? 0}
          ultimaAntiga={quote.public_last_viewed_at}
          metricas={metricasDoLink.metricas}
          aoAbrirMetricas={() => {
            setMetricasAbertas(true);
            void metricasDoLink.recarregar();
          }}
          aoCopiarLink={handleCopyLink}
          copiado={copied}
          gerandoLink={sharing}
          prazoConfigurado={diasDeValidade(profile?.quote_share_link_expires_enabled, profile?.quote_share_link_expires_days)}
          enviadoAqui={linkEnviadoAqui}
          encerrado={Boolean(quote.orders) || quote.status === "rejected"}
        />
      ) : null}

      <div className="no-print md:hidden flex items-center gap-2 flex-nowrap overflow-x-auto">
        <button
          type="button"
          onClick={handleWhatsAppShare}
          disabled={sharing || !selectedClient?.whatsapp}
          aria-label="WhatsApp"
          className={[
            "w-10 h-10 rounded-full text-white transition inline-flex items-center justify-center flex-none",
            "focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/35 disabled:opacity-60 disabled:cursor-not-allowed",
            "bg-gradient-to-r from-[#25D366] to-[#128C7E] shadow-md shadow-[var(--ks-color-primary)]/20",
            "hover:brightness-105 active:brightness-95",
          ].join(" ")}
        >
          <Send size={16} />
        </button>

        <button
          type="button"
          onClick={handlePrint}
          aria-label="Baixar PDF"
          className={[
            "w-10 h-10 rounded-full text-white transition inline-flex items-center justify-center flex-none",
            "focus:outline-none focus:ring-2 focus:ring-fuchsia-500/30",
            "bg-[linear-gradient(45deg,#feda75_0%,#fa7e1e_25%,#d62976_50%,#962fbf_75%,#4f5bd5_100%)] shadow-md shadow-fuchsia-500/20",
            "hover:brightness-105 active:brightness-95",
          ].join(" ")}
        >
          <FileDown size={16} />
        </button>

        <button
          type="button"
          onClick={handleDuplicateQuote}
          disabled={duplicating}
          aria-label="Criar outro a partir deste"
          className="w-10 h-10 rounded-full border border-[var(--ks-color-border-subtle)] bg-white text-[var(--ks-color-text-primary)] transition inline-flex items-center justify-center flex-none hover:bg-[var(--ks-color-bg-soft)] disabled:opacity-60 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/30"
        >
          <Copy size={16} />
        </button>

        {(quote?.status === "draft" || quote?.status === "sent" || quote?.status === "approved" || quote?.status === "rejected") && !quote?.orders && !isReadOnly && (
          <button
            type="button"
            onClick={handleGenerateOrder}
            disabled={generatingOrder || saving}
            aria-label={generatingOrder ? "Gerando pedido" : "Gerar pedido"}
            className={[
              "w-10 h-10 rounded-full text-white transition inline-flex items-center justify-center flex-none",
              "focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/30",
              "bg-[var(--ks-color-primary)] hover:bg-[var(--ks-color-primary-strong)] shadow-md shadow-[var(--ks-color-primary)]/20 disabled:opacity-60 disabled:cursor-not-allowed",
            ].join(" ")}
          >
            <PackageCheck size={16} />
          </button>
        )}

        {!isReadOnly && (
          <button
            type="button"
            onClick={pedirParaExcluir}
            disabled={excluindo}
            aria-label="Excluir"
            className="w-10 h-10 rounded-full border border-red-200 text-red-700 bg-white hover:bg-red-50 active:bg-red-100 transition inline-flex items-center justify-center flex-none font-semibold focus:outline-none focus:ring-2 focus:ring-red-500/30 disabled:opacity-50"
          >
            <Trash2 size={16} />
          </button>
        )}
      </div>

      {loading ? (
        <div className="no-print text-center py-10 text-zinc-500">Carregando...</div>
      ) : (
        <form onSubmit={handleSave} className="no-print space-y-6">
          {!podeMexerEmOrcamentos && (
            <div className="rounded-2xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-status-info-soft)] px-4 py-3 text-sm text-[var(--ks-color-text-secondary)]">
              Você pode consultar este orçamento, mas não editá-lo.
            </div>
          )}
          {emEtapas && (
            <Etapas etapa={etapa} feitas={etapasFeitas} aoEscolher={irPara} topoRef={paginaEmEtapas.topoRef} />
          )}
          <fieldset disabled={!podeMexerEmOrcamentos} className="contents">
          <div className="grid grid-cols-1 lg:grid-cols-12 gap-6 items-start">
            <div className={`lg:col-span-8 ${emEtapas ? "flex flex-col gap-6" : "space-y-6"} ${paginaEmEtapas.classeDaTransicao}`}>
          <div
            hidden={fora(1, 5)}
            className={
              emEtapas
                ? `${CARTAO_EM_ETAPAS} flex flex-col gap-4`
                : "bg-white rounded-3xl border border-[var(--ks-color-border-subtle)] p-6 shadow-sm space-y-5"
            }
          >
            {emEtapas && (
              <CabecalhoDoCartao
                hidden={fora(1)}
                icone="dados"
                titulo="Dados do Orçamento"
                descricao="Informações principais para identificar e organizar este orçamento."
              />
            )}
            <div hidden={fora(1)} className="grid grid-cols-1 lg:grid-cols-12 gap-5">
              <div className="space-y-1 lg:col-span-6">
                <div className="flex items-center justify-between gap-2">
                  <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">
                    Cliente
                    {emEtapas && <span className="ml-1 text-[var(--ks-color-status-danger)]">*</span>}
                  </label>
                  {!isReadOnly && (
                    <button
                      hidden={emEtapas}
                      type="button"
                      onClick={() => setClientModalOpen(true)}
                      className="h-7 w-7 rounded-full border border-[var(--ks-color-border-subtle)] text-[var(--ks-color-primary)] hover:bg-[var(--ks-color-bg-soft)] inline-flex items-center justify-center transition-colors"
                      title="Cadastrar cliente"
                      aria-label="Cadastrar cliente"
                    >
                      <Plus size={14} />
                    </button>
                  )}
                </div>
                <div className={emEtapas ? "flex gap-2" : undefined}>
                  <div className="min-w-0 flex-1">
                    <CustomSelect
                      options={clientOptions}
                      value={form.client_id}
                      onChange={(value) => setForm((p) => ({ ...p, client_id: value }))}
                      searchable
                      disabled={isReadOnly}
                    />
                  </div>
                  {/* Em etapas o cadastro rápido fica ao lado do campo, como
                      na proposta; na página única segue junto do rótulo. */}
                  {emEtapas && !isReadOnly && (
                    <button
                      type="button"
                      onClick={() => setClientModalOpen(true)}
                      className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border border-[var(--ks-color-border-subtle)] bg-white text-[var(--ks-color-primary)] transition-colors hover:bg-[var(--ks-color-bg-soft)]"
                      title="Cadastrar cliente"
                      aria-label="Cadastrar cliente"
                    >
                      <Plus size={16} />
                    </button>
                  )}
                </div>
                {selectedClient?.is_deleted ? (
                  <span className="inline-flex rounded-full border border-[rgba(239,68,68,0.25)] bg-[rgba(239,68,68,0.1)] px-2 py-0.5 text-[10px] font-bold uppercase tracking-[0.08em] text-[var(--ks-color-status-danger)]">
                    Cliente Excluído
                  </span>
                ) : null}
              </div>

              <div className={emEtapas ? "space-y-1 lg:col-span-6" : "space-y-1 lg:col-span-4"}>
                <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">Título (opcional)</label>
                <input
                  value={form.title}
                  onChange={(e) => setForm((p) => ({ ...p, title: e.target.value }))}
                  className="w-full px-3 py-2.5 rounded-xl border border-[var(--ks-color-border-subtle)] focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] disabled:bg-[var(--ks-color-bg-soft)] disabled:text-[var(--ks-color-text-muted)]"
                  placeholder="Ex: Fachada em ACM + letras caixa"
                  disabled={isReadOnly}
                />
              </div>

              <div hidden={emEtapas} className="space-y-1 lg:col-span-2">
                <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">Status</label>
                <CustomSelect
                  options={[
                    { value: "draft", label: "Rascunho" },
                    { value: "sent", label: "Enviado" },
                    { value: "approved", label: "Aprovado" },
                    { value: "rejected", label: "Recusado" },
                    { value: "production", label: "Em produção" },
                    { value: "delivered", label: "Entregue" },
                  ]}
                  value={form.status}
                  onChange={(value) => setForm((p) => ({ ...p, status: value as QuoteStatus }))}
                  disabled={isReadOnly}
                />
              </div>
            </div>

            {emEtapas && (
              <CabecalhoDoCartao
                hidden={fora(5)}
                icone="prazos"
                titulo="Prazos"
                descricao="Até quando o orçamento vale e quando o pedido fica pronto."
              />
            )}
            <div hidden={fora(5)} className="grid grid-cols-1 lg:grid-cols-12 gap-5">
              <div className="space-y-1 lg:col-span-6">
                <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">Validade (opcional)</label>
                <input
                  type="date"
                  value={form.expires_at}
                  onChange={(e) => setForm((p) => ({ ...p, expires_at: e.target.value }))}
                  className="w-full px-3 py-2.5 rounded-xl border border-[var(--ks-color-border-subtle)] focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] disabled:bg-[var(--ks-color-bg-soft)] disabled:text-[var(--ks-color-text-muted)]"
                  disabled={isReadOnly}
                />
              </div>
              <div className="space-y-1 lg:col-span-6">
                <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">Data de Entrega</label>
                <input
                  type="date"
                  value={form.delivery_date}
                  onChange={(e) => setForm((p) => ({ ...p, delivery_date: e.target.value }))}
                  className="w-full px-3 py-2.5 rounded-xl border border-[var(--ks-color-border-subtle)] focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] disabled:bg-[var(--ks-color-bg-soft)] disabled:text-[var(--ks-color-text-muted)]"
                  disabled={isReadOnly}
                />
                <CargaDoDia data={form.delivery_date} />
              </div>
            </div>
          </div>

          <div
            hidden={fora(2)}
            className={
              emEtapas
                ? `${CARTAO_EM_ETAPAS} space-y-4`
                : "bg-white rounded-3xl border border-[var(--ks-color-border-subtle)] p-6 shadow-sm space-y-4"
            }
          >
            {emEtapas ? (
              <CabecalhoDoCartao
                icone="itens"
                titulo="Itens"
                descricao="Detalhe quantidade, preço e custo para margem."
                direita={
                  <Chip tom="marca">
                    {items.length} {items.length === 1 ? "item" : "itens"}
                  </Chip>
                }
              />
            ) : (
              <div className="flex items-start gap-3">
                <div>
                  <h2 className="text-lg font-semibold text-[var(--ks-color-text-primary)]">Itens</h2>
                  <p className="text-sm text-[var(--ks-color-text-muted)]">Detalhe quantidade, preço e custo para margem.</p>
                </div>
              </div>
            )}

            <div className="space-y-3">
              {items.map((it, idx) => {
                const usedQuantity = effectiveQuantity(it);
                const lineTotal = totalDaLinhaDoRascunho(it, usedQuantity);
                const minimoDaLinha = it.minimo && !it.unit_price_locked ? it.minimo : null;
                const lineCost = usedQuantity * (it.unit_cost || 0);
                const margin = lineTotal - lineCost;
                const marginPct = lineTotal > 0 ? (margin / lineTotal) * 100 : 0;
                const isMarginLow = marginPct < 30;
                const isLoss = margin < 0;
                const art = itemArts[it.id];
                const artImages = art?.image_urls ?? [];
                const showArtInPdf = itemArtPdfVisibility[it.id] ?? true;
                const artStatus = art?.approval_status || "pending";
                const artStatusLabel =
                  artStatus === "approved" ? "Aprovada" : artStatus === "rejected" ? "Reprovada" : "Pendente";
                const artStatusStyle =
                  artStatus === "approved"
                    ? "bg-[rgba(1,181,116,0.12)] text-[var(--ks-color-status-success)] border-[rgba(1,181,116,0.28)]"
                    : artStatus === "rejected"
                      ? "bg-[rgba(238,93,80,0.12)] text-[var(--ks-color-status-danger)] border-[rgba(238,93,80,0.28)]"
                      : "bg-[rgba(255,181,71,0.18)] text-[var(--ks-color-status-warning)] border-[rgba(255,181,71,0.28)]";
                const artReviewedAt = art?.reviewed_at || art?.approved_at || "";
                const selectedProduct = it.product_id ? productsById.get(it.product_id) : null;
                const quantityRuleMessage = getItemQuantityRuleMessage(it, selectedProduct);
                const allowedQuantities = getAllowedItemQuantities(selectedProduct);
                const usesAllowedQuantities = Boolean(selectedProduct?.allowed_sale_qty_enabled) && allowedQuantities.length > 0;
                const selectedAllowedQuantity = usesAllowedQuantities
                  ? allowedQuantities.find((value) => Math.abs(value - Number(it.quantity || 0)) <= 0.000001)
                  : null;
                const stockRuleMessage =
                  estoqueV2 === true
                    ? (conferenciaDoEstoqueV2.faltas.get(idx) || []).join(" ") || null
                    : getItemVariationStockMessage(it, selectedProduct);

                const accents = [
                  { bg: '#7c3aed', light: '#ede9fe' }, // violet
                  { bg: '#0284c7', light: '#e0f2fe' }, // sky
                  { bg: '#059669', light: '#d1fae5' }, // emerald
                  { bg: '#d97706', light: '#fef3c7' }, // amber
                  { bg: '#db2777', light: '#fce7f3' }, // pink
                  { bg: '#ea580c', light: '#ffedd5' }, // orange
                  { bg: '#0891b2', light: '#cffafe' }, // cyan
                  { bg: '#a21caf', light: '#fae8ff' }, // fuchsia
                  { bg: '#65a30d', light: '#ecfccb' }, // lime
                  { bg: '#4338ca', light: '#e0e7ff' }, // indigo
                ] as const;
                const accent = isLoss
                  ? { bg: '#e11d48', light: '#ffe4e6' }
                  : accents[idx % 10];
                const isItemActive = activeItemId === it.id;
                /* Em etapas o item é uma linha que abre; o único item da
                   lista já nasce aberto. O foco pelo clique só existe na
                   página única — em etapas ele vem pelo botão "Foco". */
                const itemEstaAberto = emEtapas && (itemAberto ? itemAberto === it.id : items.length === 1);
                const temAtributos = Boolean(selectedProduct && getSortedAttributes(selectedProduct).length > 0);
                const temMedidas = it.unit_type === "square_meter" || it.unit_type === "linear_meter" || it.unit_type === "meter";
                const nomeDoItem = it.title.trim() || selectedProduct?.name || "";
                const resumoDasOpcoes = getSelectedOptionsSummary(it);

                return (
                  <div
                    id={`quote-item-${it.id}`}
                    key={it.id}
                    tabIndex={-1}
                    onMouseDownCapture={emEtapas ? undefined : () => setActiveItemId(it.id)}
                    onMouseLeave={() => {
                      if (activeItemId === it.id) setActiveItemId(null);
                    }}
                    className={
                      emEtapas
                        ? classesDoItemEmEtapas(itemEstaAberto, isItemActive)
                        : `relative rounded-3xl overflow-hidden transition-all duration-200 p-5 ${
                            isItemActive
                              ? "z-50 border border-[var(--ks-color-primary)] bg-white ring-2 ring-[var(--ks-color-primary)]/20 shadow-[0_20px_70px_rgba(15,23,42,0.28)]"
                              : "border border-slate-200/70 bg-white shadow-[0_1px_3px_rgba(0,0,0,0.06),0_6px_24px_rgba(0,0,0,0.05)] hover:shadow-[0_4px_32px_rgba(0,0,0,0.12)] hover:-translate-y-0.5"
                          }`
                    }
                  >
                    {isItemActive && (
                      <div
                        className="pointer-events-none absolute inset-y-3 left-0 w-1 rounded-full"
                        style={{ background: accent.bg }}
                        aria-hidden="true"
                      />
                    )}
                    {emEtapas ? (
                      <CabecalhoDoItem
                        numero={idx + 1}
                        nome={nomeDoItem || "Item sem nome"}
                        semNome={!nomeDoItem}
                        chips={
                          <>
                            <Chip>{nomeDaUnidade(it.unit_type)}</Chip>
                            {resumoDasOpcoes && <Chip>{resumoDasOpcoes}</Chip>}
                            {!it.product_id && <Chip>Item manual</Chip>}
                            {artImages.length > 0 && (
                              <Chip tom="marca">
                                {artImages.length} {artImages.length === 1 ? "arte" : "artes"}
                              </Chip>
                            )}
                            {art && (
                              <Chip tom={artStatus === "approved" ? "verde" : artStatus === "rejected" ? "rosa" : "neutro"}>
                                Arte {artStatusLabel.toLowerCase()}
                              </Chip>
                            )}
                          </>
                        }
                        total={formatMoney(lineTotal)}
                        detalhe={
                          minimoDaLinha
                            ? `${descreverQuantidade(usedQuantity, it.unit_type)} · Valor mínimo`
                            : `${descreverQuantidade(usedQuantity, it.unit_type)} × ${formatMoney(it.unit_price || 0)}`
                        }
                        aberto={itemEstaAberto}
                        aoAlternar={() => alternarItem(it.id)}
                        aoSubir={() => moveItem(idx, "up")}
                        aoDescer={() => moveItem(idx, "down")}
                        podeSubir={idx > 0}
                        podeDescer={idx < items.length - 1}
                        mostrarSetas={!isReadOnly}
                      />
                    ) : (
                    <div className="relative z-10 mb-4 flex items-center justify-between gap-3 border-b border-slate-100 pb-3">
                      <div className="flex items-center gap-2">
                        {/* Colored number badge */}
                        <div
                          className="flex h-8 min-w-[2rem] items-center justify-center rounded-xl px-2 text-sm font-black text-white select-none"
                          style={{ background: accent.bg, boxShadow: `0 2px 8px ${accent.bg}55` }}
                          aria-label={`Item ${idx + 1}`}
                        >
                          {idx + 1}
                        </div>
                        <div className="w-px h-5 bg-slate-200 rounded-full" />
                        {!isReadOnly && (
                          <>
                            <button
                              type="button"
                              onClick={() => moveItem(idx, "up")}
                              disabled={idx === 0}
                              className="inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-200/80 text-slate-400 transition-colors hover:bg-slate-50 hover:text-slate-600 disabled:cursor-not-allowed disabled:opacity-30"
                              aria-label="Mover item para cima"
                            >
                              <ArrowUp size={14} />
                            </button>
                            <button
                              type="button"
                              onClick={() => moveItem(idx, "down")}
                              disabled={idx === items.length - 1}
                              className="inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-200/80 text-slate-400 transition-colors hover:bg-slate-50 hover:text-slate-600 disabled:cursor-not-allowed disabled:opacity-30"
                              aria-label="Mover item para baixo"
                            >
                              <ArrowDown size={14} />
                            </button>
                            <button
                              type="button"
                              onClick={() => duplicateItem(idx)}
                              className="inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-200/80 text-slate-400 transition-colors hover:bg-slate-50 hover:text-slate-600"
                              aria-label="Duplicar item"
                              title="Duplicar item"
                            >
                              <Copy size={14} />
                            </button>
                          </>
                        )}
                      </div>
                      {/* Delete — right side */}
                      {!isReadOnly && (
                        <button
                          type="button"
                          onClick={() => removeItem(idx)}
                          disabled={items.length === 1}
                          className="inline-flex h-8 w-8 items-center justify-center rounded-xl border border-slate-200/80 text-slate-400 transition-colors hover:bg-red-50 hover:text-red-500 hover:border-red-200 disabled:opacity-30 disabled:cursor-not-allowed"
                          aria-label="Remover item"
                          title="Remover item"
                        >
                          <Trash2 size={14} />
                        </button>
                      )}
                    </div>
                    )}

                    <div hidden={emEtapas && !itemEstaAberto} className={emEtapas ? CORPO_DO_ITEM_EM_ETAPAS : undefined}>
                    <div className={emEtapas ? "grid grid-cols-1 sm:grid-cols-2 gap-3.5" : "grid grid-cols-1 lg:grid-cols-12 gap-3"}>
                      <div
                        className={emEtapas ? (temAtributos ? "space-y-1" : "space-y-1 sm:col-span-2") : "lg:col-span-12 space-y-1"}
                        data-item-primary-field="true"
                      >
                        <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">Produto</label>
                        <CustomSelect
                          options={[
                            { value: "", label: "Item manual (Avulso)" },
                            ...products
                              .filter((p) => p.id && (p.product_kind !== "digital" || p.id === it.product_id))
                              .map((p) => ({
                                value: p.id,
                                label: `${p.name} - ${p.price_on_request ? "Sob consulta" : formatMoney(p.price || 0)}`,
                                image_url: p.image_url || "https://placehold.co/100x100/f4f4f5/a1a1aa?text=IMG",
                              })),
                          ]}
                          value={it.product_id}
                          onChange={(value) => {
                            if (!value) {
                              updateItem(idx, { product_id: "", unit_type: "unit", unit_price_locked: false, selected_options: null });
                              return;
                            }
                            const selected = productsById.get(value);
                            if (!selected) return;
                            const minQty = getMinimumItemQuantity(selected);
                            const computedCost = selected.cost_total || 0;
                            const nextDraft: QuoteItemDraft = {
                              ...it,
                              product_id: selected.id,
                              title: selected.name,
                              description: it.description,
                              unit_type: selected.unit_type || "unit",
                              width_m: 0,
                              height_m: 0,
                              length_m: 0,
                              quantity: Math.max(minQty, it.quantity || minQty),
                              unit_price: 0,
                              unit_cost: computedCost || 0,
                              unit_price_locked: false,
                              selected_options: null,
                            };
                            const tierPrice = resolveProductUnitPrice(nextDraft, selected);
                            updateItem(idx, {
                              product_id: selected.id,
                              title: selected.name,
                              unit_price: tierPrice,
                              unit_cost: computedCost || 0,
                              unit_type: selected.unit_type || "unit",
                              width_m: 0,
                              height_m: 0,
                              length_m: 0,
                              quantity: Math.max(minQty, it.quantity || minQty),
                              unit_price_locked: false,
                              selected_options: null,
                            });
                          }}
                          searchable
                          placeholder="Selecione um produto..."
                          disabled={isReadOnly}
                        />
                      </div>
                      {selectedProduct && getSortedAttributes(selectedProduct).length > 0 && (
                        <div className={emEtapas ? "space-y-1" : "lg:col-span-12 space-y-1"}>
                          <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">Variações</label>
                          <div className="rounded-xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-soft)] px-3 py-2">
                            <div className="text-xs text-[var(--ks-color-text-muted)]">
                              {getSelectedOptionsSummary(it) || "Nenhuma opção selecionada"}
                            </div>
                          </div>
                        </div>
                      )}
                      {!it.product_id && (
                        <div className={emEtapas ? "space-y-1 sm:col-span-2" : "lg:col-span-12 space-y-1"}>
                          <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">Item</label>
                          <input
                            value={it.title}
                            onChange={(e) => updateItem(idx, { title: e.target.value })}
                            className="w-full px-3 py-2.5 rounded-xl border border-[var(--ks-color-border-subtle)] focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] disabled:bg-[var(--ks-color-bg-soft)] disabled:text-[var(--ks-color-text-muted)]"
                            disabled={isReadOnly}
                          />
                          {!isReadOnly && (
                            <button
                              type="button"
                              onClick={() => registerManualItemAsProduct(it.id)}
                              disabled={!canRegisterManualItem(it) || manualProductSubmittingItemId === it.id}
                              className="inline-flex h-6 items-center rounded-md border border-[var(--ks-color-primary-soft-strong)] px-2 text-[11px] font-semibold text-[var(--ks-color-primary)] hover:bg-[rgba(68,37,167,0.08)] disabled:opacity-45 disabled:cursor-not-allowed"
                            >
                              {manualProductSubmittingItemId === it.id ? "Cadastrando..." : "Cadastrar produto"}
                            </button>
                          )}
                          {manualProductErrors[it.id] ? (
                            <p className="text-[11px] font-medium text-rose-600">{manualProductErrors[it.id]}</p>
                          ) : null}
                        </div>
                      )}
                      <div className={emEtapas ? "space-y-1" : "lg:col-span-6 space-y-1"}>
                        <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">Unidade</label>
                        <div className={it.product_id ? "opacity-60 pointer-events-none" : ""}>
                          <CustomSelect
                            options={[
                              { value: "unit", label: "Unidade" },
                              { value: "meter", label: "Metro" },
                              { value: "linear_meter", label: "Metro Linear" },
                              { value: "square_meter", label: "Metro Quadrado" },
                              { value: "box", label: "Caixa" },
                              { value: "ream", label: "Resma" },
                            ]}
                            value={it.unit_type}
                            onChange={(value) => updateItem(idx, { unit_type: value })}
                            disabled={isReadOnly}
                          />
                        </div>
                      </div>
                      <div className={emEtapas ? "space-y-1" : "lg:col-span-6 space-y-1"}>
                        <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">Qtd</label>
                        {usesAllowedQuantities ? (
                          <CustomSelect
                            options={allowedQuantities.map((value) => ({
                              value: String(value),
                              label: formatQuantityLimit(value),
                            }))}
                            value={String(selectedAllowedQuantity ?? allowedQuantities[0])}
                            onChange={(value) => {
                              const parsed = Number(value);
                              if (!Number.isFinite(parsed) || parsed <= 0) return;
                              updateItem(idx, { quantity: parsed });
                            }}
                            disabled={isReadOnly}
                          />
                        ) : (
                          <input
                            value={quantityDrafts[it.id] ?? String(it.quantity)}
                            min={getMinimumItemQuantity(selectedProduct)}
                            onChange={(e) => handleQuantityDraftChange(idx, it.id, e.target.value, getMinimumItemQuantity(selectedProduct))}
                            onBlur={() => commitQuantityDraft(idx, it.id, getMinimumItemQuantity(selectedProduct))}
                            onKeyDown={(e) => {
                              if (e.key === "Enter") {
                                e.preventDefault();
                                commitQuantityDraft(idx, it.id, getMinimumItemQuantity(selectedProduct));
                                (e.currentTarget as HTMLInputElement).blur();
                              }
                            }}
                            className="w-full px-3 py-2.5 rounded-xl border border-[var(--ks-color-border-subtle)] focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] disabled:bg-[var(--ks-color-bg-soft)] disabled:text-[var(--ks-color-text-muted)]"
                            inputMode="decimal"
                            disabled={isReadOnly}
                          />
                        )}
                      </div>
                    </div>

                    <div className={emEtapas ? "mt-3.5 grid grid-cols-2 items-end gap-3.5 sm:grid-cols-4" : "mt-3 grid grid-cols-1 lg:grid-cols-12 lg:items-end gap-3"}>

                      {it.unit_type === "square_meter" ? (
                        <>
                          <div className={emEtapas ? "space-y-1" : "lg:col-span-2 space-y-1"}>
                            <label className="text-sm font-medium text-zinc-700">Largura (m)</label>
                            <input
                              value={measureDrafts[it.id]?.width_m ?? formatMeasureInput(it.width_m)}
                              onChange={(e) => {
                                const nextValue = formatMeasureInput(e.target.value);
                                setMeasureDrafts((prev) => {
                                  const base = prev[it.id] ?? {
                                    width_m: formatMeasureInput(it.width_m),
                                    height_m: formatMeasureInput(it.height_m),
                                    length_m: formatMeasureInput(it.length_m),
                                  };
                                  return { ...prev, [it.id]: { ...base, width_m: nextValue } };
                                });
                                updateItem(idx, { width_m: parseMeasureInput(nextValue) });
                              }}
                              className="w-full px-3 py-2.5 rounded-xl border border-zinc-200 focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] disabled:bg-zinc-50 disabled:text-zinc-500"
                              inputMode="decimal"
                              disabled={isReadOnly}
                            />
                          </div>
                          <div className={emEtapas ? "space-y-1" : "lg:col-span-2 space-y-1"}>
                            <label className="text-sm font-medium text-zinc-700">Altura (m)</label>
                            <input
                              value={measureDrafts[it.id]?.height_m ?? formatMeasureInput(it.height_m)}
                              onChange={(e) => {
                                const nextValue = formatMeasureInput(e.target.value);
                                setMeasureDrafts((prev) => {
                                  const base = prev[it.id] ?? {
                                    width_m: formatMeasureInput(it.width_m),
                                    height_m: formatMeasureInput(it.height_m),
                                    length_m: formatMeasureInput(it.length_m),
                                  };
                                  return { ...prev, [it.id]: { ...base, height_m: nextValue } };
                                });
                                updateItem(idx, { height_m: parseMeasureInput(nextValue) });
                              }}
                              className="w-full px-3 py-2.5 rounded-xl border border-zinc-200 focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] disabled:bg-zinc-50 disabled:text-zinc-500"
                              inputMode="decimal"
                              disabled={isReadOnly}
                            />
                          </div>
                        </>
                      ) : it.unit_type === "linear_meter" || it.unit_type === "meter" ? (
                        <div className={emEtapas ? "col-span-2 space-y-1" : "lg:col-span-4 space-y-1"}>
                          <label className="text-sm font-medium text-zinc-700">Comprimento (m)</label>
                          <input
                            value={measureDrafts[it.id]?.length_m ?? formatMeasureInput(it.length_m)}
                            onChange={(e) => {
                              const nextValue = formatMeasureInput(e.target.value);
                              setMeasureDrafts((prev) => {
                                const base = prev[it.id] ?? {
                                  width_m: formatMeasureInput(it.width_m),
                                  height_m: formatMeasureInput(it.height_m),
                                  length_m: formatMeasureInput(it.length_m),
                                };
                                return { ...prev, [it.id]: { ...base, length_m: nextValue } };
                              });
                              updateItem(idx, { length_m: parseMeasureInput(nextValue) });
                            }}
                            className="w-full px-3 py-2.5 rounded-xl border border-zinc-200 focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] disabled:bg-zinc-50 disabled:text-zinc-500"
                            inputMode="decimal"
                            disabled={isReadOnly}
                          />
                        </div>
                      ) : (
                        <div hidden={emEtapas} className="lg:col-span-4" />
                      )}

                      <div className={emEtapas ? (temMedidas ? "space-y-1" : "col-span-2 space-y-1") : "lg:col-span-3 space-y-1"}>
                        <div className="flex items-center gap-1">
                          <label className="text-sm font-medium text-zinc-700">Preço</label>
                          {it.product_id && (
                            <button
                              type="button"
                              onClick={() => toggleItemPriceLock(idx)}
                              className={`inline-flex h-6 w-6 items-center justify-center rounded-full border text-[10px] transition-colors ${
                                it.unit_price_locked
                                  ? "border-amber-300 bg-amber-50 text-amber-700"
                                  : "border-zinc-200 text-zinc-500 hover:bg-zinc-50"
                              }`}
                              title={it.unit_price_locked ? "Preço travado manualmente" : "Preço automático por faixa"}
                              disabled={isReadOnly}
                            >
                              {it.unit_price_locked ? <Lock size={12} /> : <Unlock size={12} />}
                            </button>
                          )}
                        </div>
                        <input
                          value={formatMoneyInput(it.unit_price)}
                          onChange={(e) => updateItem(idx, { unit_price: parseMoneyInput(e.target.value) }, { manualPrice: true })}
                          className="w-full px-3 py-2.5 rounded-xl border border-zinc-200 focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] disabled:bg-zinc-50 disabled:text-zinc-500"
                          inputMode="numeric"
                          disabled={isReadOnly}
                        />
                      </div>
                      <div className={emEtapas ? (temMedidas ? "space-y-1" : "col-span-2 space-y-1") : "lg:col-span-3 space-y-1"}>
                        <div className="flex items-center gap-1">
                          <label className="text-sm font-medium text-zinc-700">Custo Unit.</label>
                          <InfoTooltip text="Custo total estimado por unidade para cálculo de margem." />
                        </div>
                        <input
                          value={formatMoneyInput(it.unit_cost)}
                          onChange={(e) => updateItem(idx, { unit_cost: parseMoneyInput(e.target.value) })}
                          className="w-full px-3 py-2.5 rounded-xl border border-zinc-200 focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] disabled:bg-zinc-50 disabled:text-zinc-500"
                          inputMode="numeric"
                          disabled={isReadOnly}
                        />
                      </div>

                      <div hidden={emEtapas} className="lg:col-span-2 flex items-end justify-end">
                        <div className="text-right">
                          <div className="text-xs text-[var(--ks-color-text-soft)]">
                            Total
                            {it.unit_type === "unit" || it.unit_type === "box" || it.unit_type === "ream" ? null : (
                              <div className="whitespace-nowrap">{resumoDaMedida(it)}</div>
                            )}
                          </div>
                          <div className="text-sm font-semibold text-[var(--ks-color-text-primary)]">{formatMoney(lineTotal)}</div>
                          {(it.unit_price > 0 || it.unit_cost > 0) && (
                            <div className={`text-[10px] mt-1 font-medium flex items-center justify-end gap-1 ${
                              isLoss ? "text-rose-600" : isMarginLow ? "text-amber-600" : "text-emerald-600"
                            }`}>
                              {isLoss ? "Prejuízo" : "Margem"}: {marginPct.toFixed(1)}%
                              {isLoss && <ArrowLeft className="rotate-[-45deg]" size={10} />}
                            </div>
                          )}
                        </div>
                      </div>
                    </div>
                    {emEtapas && (
                      <ContaDoItem
                        medida={temMedidas ? resumoDaMedida(it) : "Total do item"}
                        total={formatMoney(lineTotal)}
                        margemPct={marginPct}
                        prejuizo={isLoss}
                        mostrarMargem={it.unit_price > 0 || it.unit_cost > 0}
                      />
                    )}
                    {/* Observação individual do item */}
                    <div className="mt-3 space-y-1">
                      <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">
                        Observação do item <span className="text-xs font-normal text-[var(--ks-color-text-muted)]">(opcional — aparece no PDF e no link público)</span>
                      </label>
                      <textarea
                        value={it.description}
                        onChange={(e) => updateItem(idx, { description: e.target.value })}
                        className="w-full px-3 py-2.5 rounded-xl border border-[var(--ks-color-border-subtle)] focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] bg-white text-sm resize-none min-h-[60px] disabled:bg-[var(--ks-color-bg-soft)] disabled:text-[var(--ks-color-text-muted)]"
                        placeholder="Ex: Arte aprovada, sangria 3mm, laminação fosca..."
                        rows={2}
                        disabled={isReadOnly}
                      />
                    </div>
                  {minimoDaLinha && (
                    <AvisoDoMinimo minimo={minimoDaLinha.minimo} semMinimo={minimoDaLinha.semMinimo} />
                  )}
                  {quantityRuleMessage && (
                    <div className="mt-3 rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-xs font-medium text-amber-700">
                      {quantityRuleMessage}
                    </div>
                  )}
                  {stockRuleMessage && (
                    <div className="mt-3 rounded-xl border border-rose-200 bg-rose-50 px-3 py-2 text-xs font-medium text-rose-700">
                      {stockRuleMessage}
                    </div>
                  )}
                  {selectedProduct && getSortedAttributes(selectedProduct).length > 0 && (
                    <div className="mt-3 rounded-2xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-soft)] p-3">
                      <div className="space-y-3">
                        {getSortedAttributes(selectedProduct).map((attribute) => {
                          const selectedMap = getItemVariationMap(it);
                          const selectedValues = selectedMap[attribute.name] || [];
                          const normalizedOptions = (attribute.options || [])
                            .map((option, optionIndex) => normalizeAttributeOption(option, optionIndex))
                            .sort((a, b) => Number(a.position ?? 0) - Number(b.position ?? 0));
                          return (
                            <div key={`${it.id}-${attribute.name}`} className="space-y-2">
                              <div className="flex items-center gap-2">
                                <span className="text-xs font-semibold text-[var(--ks-color-text-primary)]">{attribute.name}</span>
                                {attribute.is_required && (
                                  <span className="text-[10px] rounded-full bg-rose-100 px-2 py-0.5 text-rose-700">obrigatório</span>
                                )}
                              </div>
                              <div className="flex flex-wrap gap-2">
                                {normalizedOptions.map((option) => {
                                  const active = selectedValues.includes(option.label);
                                  const priceModeSuffix = option.price_applies_per_quantity === false ? " (1x)" : "";
                                  const priceText = option.price > 0 ? ` +${formatMoney(option.price)}${priceModeSuffix}` : "";
                                  return (
                                    <button
                                      type="button"
                                      key={`${it.id}-${attribute.name}-${option.label}`}
                                      onClick={() => updateItemVariationSelection(idx, selectedProduct, attribute, option.label)}
                                      disabled={isReadOnly}
                                      className={`rounded-full border px-3 py-1 text-xs transition-colors ${
                                        active
                                          ? "border-[var(--ks-color-primary)] bg-[var(--ks-color-primary)]/10 text-[var(--ks-color-primary)]"
                                          : "border-[var(--ks-color-border-subtle)] bg-white text-[var(--ks-color-text-muted)] hover:border-[var(--ks-color-primary)]/50"
                                      } disabled:opacity-60`}
                                    >
                                      {option.label}{priceText}
                                    </button>
                                  );
                                })}
                              </div>
                            </div>
                          );
                        })}
                      </div>
                    </div>
                  )}
                  {(!isReadOnly || artImages.length > 0) && (
                    <div className="mt-4 rounded-2xl border border-[var(--ks-color-border-subtle)] bg-white p-3">
                      <div className="flex flex-col gap-3">
                        <div className="flex flex-wrap items-center justify-between gap-2">
                          <div className="flex items-center gap-2 min-w-0">
                            <span className="text-[10px] font-bold tracking-[0.16em] uppercase text-[var(--ks-color-text-soft)]">
                              Arte
                            </span>
                            {art ? (
                              <span className={`px-2 py-1 rounded-full border font-semibold text-[10px] ${artStatusStyle}`}>
                                {artStatusLabel}
                              </span>
                            ) : (
                              <span className="px-2 py-1 rounded-full border border-[var(--ks-color-border-subtle)] text-[10px] font-semibold text-[var(--ks-color-text-soft)]">
                                Sem status
                              </span>
                            )}
                            <span className="text-[10px] text-[var(--ks-color-text-soft)] truncate">
                              {artImages.length} imagem(ns)
                            </span>
                          </div>
                          {artReviewedAt && (
                            <span className="text-[10px] text-[var(--ks-color-text-soft)]">
                              Revisado em {formatDateBr(artReviewedAt)}
                            </span>
                          )}
                        </div>

                        <div className="flex flex-wrap items-center gap-2">
                          {!isReadOnly && (
                            <button
                              type="button"
                              onClick={() => openArtModal(it.id)}
                              className="group relative h-8 px-3 rounded-full bg-[var(--ks-color-bg-soft)] text-[11px] font-semibold inline-flex items-center justify-center gap-2 border border-[var(--ks-color-border-subtle)] hover:bg-[rgba(68,37,167,0.08)] transition-colors"
                            >
                              <Plus size={14} className="text-[var(--ks-color-brand-dark)]" />
                              <span className="text-[10px] font-semibold text-[var(--ks-color-text-primary)] whitespace-nowrap">
                                Adicionar arte
                              </span>
                            </button>
                          )}
                          {!isReadOnly && quote && itensGuardados.has(it.id) && (
                            <BotaoDoDesignNoItem
                              nome={`Orçamento #${quote.number}${it.title?.trim() ? ` · ${it.title.trim()}` : ""}`}
                              vinculo={{ orcamento: quote.id, item: it.id }}
                              arteId={artesDoDesign[it.id] ?? null}
                              className="h-8 px-3 rounded-full bg-white border border-[#DDD6FE] text-[10px] font-semibold text-[#5B21B6] hover:bg-[#F5F3FF] transition-colors"
                            />
                          )}
                          {artImages.length > 0 && (
                            <div className="h-8 rounded-full bg-white border border-[var(--ks-color-border-subtle)] flex items-center gap-2 px-3">
                              <span className="text-[10px] font-semibold text-[var(--ks-color-text-primary)]">
                                Mostrar no PDF
                              </span>
                              <Switch
                                checked={showArtInPdf}
                                onCheckedChange={(checked) =>
                                  setItemArtPdfVisibility((prev) => ({ ...prev, [it.id]: checked }))
                                }
                                disabled={isReadOnly}
                              />
                            </div>
                          )}
                          </div>
                          {artImages.length === 0 && (
                            <div className="text-xs text-[var(--ks-color-text-muted)] px-1">
                              Nenhuma arte enviada.
                            </div>
                          )}
                          {artImages.length > 0 && (
                            <>
                              <div className="grid grid-cols-[repeat(auto-fill,minmax(56px,1fr))] gap-2">
                                {artImages.map((url, index) => (
                                  <div
                                    key={`${url}-${index}`}
                                  className="group relative aspect-square rounded-xl bg-[var(--ks-color-bg-soft)] overflow-hidden border border-[var(--ks-color-border-subtle)] cursor-pointer"
                                  onClick={() => handleArtThumbnailClick(it.id, url, artStatus)}
                                  >
                                    <div className="absolute inset-0 bg-gradient-to-br from-white/0 via-white/0 to-white/40 opacity-0 group-hover:opacity-100 transition-opacity" />
                                    {artStatus === "rejected" && (
                                      <div className="absolute inset-0 bg-rose-600/75 flex items-center justify-center text-white text-[10px] font-bold uppercase tracking-wider pointer-events-none">
                                        Substituir
                                      </div>
                                    )}
                                    <Image
                                      src={url}
                                      alt="Arte"
                                      fill
                                      sizes="56px"
                                      className="object-cover transition-transform duration-300 group-hover:scale-110"
                                      unoptimized
                                    />
                                  </div>
                                ))}
                              </div>
                            </>
                          )}
                          {art && (
                            <div className="rounded-2xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-soft)] p-3">
                              {art.approval_notes && (
                                <div className="text-xs text-[var(--ks-color-text-muted)]">
                                  Obs: <span className="text-[var(--ks-color-text-primary)]">{art.approval_notes}</span>
                                </div>
                              )}
                              <div className="mt-2 flex flex-wrap items-center gap-2">
                                {!isReadOnly && !isQuoteFinalized && (
                                  <button
                                    type="button"
                                    onClick={() => art && openArtChat(it, idx, art)}
                                    disabled={!art?.id}
                                    className="h-8 px-3 rounded-full border border-[var(--ks-color-brand-dark)] text-[var(--ks-color-brand-dark)] hover:bg-white transition-colors text-xs font-semibold inline-flex items-center gap-1.5 disabled:cursor-not-allowed disabled:opacity-60"
                                  >
                                    <MessageCircle size={14} />
                                    Abrir chat com cliente
                                  </button>
                                )}
                                <button
                                  type="button"
                                  onClick={() => art && openArtChat(it, idx, art)}
                                  disabled={!art?.id}
                                  className="h-8 px-3 rounded-full border border-[var(--ks-color-border-subtle)] text-[var(--ks-color-text-primary)] hover:bg-white transition-colors text-xs font-semibold inline-flex items-center gap-1.5 disabled:cursor-not-allowed disabled:opacity-60"
                                >
                                  <MessageCircle size={14} />
                                  Histórico de conversa
                                </button>
                              </div>
                            </div>
                          )}
                        </div>
                      </div>
                  )}
                  {emEtapas && !isReadOnly && (
                    <RodapeDoItem
                      aoFocar={() => setActiveItemId(it.id)}
                      aoDuplicar={() => duplicateItem(idx)}
                      aoRemover={() => removeItem(idx)}
                      podeRemover={items.length > 1}
                    />
                  )}
                  </div>
                  </div>
                );
              })}
            </div>
            {!isReadOnly &&
              (emEtapas ? (
                <BotaoAdicionarItem onClick={addItem} />
              ) : (
                <div className="flex items-center pt-1">
                  <button
                    type="button"
                    onClick={addItem}
                    className="w-10 h-10 rounded-2xl bg-[var(--ks-color-brand-dark)] text-white transition-all hover:opacity-90 active:scale-95 inline-flex items-center justify-center shadow-md"
                    title="Adicionar item"
                    aria-label="Adicionar item"
                  >
                    <Plus size={18} />
                  </button>
                </div>
              ))}
          </div>

          {/* A aba Financeiro: as condições comerciais. Em etapas ela é a
              etapa 3; na página única, um cartão logo depois dos itens. */}
          <div
            hidden={fora(3)}
            className={emEtapas ? undefined : "bg-white rounded-3xl border border-[var(--ks-color-border-subtle)] p-6 shadow-sm space-y-5"}
          >
            {/* Em etapas, com condições, a chave mora no canto do cartão "Formas de
                pagamento"; desligada (ou na página única), é a linha de título. */}
            {!(emEtapas && condicoes) && (
              <ChaveDasCondicoes
                ligada={condicoes !== null}
                emEtapas={emEtapas}
                desabilitado={isReadOnly}
                aoMudar={alternarCondicoes}
              />
            )}
            {condicoes && (
              <CondicoesComerciaisEditor
                chave={emEtapas ? <InterruptorDasCondicoes ligada aoMudar={alternarCondicoes} desabilitado={isReadOnly} /> : undefined}
                valor={condicoes}
                aoMudar={setCondicoes}
                total={totals.total}
                dataDeEntrega={form.delivery_date || null}
                emEtapas={emEtapas}
                desabilitado={isReadOnly}
                modelos={{
                  lista: modelosDaEmpresa,
                  podeGerenciar: ehDonoDaEmpresa,
                  aoUsar: setCondicoes,
                  aoMudarLista: setModelosDaEmpresa,
                }}
              />
            )}
            {quote?.orders && (
              <p className="rounded-2xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-soft)] px-4 py-3 text-xs text-[var(--ks-color-text-muted)]">
                O pedido #{quote.orders.number} nasceu com as condições combinadas na aprovação. O que mudar aqui fica só no orçamento.
              </p>
            )}
          </div>

          {emEtapas && (
            <EtapaFinanceiro
              hidden={fora(4)}
              linhas={items.flatMap((i, idx) => {
                if (!i.title.trim() && !i.description.trim()) return [];
                const quantidade = effectiveQuantity(i);
                const precoUnitario = i.unit_price || 0;
                return [
                  {
                    id: i.id,
                    nome: i.title.trim() || `Item ${idx + 1}`,
                    detalhe: [getSelectedOptionsSummary(i), `${descreverQuantidade(quantidade, i.unit_type)} × ${formatMoney(precoUnitario)}`]
                      .filter(Boolean)
                      .join(" · "),
                    total: quantidade * precoUnitario,
                    custo: quantidade * (i.unit_cost || 0),
                  },
                ];
              })}
              totais={totals}
              podeVerCustos={podeVerCustos}
            />
          )}

          <div
            hidden={fora(5, 6)}
            className={
              emEtapas
                ? `${CARTAO_EM_ETAPAS} order-2 flex flex-col gap-4`
                : "bg-white rounded-3xl border border-[var(--ks-color-border-subtle)] p-6 shadow-sm"
            }
          >
            {emEtapas && (
              <>
                <CabecalhoDoCartao
                  hidden={fora(5)}
                  icone="conversa"
                  titulo="Observações para o cliente"
                  descricao="Saem no PDF e no link público."
                />
                <CabecalhoDoCartao
                  hidden={fora(6)}
                  icone="notas"
                  titulo="Configurações"
                  descricao="Notas internas e o que o cliente vê no PDF e no link."
                />
              </>
            )}
            <div className={emEtapas ? "grid grid-cols-1 gap-4" : "grid grid-cols-1 lg:grid-cols-2 gap-4"}>
              <div hidden={fora(5)} className="space-y-1">
                <label className={emEtapas ? "sr-only" : "text-sm font-medium text-[var(--ks-color-text-primary)]"}>
                  Observações para o cliente (opcional)
                </label>
                <textarea
                  value={form.notes}
                  onChange={(e) => setForm((p) => ({ ...p, notes: e.target.value }))}
                  className="w-full px-3 py-2.5 rounded-xl border border-[var(--ks-color-border-subtle)] focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] min-h-[110px] disabled:bg-[var(--ks-color-bg-soft)] disabled:text-[var(--ks-color-text-muted)]"
                  disabled={isReadOnly}
                />
              </div>
              <div hidden={fora(6)} className="space-y-1">
                <label className="text-sm font-medium text-[var(--ks-color-text-primary)]">Notas internas (opcional)</label>
                <textarea
                  value={form.internal_notes}
                  onChange={(e) => setForm((p) => ({ ...p, internal_notes: e.target.value }))}
                  className="w-full px-3 py-2.5 rounded-xl border border-[var(--ks-color-border-subtle)] focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] min-h-[110px] disabled:bg-[var(--ks-color-bg-soft)] disabled:text-[var(--ks-color-text-muted)]"
                  disabled={isReadOnly}
                />
              </div>
              {/* Em etapas a Lista de Preço também mora em Configurações.
                  É o mesmo interruptor do resumo, ligado ao mesmo campo. */}
              {emEtapas && (
                <div hidden={fora(6)}>
                  <BlocoListaDePreco
                    checked={form.list_price_mode}
                    onCheckedChange={(checked) => setForm((p) => ({ ...p, list_price_mode: checked }))}
                    disabled={isReadOnly}
                  />
                </div>
              )}
            </div>
            {camposAtivosDoOrcamento.length > 0 && (
              <div hidden={fora(6)} className={emEtapas ? "space-y-3" : "mt-5 space-y-3 border-t border-[var(--ks-color-border-subtle)] pt-5"}>
                <div>
                  <p className="text-sm font-semibold text-[var(--ks-color-text-primary)]">Informações extras</p>
                  <p className="text-xs text-[var(--ks-color-text-muted)]">Campos criados pela sua empresa. Ficam só no painel; os que existirem também no pedido passam para ele na aprovação.</p>
                </div>
                <CamposDoFormulario
                  definicoes={camposDoOrcamento}
                  valores={camposValores}
                  erros={camposErros}
                  prefixo="orcamento-cp"
                  desabilitado={isReadOnly}
                  aoMudar={(chave, valor) => {
                    setCamposValores((atual) => ({ ...atual, [chave]: valor }));
                    setCamposErros((atual) => {
                      if (!atual[chave]) return atual;
                      const resto = { ...atual };
                      delete resto[chave];
                      return resto;
                    });
                  }}
                />
              </div>
            )}
          </div>
              {!isReadOnly && (
                <div hidden={fora(5)} className={emEtapas ? "order-1" : "mt-6"}>
                  <ShippingCalculator
                    initialZip={selectedClient?.address_zip ? selectedClient.address_zip.replace(/\D/g, "").slice(0, 8) : ""}
                    selectedOption={shippingDetails}
                    onSelectShipping={(option) => {
                      if (option) {
                        setForm((p) => ({ ...p, shipping: formatMoneyInput(option.price) }));
                        setShippingDetails(option);
                      } else {
                        setForm((p) => ({ ...p, shipping: "0,00" }));
                        setShippingDetails(null);
                      }
                    }}
                  />
                </div>
              )}
              {/* Anterior e Próximo ficam dentro do fieldset, que fica
                  desabilitado para quem só consulta — para essa pessoa a
                  faixa de etapas, fora do fieldset, é o caminho. */}
              {emEtapas && podeMexerEmOrcamentos && (
                <NavegacaoDasEtapas
                  className="order-3"
                  etapa={etapa}
                  aoVoltar={paginaEmEtapas.anterior}
                  aoAvancar={paginaEmEtapas.proximo}
                  salvar={{ rotulo: saving ? "Salvando..." : "Salvar alterações", ocupado: saving }}
                />
              )}
            </div>

            <div className="lg:col-span-4 space-y-4 lg:sticky lg:top-6">
              <div className={emEtapas ? CARTAO_EM_ETAPAS : "bg-white rounded-3xl border border-[var(--ks-color-border-subtle)] p-6 shadow-sm"}>
                {emEtapas && (
                  <div className="mb-4">
                    <CabecalhoDoCartao icone="resumo" titulo="Resumo do orçamento" descricao="Os valores mudam conforme você preenche." />
                  </div>
                )}
                <div className="flex items-center justify-between text-sm text-[var(--ks-color-text-soft)]">
                  <span>Subtotal</span>
                  <span className="font-semibold text-[var(--ks-color-text-primary)]">{formatMoney(totals.subtotal)}</span>
                </div>
                <div className="flex items-center justify-between text-sm text-[var(--ks-color-text-soft)] mt-2 gap-4">
                  <span>Desconto</span>
                  <div className="flex items-center gap-2">
                    <div className="flex items-center rounded-lg border border-[var(--ks-color-border-subtle)] bg-white overflow-hidden">
                      <button
                        type="button"
                        onClick={() => {
                          setDiscountType("value");
                          setForm((p) => ({ ...p, discount: formatMoneyInput(p.discount) }));
                        }}
                        className={[
                          "px-2 py-1 text-xs font-semibold transition-colors",
                          discountType === "value"
                            ? "bg-[var(--ks-color-primary-soft)] text-[var(--ks-color-primary-strong)]"
                            : "text-[var(--ks-color-text-soft)] hover:text-[var(--ks-color-text-primary)]",
                        ].join(" ")}
                        disabled={isReadOnly}
                      >
                        R$
                      </button>
                      <button
                        type="button"
                        onClick={() => {
                          setDiscountType("percent");
                          setForm((p) => ({ ...p, discount: formatPercentInput(p.discount) }));
                        }}
                        className={[
                          "px-2 py-1 text-xs font-semibold transition-colors border-l border-[var(--ks-color-border-subtle)]",
                          discountType === "percent"
                            ? "bg-[var(--ks-color-primary-soft)] text-[var(--ks-color-primary-strong)]"
                            : "text-[var(--ks-color-text-soft)] hover:text-[var(--ks-color-text-primary)]",
                        ].join(" ")}
                        disabled={isReadOnly}
                      >
                        %
                      </button>
                    </div>
                    <input
                      value={form.discount}
                      onChange={(e) =>
                        setForm((p) => ({
                          ...p,
                          discount:
                            discountType === "percent"
                              ? formatPercentInput(e.target.value)
                              : formatMoneyInput(e.target.value),
                        }))
                      }
                      className="w-32 px-2 py-1 text-right text-sm rounded-lg border border-[var(--ks-color-border-subtle)] focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] bg-white disabled:bg-[var(--ks-color-bg-soft)] disabled:text-[var(--ks-color-text-muted)]"
                      inputMode="numeric"
                      disabled={isReadOnly}
                    />
                  </div>
                </div>
                <div className="flex items-center justify-between text-sm text-[var(--ks-color-text-soft)] mt-2 gap-4">
                  <span>Frete</span>
                  <div className="relative w-32">
                    <span className="absolute left-3 top-1/2 -translate-y-1/2 text-[var(--ks-color-text-soft)] text-xs">R$</span>
                    <input
                      value={form.shipping}
                      onChange={(e) => setForm((p) => ({ ...p, shipping: formatMoneyInput(e.target.value) }))}
                      className="w-full pl-8 pr-3 py-1.5 text-right rounded-lg border border-[var(--ks-color-border-subtle)] focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] text-sm bg-white disabled:bg-[var(--ks-color-bg-soft)] disabled:text-[var(--ks-color-text-muted)]"
                      inputMode="numeric"
                      disabled={isReadOnly}
                    />
                  </div>
                </div>
                <div className="h-px bg-[var(--ks-color-border-subtle)] my-3" />
                <div className={emEtapas ? "flex items-baseline justify-between" : "flex items-center justify-between"}>
                  <span className={emEtapas ? "text-[17px] font-extrabold text-[var(--ks-color-text-primary)]" : "text-sm font-semibold text-[var(--ks-color-text-primary)]"}>
                    Total
                  </span>
                  <span
                    className={
                      emEtapas
                        ? "text-2xl font-extrabold tracking-tight tabular-nums text-[var(--ks-color-text-primary)]"
                        : "text-lg font-bold text-[var(--ks-color-text-primary)]"
                    }
                  >
                    {formatMoney(totals.total)}
                  </span>
                </div>
                {emEtapas ? (
                  <div className="mt-3">
                    <BlocoListaDePreco
                      checked={form.list_price_mode}
                      onCheckedChange={(checked) => setForm((p) => ({ ...p, list_price_mode: checked }))}
                      disabled={isReadOnly}
                    />
                  </div>
                ) : (
                <div className="mt-3 rounded-xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-soft)] px-3 py-2.5">
                  <div className="flex items-center justify-between gap-3">
                    <div>
                      <div className="text-sm font-semibold text-[var(--ks-color-text-primary)]">Lista de Preço</div>
                      <div className="text-[11px] text-[var(--ks-color-text-muted)]">
                        Oculta o valor total no PDF e no link público.
                      </div>
                    </div>
                    <Switch
                      checked={form.list_price_mode}
                      onCheckedChange={(checked) => setForm((p) => ({ ...p, list_price_mode: checked }))}
                      disabled={isReadOnly}
                    />
                  </div>
                </div>
                )}
                {podeVerCustos && (
                <div
                  className={
                    emEtapas
                      ? "mt-3 text-[13px] text-[var(--ks-color-text-secondary)] border-t border-[var(--ks-color-border-subtle)] pt-3"
                      : "mt-3 text-xs text-[var(--ks-color-text-muted)] border-t border-[var(--ks-color-border-subtle)] pt-3"
                  }
                >
                  <div className="flex items-center justify-between">
                    <span>Custo estimado</span>
                    <span className="font-semibold text-[var(--ks-color-text-primary)]">{formatMoney(totals.cost)}</span>
                  </div>
                  <div className="flex items-center justify-between mt-1">
                    <span>Lucro Bruto</span>
                    <span className={`font-semibold ${totals.margin >= 0 ? "text-[var(--ks-color-success)]" : "text-[var(--ks-color-danger)]"}`}>
                      {formatMoney(totals.margin)}
                    </span>
                  </div>
                  <div className="flex items-center justify-between mt-1">
                    <div className="flex items-center gap-1">
                      <span>Margem Total</span>
                      {totals.marginPct < 30 && totals.total > 0 && (
                        <InfoTooltip text="Margem abaixo de 30% pode ser arriscada." />
                      )}
                    </div>
                    <span
                      className={`font-bold ${
                        totals.margin < 0 ? "text-rose-700" : totals.marginPct < 30 ? "text-amber-600" : "text-[var(--ks-color-success)]"
                      }`}
                    >
                      {totals.marginPct.toFixed(1)}%
                    </span>
                  </div>
                </div>
                )}
              </div>

              {error && (
                <div className="bg-red-50 text-red-600 text-sm px-4 py-2 rounded-2xl flex items-center gap-2 border border-red-100">
                  <div className="w-1.5 h-1.5 rounded-full bg-red-600" />
                  {error}
                </div>
              )}

              <div className={emEtapas ? "grid" : "flex items-center justify-end gap-2"}>
                {podeMexerEmOrcamentos && (
                <button
                  type="submit"
                  disabled={saving}
                  className={`flex items-center gap-2 px-6 py-2.5 bg-[var(--ks-color-primary)] text-white rounded-2xl hover:bg-[var(--ks-color-primary-strong)] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--ks-color-primary)] disabled:opacity-50 disabled:cursor-not-allowed transition-all shadow-sm font-medium ${
                    emEtapas ? "justify-center" : ""
                  }`}
                >
                  <Save size={18} />
                  {saving ? "Salvando..." : "Salvar alterações"}
                </button>
                )}
              </div>
            </div>
          </div>
          </fieldset>
        </form>
      )}





      {chatOpen && (
      <div className="fixed bottom-6 right-6 z-40 w-[360px] max-w-[92vw] h-[70vh]">
        <div className="rounded-3xl border border-zinc-200 bg-white shadow-[0_16px_40px_rgba(0,0,0,0.15)] overflow-hidden flex flex-col h-full">
            <div className="flex items-center justify-between px-4 py-3 border-b border-zinc-100 bg-gradient-to-b from-white to-zinc-50">
              <div className="flex items-center gap-2 text-sm font-semibold text-zinc-900">
                <span className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-[#f0edff] text-[#4b2ae6]">
                  <MessageCircle size={16} />
                </span>
                Chat com cliente
                {isQuoteFinalized && (
                  <span className="ml-2 rounded-full border border-amber-200 bg-amber-50 px-2 py-0.5 text-[10px] font-semibold text-amber-700">
                    Chat encerrado
                  </span>
                )}
              </div>
              <button
                type="button"
                onClick={() => setChatOpen(false)}
                className="rounded-full p-1.5 text-zinc-500 hover:bg-zinc-100"
                aria-label="Fechar chat"
              >
                <X size={16} />
              </button>
            </div>
            <div className="px-4 pt-3">
              {chatTabs.length > 0 ? (
                <div className="flex flex-wrap gap-2">
                  {chatTabs.map((tab) => {
                    const isActive = tab.artId === activeChatArtId;
                    return (
                      <button
                        key={tab.artId}
                        type="button"
                        onClick={() => setActiveChatArtId(tab.artId)}
                        className={`rounded-full px-3 py-1 text-[11px] font-semibold border transition ${
                          isActive
                            ? "bg-[#4b2ae6] text-white border-[#4b2ae6]"
                            : "bg-white text-zinc-700 border-zinc-200 hover:bg-zinc-50"
                        }`}
                      >
                        {tab.label}
                      </button>
                    );
                  })}
                </div>
              ) : (
                <div className="text-xs text-zinc-500">Nenhuma conversa aberta.</div>
              )}
            </div>
            <div className="px-4 py-3 flex-1 min-h-0 flex flex-col gap-3">
              {activeChatTab && activeChatArt ? (
                <>
                  <div className="text-[11px] font-semibold text-zinc-600">
                    {activeChatArt.title || "Arte"} - {activeChatTab.label}
                  </div>
                  <div className="rounded-2xl border border-zinc-100 bg-zinc-50 p-3 flex-1 min-h-0 overflow-y-auto space-y-2">
                    {activeChatMessagesSorted.length > 0 ? (
                      activeChatMessagesSorted.map((message) => {
                        const senderLabel = message.sender === "client" ? clientFirstName : "Empresa";
                        const statusLabel =
                          message.status === "approved"
                            ? "Aprovada"
                            : message.status === "rejected"
                              ? "Reprovada"
                              : message.status === "updated"
                                ? "Atualização"
                                : "";
                        const dateLabel = formatDateTimeBr(message.created_at);
                        const alignClass = message.sender === "client" ? "justify-start" : "justify-end";
                        const isOutgoing = message.sender === "company";
                        const hasRead = Boolean(message.read_at);
                        const hasDelivered = Boolean(message.delivered_at);
                        const statusClass = hasRead
                          ? "text-emerald-500"
                          : hasDelivered
                            ? "text-zinc-400"
                            : "text-zinc-300";
                        const bubbleClass =
                          message.sender === "client"
                            ? "bg-white text-zinc-900 border border-zinc-200"
                            : "bg-[#ede9ff] text-[#2f236e] border border-[#e0d6ff]";
                        return (
                          <div key={message.id} className={`flex ${alignClass}`}>
                            <div className={`max-w-[85%] rounded-2xl px-3 py-2 text-xs ${bubbleClass}`}>
                              <div className="flex flex-wrap items-center gap-2 text-[10px] opacity-70">
                                <span className="font-semibold">{senderLabel}</span>
                                {statusLabel && <span>{statusLabel}</span>}
                                {dateLabel && <span>{dateLabel}</span>}
                                {isOutgoing && (
                                  <span className={`inline-flex items-center ${statusClass}`}>
                                    <Check size={12} />
                                    {(hasDelivered || hasRead) && <Check size={12} className="-ml-2" />}
                                  </span>
                                )}
                              </div>
                              <div className="mt-1 text-sm leading-relaxed">{message.message}</div>
                            </div>
                          </div>
                        );
                      })
                    ) : (
                      <div className="text-xs text-zinc-500">Nenhuma mensagem ainda.</div>
                    )}
                  </div>
                  {!isReadOnly && !isQuoteFinalized && (
                    <div className="space-y-2">
                      <textarea
                        value={activeChatDraft}
                        onChange={(event) =>
                          activeChatTab &&
                          setArtMessageDrafts((prev) => ({ ...prev, [activeChatTab.artId]: event.target.value }))
                        }
                        rows={2}
                        placeholder="Escreva sua mensagem"
                        className="w-full rounded-2xl border border-zinc-200 px-3 py-2 text-sm text-zinc-700 focus:border-[#4b2ae6] focus:outline-none bg-white"
                      />
                      <div className="flex flex-wrap gap-2">
                        <button
                          type="button"
                          onClick={() => activeChatArt && handleSendArtMessage(activeChatArt, null)}
                          disabled={!activeChatArt?.id || activeChatBusy || !activeChatDraft.trim()}
                          className="inline-flex items-center justify-center gap-2 rounded-full bg-[#4b2ae6] px-4 py-2 text-xs font-semibold text-white transition hover:bg-[#3f22c2] disabled:cursor-not-allowed disabled:bg-zinc-300"
                        >
                          {activeChatBusy ? <Loader2 size={14} className="animate-spin" /> : null}
                          Enviar
                        </button>
                        {activeChatArt?.image_urls?.length > 0 && (
                          <button
                            type="button"
                            onClick={() => activeChatArt && handleSendArtMessage(activeChatArt, "updated")}
                            disabled={
                              !activeChatArt?.id ||
                              activeChatBusy ||
                              !activeChatDraft.trim() ||
                              !artNeedsNotify[activeChatArt.id]
                            }
                            className="inline-flex items-center justify-center gap-2 rounded-full border border-[#4b2ae6] px-4 py-2 text-xs font-semibold text-[#4b2ae6] transition hover:bg-[#eef0ff] disabled:cursor-not-allowed disabled:opacity-60"
                          >
                            {activeChatBusy ? <Loader2 size={14} className="animate-spin" /> : null}
                            Avisar nova arte
                          </button>
                        )}
                      </div>
                    </div>
                  )}
                </>
              ) : (
                <div className="text-xs text-zinc-500">Selecione uma aba para ver a conversa.</div>
              )}
            </div>
          </div>
        </div>
      )}

      <Dialog open={artModalOpen} onOpenChange={(open) => !open && closeArtModal()}>
        <DialogContent className="max-w-2xl">
          <DialogHeader>
            <DialogTitle>Cadastro de arte</DialogTitle>
            <DialogDescription>Envie o croqui e fotos para aprovacao do cliente.</DialogDescription>
          </DialogHeader>
          <div className="space-y-4">
            <div className="space-y-1">
              <label className="text-sm font-medium text-zinc-700">Título</label>
              <input
                value={artDraft?.title || ""}
                onChange={(e) => setArtDraft((prev) => (prev ? { ...prev, title: e.target.value } : prev))}
                className="w-full px-3 py-2.5 rounded-xl border border-zinc-200 focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)]"
                placeholder="Ex: Croqui da fachada"
                disabled={artUploading}
              />
            </div>
            <div className="space-y-1">
              <label className="text-sm font-medium text-zinc-700">Observações (opcional)</label>
              <textarea
                value={artDraft?.notes || ""}
                onChange={(e) => setArtDraft((prev) => (prev ? { ...prev, notes: e.target.value } : prev))}
                className="w-full px-3 py-2.5 rounded-xl border border-zinc-200 focus:outline-none focus:ring-2 focus:ring-[var(--ks-color-primary)]/20 focus:border-[var(--ks-color-primary)] min-h-[90px]"
                disabled={artUploading}
              />
            </div>
            <div className="space-y-1">
              <label className="text-sm font-medium text-zinc-700">Fotos da arte</label>
              <input
                ref={artFileInputRef}
                type="file"
                multiple
                accept="image/*"
                onChange={(e) => handleArtFiles(e.target.files)}
                className="block w-full text-sm text-zinc-600 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:bg-[var(--ks-color-primary-soft)] file:text-[var(--ks-color-primary-strong)] file:font-semibold hover:file:bg-[var(--ks-color-primary)] hover:file:text-white"
                disabled={artUploading}
              />
              <div className="text-xs text-zinc-500">Imagens sao convertidas para WebP automaticamente.</div>
              {artUploadProgress ? (
                <div className="mt-2 space-y-1">
                  <div className="flex items-center justify-between text-[11px] text-zinc-600">
                    <span>
                      Enviando {artUploadProgress.completed} de {artUploadProgress.total}
                    </span>
                    <span>
                      {Math.round((artUploadProgress.completed / Math.max(artUploadProgress.total, 1)) * 100)}%
                    </span>
                  </div>
                  <div className="h-2 rounded-full bg-zinc-200 overflow-hidden">
                    <div
                      className="h-full bg-[#4b2ae6] transition-all"
                      style={{
                        width: `${Math.round(
                          (artUploadProgress.completed / Math.max(artUploadProgress.total, 1)) * 100
                        )}%`,
                      }}
                    />
                  </div>
                </div>
              ) : null}
            </div>

            {artDraft?.image_urls?.length ? (
              <div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
                {artDraft.image_urls.map((url) => (
                  <div key={url} className="relative h-24 rounded-lg border border-zinc-200 overflow-hidden">
                    <Image
                      src={url}
                      alt="Arte"
                      fill
                      sizes="96px"
                      className="object-cover"
                      unoptimized
                    />
                    <button
                      type="button"
                      onClick={() => removeArtImage(url)}
                      className="absolute top-2 right-2 bg-white/90 text-zinc-700 hover:text-red-600 rounded-full p-1 shadow"
                      disabled={artUploading}
                    >
                      <Trash2 size={14} />
                    </button>
                  </div>
                ))}
              </div>
            ) : (
              <div className="text-xs text-zinc-500">Nenhuma imagem enviada.</div>
            )}

            {artError && (
              <div className="text-sm text-red-600 bg-red-50 border border-red-100 rounded-lg px-3 py-2">
                {artError}
              </div>
            )}
          </div>
          <DialogFooter>
            <button
              type="button"
              onClick={closeArtModal}
              className="px-4 py-2 rounded-lg border border-zinc-200 text-zinc-700 hover:bg-zinc-50"
              disabled={artUploading}
            >
              Cancelar
            </button>
            <button
              type="button"
              onClick={saveArtDraft}
              className="px-4 py-2 rounded-lg bg-[var(--ks-color-primary)] text-white hover:bg-[var(--ks-color-primary-strong)]"
              disabled={artUploading}
            >
              {artUploading ? "Enviando..." : "Salvar arte"}
            </button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog
        open={previewOpen}
        onOpenChange={(open) => {
          setPreviewOpen(open);
          if (!open) setPreviewImageUrl(null);
        }}
      >
        <DialogContent className="max-w-[92vw] w-auto border-0 bg-transparent p-0 shadow-none">
          <DialogHeader className="sr-only">
            <DialogTitle>Pré-visualização da arte</DialogTitle>
          </DialogHeader>
          <div className="relative">
            <button
              type="button"
              onClick={() => {
                setPreviewOpen(false);
                setPreviewImageUrl(null);
              }}
              className="absolute -top-10 right-0 h-9 w-9 rounded-full bg-black/70 text-white flex items-center justify-center shadow-lg hover:bg-black/80"
            >
              <X size={18} />
            </button>
            {previewImageUrl && (
              <Image
                src={previewImageUrl}
                alt="Arte ampliada"
                width={1200}
                height={800}
                sizes="90vw"
                className="max-h-[80vh] max-w-[90vw] object-contain shadow-[0_30px_80px_rgba(0,0,0,0.45)]"
                unoptimized
              />
            )}
          </div>
        </DialogContent>
      </Dialog>

      <Dialog
        open={orderNegativeStockModal.open}
        onOpenChange={(open) => setOrderNegativeStockModal((prev) => ({ ...prev, open }))}
      >
        <DialogContent className="max-w-xl">
          <DialogHeader>
            <DialogTitle>Gerar pedido com saldo negativo</DialogTitle>
            <DialogDescription>
              Os itens abaixo ficarão com reposição pendente. Confirme para seguir com a geração.
            </DialogDescription>
          </DialogHeader>
          <div className="max-h-64 overflow-y-auto rounded-xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-soft)] p-3 space-y-2">
            {orderNegativeStockModal.alerts.map((alert, index) => (
              <div key={`${alert.itemLabel}-${index}`} className="rounded-lg border border-amber-200 bg-amber-50 px-3 py-2">
                <div className="text-xs font-semibold text-amber-800">{alert.itemLabel}</div>
                <div className="text-xs text-amber-700">{alert.message}</div>
              </div>
            ))}
          </div>
          <DialogFooter>
            <button
              type="button"
              onClick={() => setOrderNegativeStockModal({ open: false, alerts: [] })}
              className="px-4 py-2 rounded-lg border border-[var(--ks-color-border-subtle)] text-[var(--ks-color-text-primary)] hover:bg-[var(--ks-color-bg-soft)]"
            >
              Cancelar
            </button>
            <button
              type="button"
              onClick={generateOrderRequest}
              className="px-4 py-2 rounded-lg bg-[var(--ks-color-primary)] text-white hover:bg-[var(--ks-color-primary-strong)]"
              disabled={generatingOrder}
            >
              {generatingOrder ? "Gerando..." : "Gerar com saldo negativo"}
            </button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog
        open={orderErrorModal.open}
        onOpenChange={(open) => setOrderErrorModal((prev) => ({ ...prev, open }))}
      >
        <DialogContent className="max-w-md">
          <DialogHeader>
            <DialogTitle>Aviso</DialogTitle>
            <DialogDescription>{orderErrorModal.message}</DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <button
              type="button"
              onClick={() => setOrderErrorModal({ open: false, message: "" })}
              className="px-4 py-2 rounded-lg bg-[var(--ks-color-primary)] text-white hover:bg-[var(--ks-color-primary-strong)]"
            >
              OK
            </button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <div className="print-only" style={printPreview ? { display: "block" } : undefined}>
        {/* A folha do orçamento (o PDF): uma página limpa, sem caixas. Cabeçalho
            com a empresa e o número, cliente e prazos lado a lado, tabela de
            itens com filetes, totais, condições, observações e um rodapé. As
            classes documento-* recebem as regras de quebra de página do estilo
            no fim do arquivo. */}
        <div className="documento mx-auto max-w-[820px] text-[12px] leading-[1.45] text-zinc-900">
          <div className="documento-secao flex items-start justify-between gap-8 border-b-2 border-zinc-900 pb-4">
            <div className="flex min-w-0 items-start gap-4">
              {logoUrl && (
                <Image
                  src={logoUrl}
                  alt="Logo"
                  width={160}
                  height={56}
                  sizes="160px"
                  className="h-14 w-auto max-w-[150px] object-contain"
                  loader={passthroughLoader}
                  unoptimized
                  onLoadingComplete={() => setPrintAssetReady(true)}
                  onError={() => setPrintAssetReady(true)}
                />
              )}
              <div className="min-w-0">
                <div className="text-[17px] font-bold leading-tight text-zinc-900">
                  {profile?.catalog_name || profile?.store_name || "Empresa"}
                </div>
                <div className="mt-1 space-y-0.5 text-[11px] leading-snug text-zinc-600">
                  {profile?.cnpj && <div>CNPJ {profile.cnpj}</div>}
                  {profile?.address && <div>{profile.address}</div>}
                  {contatosDaEmpresa(profile?.company_phone || profile?.phone, profile?.company_whatsapp || profile?.whatsapp, profile?.company_email) && (
                    <div>{contatosDaEmpresa(profile?.company_phone || profile?.phone, profile?.company_whatsapp || profile?.whatsapp, profile?.company_email)}</div>
                  )}
                </div>
              </div>
            </div>
            <div className="shrink-0 text-right">
              <div className="text-[10px] font-semibold uppercase tracking-[0.2em] text-zinc-500">
                {isListPriceMode ? "Lista de preços" : "Orçamento"}
              </div>
              <div className="mt-0.5 text-[26px] font-black leading-none tracking-tight text-zinc-900">#{quote?.number ?? ""}</div>
              {quote?.created_at && (
                <div className="mt-2 text-[11px] text-zinc-600">Emitido em {formatDateBr(quote.created_at)}</div>
              )}
            </div>
          </div>

          <div className="documento-secao mt-4 grid grid-cols-[1.35fr_1fr] gap-8 border-b border-zinc-200 pb-4">
            <div className="min-w-0">
              <div className={ROTULO_DO_DOCUMENTO}>Cliente</div>
              <div className="text-[13px] font-semibold text-zinc-900">
                {selectedClient?.company_name || selectedClient?.name || ""}
              </div>
              {selectedClient?.company_name && selectedClient?.name ? (
                <div className="text-[12px] text-zinc-700">{selectedClient.name}</div>
              ) : null}
              {selectedClient?.is_deleted ? (
                <span className="mt-1 inline-flex rounded-full border border-[rgba(239,68,68,0.25)] bg-[rgba(239,68,68,0.1)] px-2 py-0.5 text-[9px] font-bold uppercase tracking-[0.08em] text-[var(--ks-color-status-danger)]">
                  Cliente Excluído
                </span>
              ) : null}
              {(selectedClient?.document || selectedClient?.whatsapp) && (
                <div className="mt-0.5 text-[11px] text-zinc-600">
                  {[
                    formatarDocumento(selectedClient?.document),
                    selectedClient?.whatsapp ? `WhatsApp ${formatarTelefone(selectedClient.whatsapp)}` : "",
                  ]
                    .filter(Boolean)
                    .join("  ·  ")}
                </div>
              )}
            </div>
            {(quote?.expires_at || quote?.delivery_date || creatorName) && (
              <div>
                <div className={ROTULO_DO_DOCUMENTO}>Detalhes</div>
                <dl className="grid grid-cols-[auto_1fr] gap-x-3 gap-y-0.5 text-[11px]">
                  {quote?.expires_at && (
                    <>
                      <dt className="text-zinc-500">Validade da proposta</dt>
                      <dd className="font-medium text-zinc-900">{formatDateBr(quote.expires_at)}</dd>
                    </>
                  )}
                  {quote?.delivery_date && (
                    <>
                      <dt className="text-zinc-500">Entrega prevista</dt>
                      <dd className="font-medium text-zinc-900">{formatDateBr(quote.delivery_date)}</dd>
                    </>
                  )}
                  {creatorName && (
                    <>
                      <dt className="text-zinc-500">Atendimento</dt>
                      <dd className="font-medium text-zinc-900">{creatorName}</dd>
                    </>
                  )}
                </dl>
              </div>
            )}
          </div>

          {quote?.title && <div className="mt-4 text-[14px] font-semibold text-zinc-900">{quote.title}</div>}

          <table className="mt-3 w-full border-collapse">
            <thead>
              <tr className="border-b-[1.5px] border-zinc-900 text-[9.5px] font-bold uppercase tracking-[0.12em] text-zinc-500">
                <th className="w-7 py-1.5 pr-2 text-left font-bold">#</th>
                <th className="py-1.5 pr-2 text-left font-bold">Descrição</th>
                <th className="w-[17%] px-2 py-1.5 text-left font-bold">Tamanho</th>
                <th className="w-[9%] px-2 py-1.5 text-right font-bold">Qtd</th>
                <th className="w-[15%] px-2 py-1.5 text-right font-bold">Unitário</th>
                <th className="w-[16%] py-1.5 pl-2 text-right font-bold">Total</th>
              </tr>
            </thead>
            <tbody>
              {items
                .filter((i) => i.title.trim())
                .map((it, idx) => {
                  const usedQuantity = effectiveQuantity(it);
                  const lineTotal = totalDaLinhaDoRascunho(it, usedQuantity);
                  const minimoDaLinha = it.minimo && !it.unit_price_locked ? it.minimo : null;
                  const area = (it.width_m || 0) * (it.height_m || 0);
                  const displayUnitPrice =
                    it.unit_type === "square_meter" && area > 0 ? area * (it.unit_price || 0) : (it.unit_price || 0);
                  const artImages = itemArts[it.id]?.image_urls ?? [];
                  const selectedOptionsSummary = getSelectedOptionsSummary(it);

                  const medidaDoItem = quantidadeDoItem(it);
                  const tamanho = medidaDoItem.medida ? [medidaDoItem.medida, medidaDoItem.total].filter(Boolean).join(" · ") : "—";

                  return (
                    <tr key={idx} className="border-b border-zinc-200 align-top">
                      <td className="py-2 pr-2 text-zinc-500 tabular-nums">{idx + 1}</td>
                      <td className="py-2 pr-2">
                        <div className="font-semibold text-zinc-900">{it.title}</div>
                        {selectedOptionsSummary ? (
                          <div className="mt-0.5 text-[11px] text-zinc-600">Variações: {selectedOptionsSummary}</div>
                        ) : null}
                        {it.description?.trim() ? (
                          <div className="mt-0.5 whitespace-pre-wrap text-[11px] text-zinc-600">Obs.: {it.description.trim()}</div>
                        ) : null}
                        {minimoDaLinha ? (
                          <div className="mt-1">
                            <span className="inline-block rounded-sm border border-zinc-900 px-1.5 py-0.5 text-[9.5px] font-bold uppercase tracking-[0.06em] text-zinc-900">
                              {ROTULO_DO_MINIMO}
                            </span>
                          </div>
                        ) : null}
                        {artImages.length > 0 && (
                          <div className="mt-1.5 flex flex-wrap gap-1.5">
                            {artImages.slice(0, 4).map((url) => (
                              <Image
                                key={url}
                                src={url}
                                alt="Arte"
                                width={40}
                                height={40}
                                className="h-10 w-10 rounded border border-zinc-200 object-cover"
                                unoptimized
                              />
                            ))}
                          </div>
                        )}
                      </td>
                      <td className="px-2 py-2 text-zinc-700">{tamanho}</td>
                      <td className="px-2 py-2 text-right text-zinc-900 tabular-nums">{medidaDoItem.pecasTexto}</td>
                      <td className="px-2 py-2 text-right text-zinc-900 tabular-nums">
                        {minimoDaLinha ? "Valor mínimo" : formatMoney(displayUnitPrice)}
                      </td>
                      <td className="py-2 pl-2 text-right font-semibold text-zinc-900 tabular-nums">{formatMoney(lineTotal)}</td>
                    </tr>
                  );
                })}
            </tbody>
          </table>

          {!isListPriceMode && (
            <div className="documento-secao mt-3 flex justify-end">
              <table className="w-[300px] text-[12px]">
                <tbody>
                  <tr>
                    <td className="py-0.5 text-zinc-600">Subtotal</td>
                    <td className="py-0.5 text-right text-zinc-900 tabular-nums">{formatMoney(totals.subtotal)}</td>
                  </tr>
                  {totals.discount > 0 && (
                    <tr>
                      <td className="py-0.5 text-zinc-600">Desconto</td>
                      <td className="py-0.5 text-right text-zinc-900 tabular-nums">- {formatMoney(totals.discount)}</td>
                    </tr>
                  )}
                  {totals.shipping > 0 && (
                    <tr>
                      <td className="py-0.5 text-zinc-600">Frete</td>
                      <td className="py-0.5 text-right text-zinc-900 tabular-nums">+ {formatMoney(totals.shipping)}</td>
                    </tr>
                  )}
                  <tr className="border-t-[1.5px] border-zinc-900">
                    <td className="pt-1.5 text-[13px] font-bold text-zinc-900">Total</td>
                    <td className="pt-1.5 text-right text-[18px] font-black leading-none text-zinc-900 tabular-nums">{formatMoney(totals.total)}</td>
                  </tr>
                </tbody>
              </table>
            </div>
          )}

          {condicoes && (
            <div className="documento-secao mt-5">
              <div className={TITULO_DE_SECAO_DO_DOCUMENTO}>Condições de pagamento</div>
              <CondicoesParaImpressao
                condicoes={condicoes}
                total={isListPriceMode ? 0 : totals.total}
                datas={{ entrega: form.delivery_date || null }}
                moldura={false}
              />
            </div>
          )}

          {(profile?.quote_default_notes_enabled && profile?.quote_default_notes) || quote?.notes ? (
            <div className="documento-secao mt-5">
              <div className={TITULO_DE_SECAO_DO_DOCUMENTO}>Observações</div>
              <div className="space-y-2 text-[11.5px] leading-relaxed text-zinc-700">
                {profile?.quote_default_notes_enabled && profile?.quote_default_notes && (
                  <div className="whitespace-pre-wrap">{profile.quote_default_notes}</div>
                )}
                {quote?.notes && <div className="whitespace-pre-wrap">{quote.notes}</div>}
              </div>
            </div>
          ) : null}
        </div>
      </div>

      <ClientFormModal
        isOpen={clientModalOpen}
        onClose={() => setClientModalOpen(false)}
        onSuccess={(createdClientId) => {
          void refreshClients(createdClientId);
        }}
      />

      <style jsx global>{`
        .print-only {
          display: none;
        }
        @media print {
          @page {
            size: A4;
            margin: 14mm;
          }
          .no-print {
            display: none !important;
          }
          .print-only {
            display: block !important;
          }
          .print-main {
            margin-left: 0 !important;
            padding: 0 !important;
          }
          .print-container {
            max-width: none !important;
            margin: 0 !important;
          }
          body {
            background: #ffffff !important;
          }
          /* A folha: o cabeçalho da tabela repete a cada página e nenhuma
             linha, seção ou imagem fica partida entre duas folhas. */
          .documento thead {
            display: table-header-group;
          }
          .documento tr,
          .documento .documento-secao,
          .documento img {
            break-inside: avoid;
            page-break-inside: avoid;
          }
        }
      `}</style>
      </div>
    </div>
  );
}
