"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { AnimatePresence, motion } from "framer-motion";
import {
  Package, Wrench, Plus, Trash2, TriangleAlert, Boxes, ArrowRight, ArrowLeft,
  Sparkles, Search, PencilRuler, Percent, ClipboardCheck, Check, Pencil,
  Presentation, Frame, Printer, CupSoda, Shirt, Gift, Box,
} from "lucide-react";
import { PageHeader } from "@/components/ui/page-header";
import { Button } from "@/components/ui/button";
import { InfoTooltip } from "@/components/ui/info-tooltip";
import { CampoDecimal, textoDecimalValido } from "@/components/ui/campo-decimal";
import { ModalDeInsumo, type InsumoParaEditar } from "@/components/producao/modal-de-insumo";
import { ModalDeMaquina, type MaquinaParaEditar } from "@/components/producao/modal-de-maquina";
import { usePermissoes } from "@/lib/permissoes-do-usuario";
import {
  calcularCustoDaComposicao, custoNoTamanho, custoPorM2NoTamanho, medidaDaBase,
  ROTULO_DA_BASE,
  type BaseDeConsumo, type LinhaDeMaterial, type LinhaDeProcesso,
} from "@/lib/precificacao/composicao";
import { calcularPreco, type RegraDePreco } from "@/lib/precificacao/motor";
import { perdaDeRodagem } from "@/lib/precificacao/formato-de-compra";

/**
 * Formar o preço de um produto como uma conversa: uma pergunta por vez.
 *
 * O fluxo é o mesmo do primeiro acesso — preencheu, avança, o próximo bloco
 * desliza para dentro. A vitrine escolhe o modelo (a receita chega pronta,
 * faltando só os números da pessoa); os passos preenchem; o resumo confirma; e
 * só então o preço aparece, inteiro, com a explicação de cada centavo e o
 * botão de salvar — que também cria sozinho os insumos usados.
 *
 * Texto explicativo virou tooltip: quem já sabe não tropeça nele, quem não
 * sabe passa o mouse. A tela fala o mínimo e o preço fala o resto.
 */

type Opcao = {
  id: string; nome: string; unidade?: string; custo: number; librarySlug?: string;
  /* Insumo comprado em bobina: a largura, para a perda de rodagem sair
     calculada em vez de chutada. */
  larguraBobina?: number;
};

type ItemBiblioteca = {
  slug: string;
  kind: "material" | "maquina" | "mao_de_obra";
  name: string;
  unit: string | null;
  consumption_base: BaseDeConsumo;
  consumption_default: number | null;
  waste_pct_default: number | null;
  minutes_default: number | null;
  setup_minutes_default: number | null;
  segment: string;
  purchase_format?: string | null;
  roll_width_m?: number | null;
};

type LinhaReceita = {
  item: string;
  consumo?: number;
  minutos?: number;
  base: BaseDeConsumo;
  perda?: number;
  setup?: number;
  opcional?: boolean;
  aproveitamento?: number;
};

type ModeloBiblioteca = {
  slug: string;
  name: string;
  segment: string;
  unit_type: "unit" | "square_meter";
  description: string | null;
  reference_width_m: number | null;
  reference_height_m: number | null;
  recipe: LinhaReceita[];
};

const SEGMENTOS: Record<string, { rotulo: string; cor: string; icone: React.ElementType }> = {
  grande_formato: { rotulo: "Grande formato", cor: "var(--ks-brand-600)", icone: Presentation },
  placas_fachadas: { rotulo: "Placas e fachadas", cor: "var(--ks-amber)", icone: Frame },
  grafica_rapida: { rotulo: "Gráfica rápida", cor: "var(--ks-rose)", icone: Printer },
  sublimacao: { rotulo: "Sublimação", cor: "var(--ks-lime)", icone: CupSoda },
  textil: { rotulo: "Têxtil", cor: "var(--ks-emerald)", icone: Shirt },
  brindes_laser: { rotulo: "Brindes e laser", cor: "var(--ks-color-primary)", icone: Gift },
  impressao_3d: { rotulo: "Impressão 3D", cor: "var(--ks-sky)", icone: Box },
  comum: { rotulo: "Geral", cor: "var(--ks-color-text-soft)", icone: Package },
};

/* Ícones próprios da casa em /public/icones/precificacao, nomeados pelo slug
   do modelo. Modelo fora da lista cai no ícone do segmento. */
const MODELOS_COM_ICONE = new Set([
  "banner-lona",
  "adesivo-vinil",
  "fachada-lona",
  "wind-banner",
  "placa-acm",
  "cartao-visita",
  "flyer",
  "etiqueta-rotulo",
  "caneca-sublimada",
  "camiseta-sublimada",
  "camiseta-dtf",
  "chaveiro-mdf",
  "impressao-3d",
]);

const PASSOS = [
  { id: "produto", titulo: "O que você vai fazer?", icone: Package, cor: "var(--ks-brand-600)" },
  { id: "materiais", titulo: "Do que ele é feito?", icone: Boxes, cor: "var(--ks-lime)" },
  { id: "processos", titulo: "Como ele é feito?", icone: Wrench, cor: "var(--ks-amber)" },
  { id: "margem", titulo: "Quanto você quer ganhar?", icone: Percent, cor: "var(--ks-emerald)" },
  { id: "resumo", titulo: "Confira antes de calcular", icone: ClipboardCheck, cor: "var(--ks-color-primary)" },
  { id: "resultado", titulo: "O seu preço", icone: Sparkles, cor: "var(--ks-color-primary)" },
] as const;

const dinheiro = (v: number) =>
  new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(v || 0);

const medida = (v: number) =>
  new Intl.NumberFormat("pt-BR", { maximumFractionDigits: 2 }).format(v || 0);

const duasCasas = (n: number) => Math.round(n * 100) / 100;
const numeroDoCampo = (v: string) => Number(v.replace(",", ".")) || 0;
const campoDoNumero = (n: number) => (n > 0 ? String(n).replace(".", ",") : "");

const REGRA_PADRAO: RegraDePreco = {
  marginTargetPct: 50, marginMinPct: 35, rounding: { type: "END_WITH", cents: 90 },
};

let contador = 0;
const novoId = () => `l${++contador}`;

export default function NovaPrecificacaoPage() {
  const router = useRouter();
  const { pode } = usePermissoes();
  const podeSalvar = pode("products.catalog.write");

  const [modo, setModo] = useState<"escolha" | "montagem">("escolha");
  const [modeloEscolhido, setModeloEscolhido] = useState<ModeloBiblioteca | null>(null);
  const [buscaModelo, setBuscaModelo] = useState("");

  const [passo, setPasso] = useState(0);
  const [direcao, setDirecao] = useState(1);
  const [erroDoPasso, setErroDoPasso] = useState("");

  const [nome, setNome] = useState("");
  const [porMedida, setPorMedida] = useState(false);
  const [largura, setLargura] = useState("1");
  const [altura, setAltura] = useState("1");
  const [quantidade, setQuantidade] = useState("1");
  const [margem, setMargem] = useState("50");

  const [materiais, setMateriais] = useState<LinhaDeMaterial[]>([]);
  const [processos, setProcessos] = useState<LinhaDeProcesso[]>([]);
  const [opcionaisRestantes, setOpcionaisRestantes] = useState<LinhaReceita[]>([]);

  const [modelos, setModelos] = useState<ModeloBiblioteca[]>([]);
  const [itensBiblioteca, setItensBiblioteca] = useState<Map<string, ItemBiblioteca>>(new Map());
  const [insumosDisponiveis, setInsumos] = useState<Opcao[]>([]);
  const [maquinasDisponiveis, setMaquinas] = useState<Opcao[]>([]);
  const [cargosDisponiveis, setCargos] = useState<Opcao[]>([]);
  const [variaveisPct, setVariaveisPct] = useState(0);
  /* A hora da estrutura (R$/h), quando a empresa informou as horas produtivas
     em Contas a Pagar → Custos fixos: cada minuto da ficha carrega a sua
     fatia de aluguel, pró-labore e afins. Zero = fixos fora do preço. */
  const [taxaEstrutura, setTaxaEstrutura] = useState(0);
  /* O custo fixo do mês, para o resultado dizer quantos % dele esta venda
     paga — o IRG do padrão do ramo. */
  const [fixoMensal, setFixoMensal] = useState(0);
  /* As regras de margem por categoria: o número que o setup mandou
     configurar chega onde o preço nasce. Escolher a categoria aplica a
     margem DELA — e o produto é salvo já categorizado. */
  const [categoriasComRegra, setCategoriasComRegra] = useState<
    { id: string; nome: string; marginTargetPct: number; marginMinPct: number | null }[]
  >([]);
  const [categoriaId, setCategoriaId] = useState("");
  const [carregando, setCarregando] = useState(true);
  const [salvando, setSalvando] = useState(false);
  const [salvo, setSalvo] = useState("");
  const [aviso, setAviso] = useState("");

  const carregar = useCallback(async () => {
    const [ins, maq, car, prep, bib] = await Promise.all([
      fetch("/api/production/materials?papel=insumo", { cache: "no-store" }).then((r) => r.json()).catch(() => null),
      fetch("/api/production/operations?type=machines", { cache: "no-store" }).then((r) => r.json()).catch(() => null),
      fetch("/api/production/operations?type=roles", { cache: "no-store" }).then((r) => r.json()).catch(() => null),
      fetch("/api/precificacao/preparo", { cache: "no-store" }).then((r) => r.json()).catch(() => null),
      fetch("/api/precificacao/biblioteca", { cache: "no-store" }).then((r) => r.json()).catch(() => null),
    ]);

    const lista = (v: unknown): Record<string, unknown>[] => {
      const d = (v as { data?: unknown })?.data ?? v;
      return Array.isArray(d) ? (d as Record<string, unknown>[]) : [];
    };

    setInsumos(lista(ins).map((m) => ({
      id: String(m.id), nome: String(m.name ?? ""),
      unidade: String(m.unit ?? "un"), custo: Number(m.cost_per_unit ?? 0),
      librarySlug: m.library_slug ? String(m.library_slug) : undefined,
      larguraBobina: m.purchase_format === "bobina" && Number(m.roll_width_m) > 0
        ? Number(m.roll_width_m) : undefined,
    })));
    setMaquinas(lista(maq).map((m) => ({
      id: String(m.id), nome: String(m.name ?? ""), custo: Number(m.hourly_cost ?? 0),
      librarySlug: m.library_slug ? String(m.library_slug) : undefined,
    })));
    setCargos(lista(car).map((c) => ({
      id: String(c.id), nome: String(c.role_name ?? ""), custo: Number(c.hourly_rate ?? 0),
      librarySlug: c.library_slug ? String(c.library_slug) : undefined,
    })));
    setVariaveisPct(Number(prep?.custosVariaveis?.totalPct ?? 0));
    setTaxaEstrutura(Number(prep?.horaDaEstrutura?.taxa ?? 0));
    setFixoMensal(Number(prep?.custosFixos?.total_mensal ?? 0));
    setCategoriasComRegra(
      Array.isArray(prep?.margens?.lista)
        ? (prep.margens.lista as { id: string; nome: string; marginTargetPct: number; marginMinPct: number | null }[])
        : []
    );

    const listaModelos = Array.isArray(bib?.modelos) ? (bib.modelos as ModeloBiblioteca[]) : [];
    const listaItens = Array.isArray(bib?.itens) ? (bib.itens as ItemBiblioteca[]) : [];
    setModelos(listaModelos);
    setItensBiblioteca(new Map(listaItens.map((i) => [i.slug, i])));
    /* Biblioteca fora do ar não pode trancar a porta. */
    if (listaModelos.length === 0) setModo("montagem");

    setCarregando(false);
  }, []);

  useEffect(() => { void carregar(); }, [carregar]);

  const doTenant = useCallback((slug: string, kind: ItemBiblioteca["kind"]) => {
    const listaDoTipo =
      kind === "material" ? insumosDisponiveis : kind === "maquina" ? maquinasDisponiveis : cargosDisponiveis;
    return listaDoTipo.find((o) => o.librarySlug === slug) ?? null;
  }, [insumosDisponiveis, maquinasDisponiveis, cargosDisponiveis]);

  const linhaDaReceita = useCallback((l: LinhaReceita): { material?: LinhaDeMaterial; processo?: LinhaDeProcesso } => {
    const item = itensBiblioteca.get(l.item);
    if (!item) return {};
    const existente = doTenant(l.item, item.kind);

    if (item.kind === "material") {
      const perdaCasada = l.perda ?? item.waste_pct_default ?? 0;
      return {
        material: {
          id: novoId(),
          origemId: existente?.id,
          librarySlug: item.slug,
          nome: item.name,
          unidade: item.unit ?? "un",
          custoUnitario: existente?.custo ?? 0,
          consumo: l.consumo ?? item.consumption_default ?? 1,
          base: l.base,
          /* A perda que entra é a da rodagem CASADA (a receita), não a da
             geometria: rodar sozinho é exceção, e virou pergunta na linha. */
          perdaPct: perdaCasada,
          perdaCasada,
          rodagem: "casado" as const,
          aproveitamento: l.aproveitamento,
          /* A largura da bobina vem do cadastro do tenant quando ele já usou o
             insumo — senão, da própria biblioteca: se a pessoa disser que roda
             sozinho, a perda da geometria sai calculada na hora. */
          larguraBobina: existente?.larguraBobina ??
            (item.purchase_format === "bobina" && Number(item.roll_width_m) > 0
              ? Number(item.roll_width_m) : undefined),
        },
      };
    }
    return {
      processo: {
        id: novoId(),
        origemId: existente?.id,
        librarySlug: item.slug,
        nome: item.name,
        tipo: item.kind === "maquina" ? "maquina" : "pessoa",
        custoHora: existente?.custo ?? 0,
        minutos: l.minutos ?? item.minutes_default ?? 0,
        base: l.base,
        minutosDeSetup: l.setup ?? item.setup_minutes_default ?? 0,
      },
    };
  }, [itensBiblioteca, doTenant]);

  const irPara = (proximo: number) => {
    setDirecao(proximo >= passo ? 1 : -1);
    setErroDoPasso("");
    setPasso(Math.max(0, Math.min(PASSOS.length - 1, proximo)));
  };

  const aplicarModelo = (m: ModeloBiblioteca) => {
    setModeloEscolhido(m);
    setNome(m.name);
    const medido = m.unit_type === "square_meter";
    setPorMedida(medido);
    if (medido) {
      setLargura(campoDoNumero(m.reference_width_m ?? 1) || "1");
      setAltura(campoDoNumero(m.reference_height_m ?? 1) || "1");
    }
    setQuantidade("1");

    const mats: LinhaDeMaterial[] = [];
    const procs: LinhaDeProcesso[] = [];
    const opcionais: LinhaReceita[] = [];
    for (const l of m.recipe) {
      if (l.opcional) { opcionais.push(l); continue; }
      const criada = linhaDaReceita(l);
      if (criada.material) mats.push(criada.material);
      if (criada.processo) procs.push(criada.processo);
    }
    setMateriais(mats);
    setProcessos(procs);
    setOpcionaisRestantes(opcionais);
    setAviso("");
    setPasso(0);
    setDirecao(1);
    setModo("montagem");
  };

  const montarDoZero = () => {
    setModeloEscolhido(null);
    setNome("");
    setPorMedida(false);
    setMateriais([]);
    setProcessos([]);
    setOpcionaisRestantes([]);
    setAviso("");
    setPasso(0);
    setDirecao(1);
    setModo("montagem");
  };

  const adicionarOpcional = (l: LinhaReceita) => {
    const criada = linhaDaReceita(l);
    if (criada.material) setMateriais((v) => [...v, criada.material!]);
    if (criada.processo) setProcessos((v) => [...v, criada.processo!]);
    setOpcionaisRestantes((v) => v.filter((x) => x.item !== l.item));
  };

  const pecas = numeroDoCampo(quantidade);
  const margemAlvo = numeroDoCampo(margem);
  const larguraRef = porMedida ? numeroDoCampo(largura) : 0;
  const alturaRef = porMedida ? numeroDoCampo(altura) : 0;

  const composicao = useMemo(
    () => calcularCustoDaComposicao({
      materiais, processos,
      quantidade: pecas,
      larguraM: larguraRef,
      alturaM: alturaRef,
    }),
    [materiais, processos, pecas, larguraRef, alturaRef]
  );

  const ehMedido = porMedida && composicao.areaUnitaria > 0;

  /* A perda de rodagem só se recalcula sozinha para quem disse que RODA
     SOZINHO — rodar casado é a regra da casa, e a perda dela é a da receita.
     Recalcula quando a medida muda e para assim que a pessoa edita à mão. */
  useEffect(() => {
    if (!porMedida || larguraRef <= 0 || alturaRef <= 0) return;
    setMateriais((linhas) => {
      let mudou = false;
      const novas = linhas.map((m) => {
        if (!m.larguraBobina || m.base !== "por_m2" || m.perdaEditada) return m;
        if (m.rodagem !== "sozinho") return m;
        const perda = perdaDeRodagem(m.larguraBobina, larguraRef, alturaRef);
        if (perda === (m.perdaPct ?? 0)) return m;
        mudou = true;
        return { ...m, perdaPct: perda };
      });
      return mudou ? novas : linhas;
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [porMedida, larguraRef, alturaRef, materiais.length]);

  const categoriaEscolhida = categoriasComRegra.find((c) => c.id === categoriaId);
  const regra = useMemo(
    () => ({
      ...REGRA_PADRAO,
      marginTargetPct: margemAlvo,
      marginMinPct: categoriaEscolhida?.marginMinPct ?? REGRA_PADRAO.marginMinPct,
    }),
    [margemAlvo, categoriaEscolhida]
  );

  /* O custo e o preço de UMA unidade de venda: um m² no produto medido, uma
     peça no de unidade. É o que o produto guarda e o orçamento multiplica. */
  const custoDaUnidade = ehMedido ? composicao.custoPorM2 : composicao.custoPorPeca;
  const unidadesDoPedido = ehMedido ? composicao.areaTotal : pecas;

  /* Os minutos que o pedido de referência ocupa — máquina e gente, com o
     preparo. É por eles que os custos fixos entram: tempo × hora da
     estrutura, diluído nas unidades vendidas. */
  const minutosDoPedido = useMemo(
    () => processos.reduce((acc, p) => {
      const medida = medidaDaBase(p.base, larguraRef, alturaRef);
      if (p.base !== "por_peca" && medida <= 0) return acc;
      return acc + p.minutos * medida * pecas + (p.minutosDeSetup ?? 0);
    }, 0),
    [processos, larguraRef, alturaRef, pecas]
  );
  const fixoDaUnidade = taxaEstrutura > 0 && unidadesDoPedido > 0
    ? duasCasas(((minutosDoPedido / 60) * taxaEstrutura) / unidadesDoPedido)
    : 0;

  const preco = useMemo(
    () => calcularPreco({
      custoUnitario: custoDaUnidade,
      quantidade: 1,
      modo: "unidade",
      regra,
      custosVariaveisPct: variaveisPct,
      custoFixoUnitario: fixoDaUnidade,
    }),
    [custoDaUnidade, regra, variaveisPct, fixoDaUnidade]
  );

  const precoDoPedido = duasCasas(preco.precoUnitario * unidadesDoPedido);
  const impostosDoPedido = duasCasas((precoDoPedido * variaveisPct) / 100);
  const fixosDoPedido = duasCasas(fixoDaUnidade * unidadesDoPedido);
  const sobraDoPedido = duasCasas(precoDoPedido - composicao.custoTotal - impostosDoPedido - fixosDoPedido);

  /* O IRG: quantos % do custo fixo do mês este pedido paga. A contribuição é
     o padrão do ramo — preço menos matéria-prima e variáveis; a mão de obra e
     os fixos pelo tempo ficam dentro, porque são o fixo sendo recuperado. */
  const materiaisDoPedido = duasCasas(
    composicao.itens.filter((i) => i.tipo === "material").reduce((acc, i) => acc + i.custo, 0)
  );
  const contribuicaoDoPedido = duasCasas(precoDoPedido - materiaisDoPedido - impostosDoPedido);
  const irgPct = fixoMensal > 0 ? duasCasas((contribuicaoDoPedido / fixoMensal) * 100) : 0;

  const materiaisSemCusto = materiais.filter((m) => m.custoUnitario <= 0).map((m) => m.nome);
  const processosSemCusto = processos.filter((p) => p.custoHora <= 0).map((p) => p.nome);

  /* A curva do metro quadrado: mesma ficha, tamanhos diferentes. Escala as
     duas dimensões juntas (√ do fator) mantendo a proporção da referência. */
  const tabela = useMemo(() => {
    if (!ehMedido || composicao.custoTotal <= 0) return [];
    return [0.25, 0.5, 1, 2, 4]
      .map((fator) => ({
        l: duasCasas(larguraRef * Math.sqrt(fator)),
        a: duasCasas(alturaRef * Math.sqrt(fator)),
        ehReferencia: fator === 1,
      }))
      .filter((t, i, arr) => t.l >= 0.05 && t.a >= 0.05 &&
        arr.findIndex((x) => x.l === t.l && x.a === t.a) === i)
      .map(({ l, a, ehReferencia }) => {
        const area = duasCasas(l * a);
        const custoDaPeca = custoNoTamanho(composicao.naturezas, l, a, 1);
        /* Os fixos deste tamanho: a ficha DELE ocupa outras horas — é
           exatamente o que o rateio percentual nunca soube fazer. */
        const minutosDaPeca = processos.reduce((acc, p) => {
          const medida = medidaDaBase(p.base, l, a);
          if (p.base !== "por_peca" && medida <= 0) return acc;
          return acc + p.minutos * medida + (p.minutosDeSetup ?? 0);
        }, 0);
        const fixoPorM2 = taxaEstrutura > 0 && area > 0
          ? duasCasas(((minutosDaPeca / 60) * taxaEstrutura) / area)
          : 0;
        const justo = calcularPreco({
          custoUnitario: custoPorM2NoTamanho(composicao.naturezas, l, a, 1),
          quantidade: 1, modo: "unidade", regra, custosVariaveisPct: variaveisPct,
          custoFixoUnitario: fixoPorM2,
        });
        const comOM2Liso = duasCasas(preco.precoUnitario * area);
        return {
          l, a, area, custoDaPeca,
          precoJustoM2: justo.precoUnitario,
          precoJustoDaPeca: duasCasas(justo.precoUnitario * area),
          comOM2Liso,
          noPrejuizo: comOM2Liso < custoDaPeca,
          ehReferencia,
        };
      });
  }, [ehMedido, composicao.naturezas, composicao.custoTotal, larguraRef, alturaRef, preco.precoUnitario, regra, variaveisPct, processos, taxaEstrutura]);

  const palpiteDeBase = (unidade?: string): BaseDeConsumo => {
    if (!porMedida) return "por_peca";
    const u = (unidade ?? "").toLowerCase();
    if (u === "m2" || u === "m²") return "por_m2";
    if (u === "m" || u === "mt" || u === "metro") return "por_m_largura";
    return "por_peca";
  };

  const addMaterial = (opcao: Opcao) => setMateriais((v) => [...v, {
    id: novoId(), origemId: opcao.id, librarySlug: opcao.librarySlug,
    nome: opcao.nome, unidade: opcao.unidade ?? "un",
    custoUnitario: opcao.custo, consumo: 1,
    base: palpiteDeBase(opcao.unidade),
    perdaPct: 0,
    perdaCasada: 0,
    rodagem: "casado" as const,
    larguraBobina: opcao.larguraBobina,
  }]);

  const addProcesso = (opcao: Opcao, tipo: "maquina" | "pessoa") =>
    setProcessos((v) => [...v, {
      id: novoId(), origemId: opcao.id, librarySlug: opcao.librarySlug,
      nome: opcao.nome, tipo, custoHora: opcao.custo,
      minutos: 10, base: "por_peca", minutosDeSetup: 0,
    }]);

  /* Cadastro na hora, sem sair do passo — no MESMO modal das páginas de
     produção, com o "calcular o custo por hora para mim" e o formato de
     compra. O que o modal devolve entra na lista e já cai na receita. */
  const [cadastrandoInsumo, setCadastrandoInsumo] = useState<string | null>(null);
  const [cadastrandoMaquina, setCadastrandoMaquina] = useState<string | null>(null);

  const usarInsumoSalvo = (d: InsumoParaEditar) => {
    const opcao: Opcao = {
      id: String(d.id), nome: String(d.name), unidade: String(d.unit ?? "un"),
      custo: Number(d.cost_per_unit ?? 0),
      larguraBobina: d.purchase_format === "bobina" && Number(d.roll_width_m) > 0
        ? Number(d.roll_width_m)
        : undefined,
    };
    setInsumos((v) => [...v, opcao]);
    addMaterial(opcao);
  };

  const usarMaquinaSalva = (d: MaquinaParaEditar) => {
    const opcao: Opcao = { id: String(d.id), nome: String(d.name), custo: Number(d.hourly_cost ?? 0) };
    setMaquinas((v) => [...v, opcao]);
    addProcesso(opcao, "maquina");
  };

  const criarPessoa = async (f: Record<string, string>): Promise<Opcao | null> => {
    if (!f.nome?.trim()) return null;
    const salario = numeroDoCampo(f.salario ?? "");
    const encargos = numeroDoCampo(f.encargos ?? "");
    const horas = numeroDoCampo(f.horas ?? "") || 176;
    /* A pessoa informa o salário; a hora sai calculada — aqui e no cadastro,
       pelo mesmo caminho (o gatilho do banco refaz a conta quando o salário
       mudar). */
    const r = await fetch("/api/production/operations", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({
        type: "role",
        role_name: f.nome.trim(),
        monthly_salary: salario,
        charges_percent: encargos,
        productive_hours_month: horas,
        hourly_rate: salario > 0 ? duasCasas((salario * (1 + encargos / 100)) / horas) : 0,
        hourly_rate_manual: false,
      }),
    });
    const j = await r.json().catch(() => null);
    const d = j?.data ?? j;
    if (!r.ok || !d?.id) return null;
    const opcao: Opcao = { id: String(d.id), nome: String(d.role_name), custo: Number(d.hourly_rate ?? 0) };
    setCargos((v) => [...v, opcao]);
    return opcao;
  };

  /** O portão de cada passo: avança só quando o passo está inteiro. */
  const avancar = () => {
    const id = PASSOS[passo].id;
    if (id === "produto") {
      if (!nome.trim()) return setErroDoPasso("Dê um nome ao produto.");
      if (porMedida && (larguraRef <= 0 || alturaRef <= 0)) {
        return setErroDoPasso("Informe a largura e a altura de referência.");
      }
    }
    if (id === "materiais" && materiaisSemCusto.length > 0) {
      return setErroDoPasso(`Falta o custo de: ${materiaisSemCusto.join(", ")}.`);
    }
    if (id === "processos") {
      if (processosSemCusto.length > 0) {
        return setErroDoPasso(`Falta o custo de: ${processosSemCusto.join(", ")}.`);
      }
      if (materiais.length + processos.length === 0) {
        return setErroDoPasso("Adicione ao menos um insumo ou um processo.");
      }
    }
    if (id === "margem") {
      if (margemAlvo <= 0) return setErroDoPasso("Informe a margem que você quer.");
      if (!preco.possivel && composicao.custoTotal > 0) {
        return setErroDoPasso(preco.avisos[0] ?? "Essa margem não fecha a conta.");
      }
    }
    irPara(passo + 1);
  };

  const salvarComoProduto = async (destino: "produto" | "outra") => {
    setSalvando(true);
    setAviso("");
    try {
      /* Fase 1 — o que veio da biblioteca vira cadastro da empresa, numa
         viagem só. É daqui que nasce o "sem precisar cadastrar antes". */
      const daBiblioteca = [
        ...materiais.filter((m) => m.librarySlug).map((m) => ({ slug: m.librarySlug, custo: m.custoUnitario })),
        ...processos.filter((p) => p.librarySlug).map((p) => ({ slug: p.librarySlug, custo: p.custoHora })),
      ];
      let resolvidos: Record<string, { id: string }> = {};
      if (daBiblioteca.length > 0) {
        const r = await fetch("/api/precificacao/usar-biblioteca", {
          method: "POST",
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ itens: daBiblioteca }),
        });
        const j = await r.json().catch(() => null);
        if (!r.ok) { setAviso(j?.error || "Não foi possível salvar os insumos."); return; }
        resolvidos = j?.resolvidos ?? {};
      }

      const idDaLinha = (librarySlug?: string, origemId?: string) =>
        (librarySlug ? resolvidos[librarySlug]?.id : undefined) ?? origemId ?? null;

      /* Fase 2 — o produto nasce com a ficha completa e a medida de
         referência, para a ficha se reconstruir depois. */
      const materials = materiais
        .filter((m) => m.custoUnitario > 0 && m.consumo > 0)
        .map((m) => ({
          material_id: idDaLinha(m.librarySlug, m.origemId),
          consumption_qty: m.consumo,
          waste_pct: m.perdaPct ?? 0,
          consumption_base: m.base,
          pieces_per_unit: m.aproveitamento ?? null,
          cost_total: duasCasas(
            m.consumo * (1 + (m.perdaPct ?? 0) / 100) * m.custoUnitario *
            medidaDaBase(m.base, larguraRef, alturaRef)
          ),
        }))
        .filter((m) => m.material_id);
      const processes = processos
        .filter((p) => p.custoHora > 0)
        .map((p) => ({
          kind: p.tipo === "maquina" ? "MACHINE" : "ROLE",
          machine_id: p.tipo === "maquina" ? idDaLinha(p.librarySlug, p.origemId) : null,
          role_id: p.tipo === "pessoa" ? idDaLinha(p.librarySlug, p.origemId) : null,
          process_name: p.nome,
          minutes: p.minutos,
          setup_minutes: p.minutosDeSetup ?? 0,
          consumption_base: p.base,
          cost_total: duasCasas(
            (p.minutos / 60) * p.custoHora * medidaDaBase(p.base, larguraRef, alturaRef) +
            ((p.minutosDeSetup ?? 0) / 60) * p.custoHora
          ),
        }));

      const r = await fetch("/api/products", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          name: nome.trim(),
          price: preco.precoUnitario,
          /* A categoria escolhida no passo da margem viaja junto: o produto
             nasce categorizado, e a análise dele usa a regra certa. */
          category_id: categoriaId || null,
          /* Zero de propósito: no modelo do produto, o estimado são custos
             FORA da ficha, e aqui a ficha é o custo inteiro. Gravar o total no
             estimado fazia a tela do produto somar tudo duas vezes. */
          estimated_base_cost: 0,
          cost_total: custoDaUnidade,
          unit_type: ehMedido ? "square_meter" : "unit",
          pricing_reference_width_m: ehMedido ? larguraRef : null,
          pricing_reference_height_m: ehMedido ? alturaRef : null,
          materials,
          processes,
        }),
      });
      const j = await r.json().catch(() => null);
      if (!r.ok) { setAviso(j?.error || "Não foi possível salvar o produto."); return; }
      const id = j?.id ?? j?.data?.id;
      if (destino === "produto") {
        router.push(id ? `/dashboard/products/${id}` : "/dashboard/products");
        return;
      }
      /* Emendar a próxima: salvou, avisa e volta para a vitrine com os
         cadastros recém-criados já na lista. */
      setSalvo(nome.trim());
      await carregar();
      setModo("escolha");
      setBuscaModelo("");
      setModeloEscolhido(null);
      setMateriais([]);
      setProcessos([]);
      setOpcionaisRestantes([]);
      setNome("");
      setPasso(0);
    } finally {
      setSalvando(false);
    }
  };

  if (carregando) {
    return <PageHeader eyebrow="Precificação" title="Nova precificação" subtitle="Carregando…" />;
  }

  /* ----------------------------------------------------------------------- */
  /* A vitrine                                                               */
  /* ----------------------------------------------------------------------- */
  if (modo === "escolha") {
    const filtrados = modelos.filter((m) =>
      `${m.name} ${m.description ?? ""}`.toLowerCase().includes(buscaModelo.toLowerCase())
    );
    const segmentos = [...new Set(filtrados.map((m) => m.segment))];

    return (
      <div className="space-y-6 max-w-5xl">
        <PageHeader
          eyebrow="Precificação"
          title="O que você vai fazer?"
          subtitle="Escolha um modelo com a receita pronta — você só coloca os seus custos"
        />

        {salvo && (
          <div className="flex items-center gap-2 rounded-xl border border-[var(--ks-emerald)] bg-[var(--ks-color-bg-inset)] px-4 py-3 text-sm text-[var(--ks-color-text-primary)]">
            <Check size={15} className="text-[var(--ks-emerald)] shrink-0" />
            <strong>{salvo}</strong>&nbsp;salvo nos produtos. Bora para o próximo.
          </div>
        )}

        <div className="relative max-w-md">
          <Search size={15} className="absolute left-3 top-1/2 -translate-y-1/2 text-[var(--ks-color-text-soft)]" />
          <input
            value={buscaModelo}
            onChange={(e) => setBuscaModelo(e.target.value)}
            placeholder="Buscar: banner, caneca, cartão…"
            className="ks-field w-full pl-9"
          />
        </div>

        {segmentos.map((seg) => {
          const info = SEGMENTOS[seg] ?? SEGMENTOS.comum;
          return (
            <div key={seg}>
              <h2 className="flex items-center gap-2 text-xs font-bold uppercase tracking-wide text-[var(--ks-color-text-soft)] mb-2.5">
                <span className="h-2 w-2 rounded-full" style={{ background: info.cor }} />
                {info.rotulo}
              </h2>
              <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3">
                {filtrados.filter((m) => m.segment === seg).map((m) => {
                  const Icone = info.icone;
                  return (
                    <button
                      key={m.slug}
                      type="button"
                      onClick={() => aplicarModelo(m)}
                      className="ks-surface group text-left rounded-2xl p-4 border border-[var(--ks-color-border-subtle)] transition-all hover:-translate-y-0.5 hover:shadow-md hover:border-[var(--ks-color-primary)]"
                    >
                      <div className="flex items-center gap-3">
                        {MODELOS_COM_ICONE.has(m.slug) ? (
                          <img
                            src={`/icones/precificacao/${m.slug}.png`}
                            alt=""
                            className="h-11 w-11 shrink-0 object-contain transition-transform group-hover:scale-110"
                          />
                        ) : (
                          <span
                            className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl text-white transition-transform group-hover:scale-110"
                            style={{ background: info.cor }}
                          >
                            <Icone size={18} />
                          </span>
                        )}
                        <div className="min-w-0 flex-1">
                          <div className="font-bold text-sm text-[var(--ks-color-text-primary)] truncate">
                            {m.name}
                          </div>
                          <div className="text-[11px] text-[var(--ks-color-text-soft)] truncate">
                            {m.description}
                          </div>
                        </div>
                        <span className="shrink-0 text-[9px] uppercase font-bold px-2 py-1 rounded-full bg-[var(--ks-color-bg-inset)] text-[var(--ks-color-text-soft)]">
                          {m.unit_type === "square_meter" ? "m²" : "peça"}
                        </span>
                      </div>
                    </button>
                  );
                })}
              </div>
            </div>
          );
        })}

        {filtrados.length === 0 && (
          <p className="text-sm text-[var(--ks-color-text-muted)]">
            Nenhum modelo com esse nome — dá para montar do zero logo abaixo.
          </p>
        )}

        <button
          type="button"
          onClick={montarDoZero}
          className="w-full flex items-center justify-center gap-2 py-3.5 rounded-2xl border border-dashed border-[var(--ks-color-border-subtle)] text-sm font-semibold text-[var(--ks-color-text-muted)] hover:border-[var(--ks-color-primary)] hover:text-[var(--ks-color-primary)] transition"
        >
          <PencilRuler size={16} /> Montar do zero, sem modelo
        </button>
      </div>
    );
  }

  /* ----------------------------------------------------------------------- */
  /* O assistente                                                            */
  /* ----------------------------------------------------------------------- */
  const ativo = PASSOS[passo];
  const chipsMateriais = opcionaisRestantes.filter((l) => itensBiblioteca.get(l.item)?.kind === "material");
  const chipsProcessos = opcionaisRestantes.filter((l) => {
    const k = itensBiblioteca.get(l.item)?.kind;
    return k === "maquina" || k === "mao_de_obra";
  });
  const unidade = ehMedido ? "m²" : "peça";

  return (
    <div className="max-w-3xl mx-auto">
      {/* Cabeçalho do assistente: onde estou, e por onde voltar. */}
      <div className="flex items-center justify-between gap-3 mb-2">
        {modelos.length > 0 ? (
          <button
            type="button"
            onClick={() => { setModo("escolha"); setBuscaModelo(""); }}
            className="inline-flex items-center gap-1.5 text-xs font-semibold text-[var(--ks-color-text-muted)] hover:text-[var(--ks-color-primary)] transition"
          >
            <ArrowLeft size={13} /> {modeloEscolhido ? modeloEscolhido.name : "Trocar modelo"}
          </button>
        ) : <span />}
        <span className="text-[11px] font-bold uppercase tracking-[0.18em] text-[var(--ks-color-text-soft)]">
          {String(passo + 1).padStart(2, "0")} / {String(PASSOS.length).padStart(2, "0")}
        </span>
      </div>

      {/* A trilha: bolinhas viram check quando o passo fica para trás. */}
      <div className="flex items-center gap-1.5 mb-8">
        {PASSOS.map((p, i) => {
          const feito = i < passo;
          const atual = i === passo;
          return (
            <button
              key={p.id}
              type="button"
              title={p.titulo}
              onClick={() => { if (feito) irPara(i); }}
              disabled={!feito}
              className="flex items-center gap-1.5 disabled:cursor-default"
            >
              <span
                className="flex h-7 w-7 items-center justify-center rounded-full text-white transition-all duration-300"
                style={{
                  background: feito || atual ? p.cor : "var(--ks-color-bg-hover)",
                  color: feito || atual ? "#fff" : "var(--ks-color-text-soft)",
                  transform: atual ? "scale(1.15)" : "scale(1)",
                }}
              >
                {feito ? <Check size={13} /> : <p.icone size={13} />}
              </span>
              {i < PASSOS.length - 1 && (
                <span
                  className="hidden sm:block h-0.5 w-6 rounded-full transition-colors duration-300"
                  style={{ background: feito ? p.cor : "var(--ks-color-bg-hover)" }}
                />
              )}
            </button>
          );
        })}
        {composicao.custoTotal > 0 && passo < 5 && (
          <span className="ml-auto text-[11px] font-semibold text-[var(--ks-color-text-soft)] bg-[var(--ks-color-bg-inset)] px-2.5 py-1 rounded-full whitespace-nowrap">
            custo: {dinheiro(composicao.custoTotal)}
          </span>
        )}
      </div>

      <AnimatePresence mode="wait" custom={direcao}>
        <motion.div
          key={ativo.id}
          custom={direcao}
          initial={{ opacity: 0, x: 42 * direcao }}
          animate={{ opacity: 1, x: 0 }}
          exit={{ opacity: 0, x: -42 * direcao }}
          transition={{ duration: 0.24, ease: "easeOut" }}
        >
          <h1 className="text-2xl sm:text-3xl font-black tracking-tight text-[var(--ks-color-text-primary)] mb-6">
            {ativo.titulo}
          </h1>

          {/* -------------------------------------------------- passo 1 --- */}
          {ativo.id === "produto" && (
            <div className="space-y-5">
              <input
                value={nome}
                onChange={(e) => setNome(e.target.value)}
                placeholder="Ex: Banner em lona 440g"
                className="ks-field w-full text-lg py-3"
                autoFocus
              />
              <div className="flex flex-wrap items-center gap-2">
                {modeloEscolhido ? (
                  /* A forma de cobrança vem do modelo — a receita foi escrita
                     para ela. Oferecer "por m²" numa caneca ou numa peça 3D só
                     confunde; quem quer cobrar diferente monta do zero. */
                  <>
                    <span
                      className="px-4 py-2 rounded-full text-sm font-semibold border"
                      style={{ background: "var(--ks-color-primary)", borderColor: "var(--ks-color-primary)", color: "#fff" }}
                    >
                      {porMedida ? "Cobro por m²" : "Cobro por peça"}
                    </span>
                    <InfoTooltip text={porMedida
                      ? "Este modelo se cobra por metro quadrado: o produto guarda o preço do m², e o tamanho de cada venda é informado no orçamento."
                      : "Este modelo se cobra por peça, como caneca e camiseta. Para um produto cobrado por m², escolha um modelo medido ou monte do zero."} />
                  </>
                ) : (
                  <>
                    <Alternar ativo={!porMedida} aoClicar={() => setPorMedida(false)} rotulo="Cobro por peça" />
                    <Alternar ativo={porMedida} aoClicar={() => setPorMedida(true)} rotulo="Cobro por m²" />
                    <InfoTooltip text="Caneca e camiseta se cobram por peça. Banner, adesivo e placa se cobram por metro quadrado: o produto guarda o preço do m², e o tamanho de cada venda é informado no orçamento." />
                  </>
                )}
              </div>

              <div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
                {porMedida && (
                  <>
                    <Campo rotulo="Largura de referência (m)" valor={largura} aoMudar={setLargura}
                           dica="Medida só para medir o custo — o tamanho real de cada peça vendida vai no orçamento. Use o tamanho que você mais vende." />
                    <Campo rotulo="Altura de referência (m)" valor={altura} aoMudar={setAltura} />
                  </>
                )}
                <Campo rotulo="Peças por pedido" valor={quantidade} aoMudar={setQuantidade}
                       dica="Quantas peças saem num pedido típico. O preparo da máquina acontece uma vez por pedido e se dilui entre elas." />
              </div>
              {/* A referência do modelo em evidência: o preparo se dilui nela,
                  e o preço por m² do fim é calibrado nela — deixar o default
                  quieto era a maior fonte de m² inflado. */}
              {modeloEscolhido && porMedida &&
                largura === (campoDoNumero(modeloEscolhido.reference_width_m ?? 1) || "1") &&
                altura === (campoDoNumero(modeloEscolhido.reference_height_m ?? 1) || "1") && (
                <div className="flex items-start gap-2 rounded-xl bg-[var(--ks-color-status-warning-soft)] px-4 py-3">
                  <TriangleAlert size={14} className="mt-0.5 shrink-0 text-[var(--ks-color-status-warning)]" />
                  <p className="text-xs text-[var(--ks-color-text-secondary)]">
                    O modelo veio com <strong>{medida(larguraRef)} × {medida(alturaRef)} m</strong> de
                    referência. Este é o tamanho que você <strong>mais vende</strong>? O preparo se
                    dilui nele e o preço por m² é calibrado nele — ajuste acima antes de seguir.
                  </p>
                </div>
              )}
              {ehMedido && (
                <p className="text-xs text-[var(--ks-color-text-soft)]">
                  {medida(composicao.areaUnitaria)} m² por peça · {medida(composicao.areaTotal)} m² no pedido
                </p>
              )}
            </div>
          )}

          {/* -------------------------------------------------- passo 2 --- */}
          {ativo.id === "materiais" && (
            <div className="space-y-4">
              {chipsMateriais.length > 0 && (
                <div className="flex flex-wrap items-center gap-1.5">
                  <span className="text-[11px] text-[var(--ks-color-text-soft)] mr-1">Este produto costuma levar:</span>
                  {chipsMateriais.map((l) => (
                    <Chip key={l.item} rotulo={itensBiblioteca.get(l.item)?.name ?? l.item}
                          aoClicar={() => adicionarOpcional(l)} />
                  ))}
                </div>
              )}
              <Seletor
                opcoes={insumosDisponiveis}
                vazio="Nenhum insumo cadastrado ainda."
                aoEscolher={addMaterial}
                rotulo="Adicionar insumo"
                cadastro="insumo"
                aoCadastrar={setCadastrandoInsumo}
              />
              {materiais.length === 0 ? (
                <p className="text-sm text-[var(--ks-color-text-muted)] text-center py-6">
                  Lona, tinta, papel, blank… adicione o que o produto consome.
                </p>
              ) : (
                <div className="space-y-2">
                  {materiais.map((m, i) => (
                    <LinhaMaterial
                      key={m.id}
                      linha={m}
                      porMedida={porMedida}
                      larguraRef={larguraRef}
                      alturaRef={alturaRef}
                      aoMudar={(novo) => setMateriais((v) => v.map((x, j) => (j === i ? novo : x)))}
                      aoRemover={() => setMateriais((v) => v.filter((_, j) => j !== i))}
                    />
                  ))}
                </div>
              )}
            </div>
          )}

          {/* -------------------------------------------------- passo 3 --- */}
          {ativo.id === "processos" && (
            <div className="space-y-4">
              {chipsProcessos.length > 0 && (
                <div className="flex flex-wrap items-center gap-1.5">
                  <span className="text-[11px] text-[var(--ks-color-text-soft)] mr-1">Costuma entrar também:</span>
                  {chipsProcessos.map((l) => (
                    <Chip key={l.item} rotulo={itensBiblioteca.get(l.item)?.name ?? l.item}
                          aoClicar={() => adicionarOpcional(l)} />
                  ))}
                </div>
              )}
              <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
                <Seletor
                  opcoes={maquinasDisponiveis}
                  vazio="Nenhuma máquina cadastrada."
                  aoEscolher={(o) => addProcesso(o, "maquina")}
                  rotulo="Adicionar máquina"
                  cadastro="maquina"
                  aoCadastrar={setCadastrandoMaquina}
                />
                <Seletor
                  opcoes={cargosDisponiveis}
                  vazio="Nenhum cargo cadastrado."
                  aoEscolher={(o) => addProcesso(o, "pessoa")}
                  rotulo="Adicionar mão de obra"
                  cadastro="pessoa"
                  aoCriar={criarPessoa}
                />
              </div>
              {processos.length === 0 ? (
                <p className="text-sm text-[var(--ks-color-text-muted)] text-center py-6">
                  Tempo de máquina e tempo de gente — é aqui que eles viram custo.
                </p>
              ) : (
                <div className="space-y-2">
                  {processos.map((p, i) => (
                    <LinhaProcesso
                      key={p.id}
                      linha={p}
                      porMedida={porMedida}
                      aoMudar={(novo) => setProcessos((v) => v.map((x, j) => (j === i ? novo : x)))}
                      aoRemover={() => setProcessos((v) => v.filter((_, j) => j !== i))}
                    />
                  ))}
                </div>
              )}
            </div>
          )}

          {/* -------------------------------------------------- passo 4 --- */}
          {ativo.id === "margem" && (
            <div className="space-y-5">
              {categoriasComRegra.length > 0 && (
                <div className="space-y-1 max-w-sm">
                  <label className="text-xs text-[var(--ks-color-text-soft)] inline-flex items-center">
                    Categoria do produto
                    <InfoTooltip text="Escolhendo a categoria, a margem configurada PARA ELA entra sozinha — banner não tem a margem de brinde. O produto também é salvo já categorizado. Dá para ajustar a margem depois de escolher." />
                  </label>
                  <select
                    value={categoriaId}
                    onChange={(e) => {
                      const id = e.target.value;
                      setCategoriaId(id);
                      const c = categoriasComRegra.find((x) => x.id === id);
                      if (c) setMargem(String(c.marginTargetPct).replace(".", ","));
                    }}
                    className="ks-field w-full"
                  >
                    <option value="">Sem categoria — margem manual</option>
                    {categoriasComRegra.map((c) => (
                      <option key={c.id} value={c.id}>
                        {c.nome} · margem {c.marginTargetPct}%
                      </option>
                    ))}
                  </select>
                </div>
              )}
              <div className="flex flex-wrap items-center gap-2">
                {[30, 40, 50, 60].map((m) => (
                  <Alternar key={m} ativo={margemAlvo === m} aoClicar={() => setMargem(String(m))} rotulo={`${m}%`} />
                ))}
                <div className="w-28">
                  <input
                    inputMode="decimal"
                    value={margem}
                    onChange={(e) => { if (textoDecimalValido(e.target.value)) setMargem(e.target.value); }}
                    className="ks-field w-full text-center font-bold"
                    placeholder="%"
                  />
                </div>
                <InfoTooltip text={`Margem sobre o preço, não sobre o custo. Os ${variaveisPct.toFixed(1)}% que saem de cada venda (imposto, taxa de recebimento, perda) já são descontados por fora${fixoDaUnidade > 0 ? ", e os custos fixos entram pelo tempo de produção da ficha" : ""} — a margem escolhida é o que sobra limpo.`} />
              </div>
              <p className="text-sm text-[var(--ks-color-text-muted)]">
                De cada venda saem <strong>{variaveisPct.toFixed(1)}%</strong> em impostos e taxas
                {fixoDaUnidade > 0 && (
                  <> — e os custos fixos entram pelo tempo de produção:{" "}
                  <strong>{dinheiro(fixoDaUnidade)}</strong> por {ehMedido ? "m²" : "peça"}</>
                )}.
                O que você escolher aqui é o que sobra <strong>de verdade</strong>.
              </p>
            </div>
          )}

          {/* -------------------------------------------------- passo 5 --- */}
          {ativo.id === "resumo" && (
            <div className="space-y-3">
              <GrupoDoResumo titulo="Produto" aoEditar={() => irPara(0)}>
                <span className="font-semibold text-[var(--ks-color-text-primary)]">{nome || "—"}</span>
                {" · "}cobrado por {unidade}
                {ehMedido && <> · referência {medida(larguraRef)} × {medida(alturaRef)} m</>}
                {" · "}{medida(pecas)} {pecas === 1 ? "peça" : "peças"} por pedido
              </GrupoDoResumo>

              <GrupoDoResumo titulo={`Insumos (${materiais.length})`} aoEditar={() => irPara(1)}>
                {materiais.length === 0 ? "Nenhum." : (
                  <ul className="space-y-1">
                    {materiais.map((m) => (
                      <li key={m.id} className="flex justify-between gap-3">
                        <span className="truncate">{m.nome}
                          <span className="text-[var(--ks-color-text-soft)]"> · {ROTULO_DA_BASE[m.base]}</span>
                        </span>
                        <span className="shrink-0">{dinheiro(m.custoUnitario)}/{m.unidade}</span>
                      </li>
                    ))}
                  </ul>
                )}
              </GrupoDoResumo>

              <GrupoDoResumo titulo={`Produção (${processos.length})`} aoEditar={() => irPara(2)}>
                {processos.length === 0 ? "Nenhum processo." : (
                  <ul className="space-y-1">
                    {processos.map((p) => (
                      <li key={p.id} className="flex justify-between gap-3">
                        <span className="truncate">{p.nome}
                          <span className="text-[var(--ks-color-text-soft)]">
                            {" · "}{medida(p.minutos)} min{(p.minutosDeSetup ?? 0) > 0 ? ` + ${medida(p.minutosDeSetup ?? 0)} de preparo` : ""}
                          </span>
                        </span>
                        <span className="shrink-0">{dinheiro(p.custoHora)}/h</span>
                      </li>
                    ))}
                  </ul>
                )}
              </GrupoDoResumo>

              <GrupoDoResumo titulo="Margem" aoEditar={() => irPara(3)}>
                <span className="font-semibold text-[var(--ks-color-text-primary)]">{medida(margemAlvo)}%</span>
                {" "}limpos, com {variaveisPct.toFixed(1)}% de impostos e taxas descontados por fora
              </GrupoDoResumo>
            </div>
          )}

          {/* -------------------------------------------------- passo 6 --- */}
          {ativo.id === "resultado" && (
            <div className="space-y-5">
              <motion.div
                initial={{ scale: 0.9, opacity: 0 }}
                animate={{ scale: 1, opacity: 1 }}
                transition={{ delay: 0.1, type: "spring", stiffness: 260, damping: 18 }}
                className="rounded-3xl p-6 text-center text-white"
                style={{ background: "linear-gradient(135deg, var(--ks-color-primary), var(--ks-brand-600))" }}
              >
                <div className="text-xs uppercase tracking-[0.2em] opacity-80">Preço por {unidade}</div>
                <div className="text-5xl font-black mt-1">{dinheiro(preco.precoUnitario)}</div>
                <div className="text-xs mt-2 opacity-80">
                  No pedido de referência ({ehMedido ? `${medida(composicao.areaTotal)} m²` : `${medida(pecas)} ${pecas === 1 ? "peça" : "peças"}`}): {dinheiro(precoDoPedido)}
                </div>
              </motion.div>

              <div>
                <div className="flex h-3 rounded-full overflow-hidden bg-[var(--ks-color-bg-hover)]">
                  <div style={{ width: `${preco.composicao.custoPct}%`, background: "var(--ks-amber)" }} />
                  <div style={{ width: `${preco.composicao.variaveisPct}%`, background: "var(--ks-rose)" }} />
                  {preco.composicao.fixosPct > 0 && (
                    <div style={{ width: `${preco.composicao.fixosPct}%`, background: "var(--ks-sky)" }} />
                  )}
                  <div style={{ width: `${preco.composicao.margemPct}%`, background: "var(--ks-emerald)" }} />
                </div>
                <div className={`mt-2 grid gap-2 text-center text-[11px] ${fixosDoPedido > 0 ? "grid-cols-2 sm:grid-cols-4" : "grid-cols-3"}`}>
                  <Fatia cor="var(--ks-amber)" rotulo="Custo" valor={composicao.custoTotal} />
                  <Fatia cor="var(--ks-rose)" rotulo="Impostos e taxas" valor={impostosDoPedido} />
                  {fixosDoPedido > 0 && <Fatia cor="var(--ks-sky)" rotulo="Fixos pelo tempo" valor={fixosDoPedido} />}
                  <Fatia cor="var(--ks-emerald)" rotulo="Sobra para você" valor={sobraDoPedido} />
                </div>
              </div>

              {fixoMensal > 0 && preco.possivel && (
                <div className="flex items-start gap-2 rounded-2xl border border-[var(--ks-color-border-subtle)] px-4 py-3">
                  <span className="h-2 w-2 rounded-full mt-1.5 shrink-0"
                        style={{ background: contribuicaoDoPedido > 0 ? "var(--ks-emerald)" : "var(--ks-color-status-danger)" }} />
                  <p className="text-xs text-[var(--ks-color-text-muted)]">
                    Um pedido como este contribui com{" "}
                    <strong className="text-[var(--ks-color-text-primary)]">{dinheiro(contribuicaoDoPedido)}</strong>
                    {" "}para as contas do mês — paga <strong className="text-[var(--ks-color-text-primary)]">{irgPct.toFixed(1)}%</strong> do
                    custo fixo de {dinheiro(fixoMensal)}.
                    <InfoTooltip text="A contribuição é o preço menos matéria-prima, impostos e taxas. A mão de obra e os fixos pelo tempo ficam dentro dela: são exatamente o custo fixo sendo recuperado. Acumulando contribuições até cruzar o custo fixo, o mês bate o equilíbrio — acompanhe no Ponto de Equilíbrio." />
                  </p>
                </div>
              )}

              {composicao.itens.length > 0 && (
                <div className="rounded-2xl border border-[var(--ks-color-border-subtle)] p-4">
                  <div className="text-xs font-semibold text-[var(--ks-color-text-soft)] mb-2 flex items-center">
                    De onde vem o custo
                    {composicao.custoDeSetup > 0 && (
                      <InfoTooltip text={`${dinheiro(composicao.custoDeSetup)} do custo é preparo, que acontece uma vez por pedido — fazer mais peças dilui esse valor.`} />
                    )}
                  </div>
                  <div className="space-y-1.5">
                    {composicao.itens.map((i, k) => (
                      <div key={k} className="flex items-baseline justify-between gap-2 text-xs">
                        <div className="min-w-0">
                          <span className="text-[var(--ks-color-text-primary)]">{i.nome}</span>
                          <span className="text-[10px] text-[var(--ks-color-text-soft)] ml-2">{i.detalhe}</span>
                        </div>
                        <span className="font-semibold shrink-0">{dinheiro(i.custo)}</span>
                      </div>
                    ))}
                  </div>
                </div>
              )}

              {tabela.length > 0 && (
                <div className="rounded-2xl border border-[var(--ks-color-border-subtle)] p-4">
                  <div className="text-xs font-semibold text-[var(--ks-color-text-soft)] mb-2 flex items-center">
                    O mesmo produto em outros tamanhos
                    <InfoTooltip text="O custo do m² muda com o tamanho: o torsal, a ponteira e o preparo se diluem numa área maior. A última coluna mostra o que acontece cobrando o seu preço liso por m² — vermelho quando ele não paga nem o custo." />
                  </div>
                  <div className="overflow-x-auto -mx-1 px-1">
                    <table className="w-full text-xs border-separate border-spacing-y-1">
                      <thead>
                        <tr className="text-left text-[10px] uppercase tracking-wide text-[var(--ks-color-text-soft)]">
                          <th className="font-medium py-1">Tamanho</th>
                          <th className="font-medium py-1 text-right">Custo</th>
                          <th className="font-medium py-1 text-right">Preço justo</th>
                          <th className="font-medium py-1 text-right">A {dinheiro(preco.precoUnitario)}/m²</th>
                        </tr>
                      </thead>
                      <tbody>
                        {tabela.map((l) => (
                          <tr key={`${l.l}x${l.a}`} className="bg-[var(--ks-color-bg-inset)]"
                              style={l.ehReferencia ? { outline: "1px solid var(--ks-color-primary)", outlineOffset: "-1px" } : undefined}>
                            <td className="py-1.5 px-2 rounded-l-lg whitespace-nowrap">
                              {medida(l.l)} × {medida(l.a)} m
                              <span className="text-[10px] text-[var(--ks-color-text-soft)] ml-1">({medida(l.area)} m²)</span>
                            </td>
                            <td className="py-1.5 px-2 text-right text-[var(--ks-color-text-muted)] whitespace-nowrap">{dinheiro(l.custoDaPeca)}</td>
                            <td className="py-1.5 px-2 text-right font-semibold whitespace-nowrap">{dinheiro(l.precoJustoDaPeca)}</td>
                            <td className="py-1.5 px-2 rounded-r-lg text-right font-semibold whitespace-nowrap"
                                style={{ color: l.noPrejuizo ? "var(--ks-color-status-danger)" : "var(--ks-color-text-primary)" }}>
                              {dinheiro(l.comOM2Liso)}{l.noPrejuizo && " ⚠"}
                            </td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </div>
                </div>
              )}

              {preco.avisos.length > 0 && (
                <p className="text-xs text-[var(--ks-color-status-warning)] flex items-start gap-1.5">
                  <TriangleAlert size={13} className="mt-0.5 shrink-0" />
                  {preco.avisos[0]}
                </p>
              )}

              {aviso && (
                <div className="rounded-xl bg-[var(--ks-color-status-danger-soft)] px-4 py-3 text-sm text-[var(--ks-color-status-danger)]">
                  {aviso}
                </div>
              )}
            </div>
          )}

          {erroDoPasso && (
            <p className="mt-4 text-xs text-[var(--ks-color-status-danger)] flex items-center gap-1.5">
              <TriangleAlert size={13} /> {erroDoPasso}
            </p>
          )}

          {/* Navegação */}
          <div className="mt-8 flex flex-col-reverse sm:flex-row items-stretch sm:items-center gap-3">
            {passo > 0 && (
              <button
                type="button"
                onClick={() => irPara(passo - 1)}
                className="inline-flex h-12 items-center justify-center gap-2 rounded-2xl border border-[var(--ks-color-border-subtle)] px-6 text-sm font-semibold text-[var(--ks-color-text-muted)] transition hover:border-[var(--ks-color-primary)] hover:text-[var(--ks-color-primary)]"
              >
                <ArrowLeft size={15} /> Voltar
              </button>
            )}
            {ativo.id === "resultado" ? (
              podeSalvar && (
                <div className="flex flex-1 flex-col sm:flex-row gap-2">
                  <Button
                    onClick={() => void salvarComoProduto("produto")}
                    disabled={salvando}
                    className="flex-1 justify-center h-12 text-base"
                    icon={<Check size={16} />}
                  >
                    {salvando ? "Salvando…" : "Salvar e abrir produto"}
                  </Button>
                  <button
                    type="button"
                    onClick={() => void salvarComoProduto("outra")}
                    disabled={salvando}
                    className="inline-flex h-12 items-center justify-center gap-2 rounded-2xl border border-[var(--ks-color-primary)] px-5 text-sm font-semibold text-[var(--ks-color-primary)] transition hover:bg-[var(--ks-color-bg-hover)] disabled:opacity-50"
                  >
                    <Sparkles size={15} /> Salvar e precificar outro
                  </button>
                </div>
              )
            ) : (
              <Button
                onClick={avancar}
                className="flex-1 justify-center h-12 text-base"
                icon={ativo.id === "resumo" ? <Sparkles size={16} /> : <ArrowRight size={16} />}
              >
                {ativo.id === "resumo" ? "Confirmar e ver o preço" : "Continuar"}
              </Button>
            )}
          </div>
          {ativo.id === "resultado" && podeSalvar && (
            <p className="mt-2 text-center text-[11px] text-[var(--ks-color-text-soft)]">
              Salva o produto por {dinheiro(preco.precoUnitario)}/{unidade} com a ficha completa —
              e os insumos usados entram no seu cadastro sozinhos.
            </p>
          )}
        </motion.div>
      </AnimatePresence>

      <ModalDeInsumo
        aberta={cadastrandoInsumo !== null}
        nomeInicial={cadastrandoInsumo ?? ""}
        aoFechar={() => setCadastrandoInsumo(null)}
        aoSalvar={usarInsumoSalvo}
      />
      <ModalDeMaquina
        aberta={cadastrandoMaquina !== null}
        nomeInicial={cadastrandoMaquina ?? ""}
        aoFechar={() => setCadastrandoMaquina(null)}
        aoSalvar={usarMaquinaSalva}
      />
    </div>
  );
}

/* ------------------------------------------------------------------------- */

function GrupoDoResumo({ titulo, aoEditar, children }: {
  titulo: string; aoEditar: () => void; children: React.ReactNode;
}) {
  return (
    <div className="rounded-2xl border border-[var(--ks-color-border-subtle)] p-4">
      <div className="flex items-center justify-between mb-1.5">
        <span className="text-[11px] font-bold uppercase tracking-wide text-[var(--ks-color-text-soft)]">{titulo}</span>
        <button type="button" onClick={aoEditar}
                className="inline-flex items-center gap-1 text-[11px] font-semibold text-[var(--ks-color-primary)] hover:underline">
          <Pencil size={11} /> Editar
        </button>
      </div>
      <div className="text-sm text-[var(--ks-color-text-muted)]">{children}</div>
    </div>
  );
}

function Fatia({ cor, rotulo, valor }: { cor: string; rotulo: string; valor: number }) {
  return (
    <div>
      <div className="flex items-center justify-center gap-1 text-[var(--ks-color-text-soft)]">
        <span className="h-2 w-2 rounded-full" style={{ background: cor }} /> {rotulo}
      </div>
      <div className="font-bold text-[var(--ks-color-text-primary)] mt-0.5">{dinheiro(valor)}</div>
    </div>
  );
}

function Chip({ rotulo, aoClicar }: { rotulo: string; aoClicar: () => void }) {
  return (
    <button
      type="button"
      onClick={aoClicar}
      className="inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-[11px] font-semibold border border-dashed border-[var(--ks-color-border-subtle)] text-[var(--ks-color-text-muted)] hover:border-[var(--ks-color-primary)] hover:text-[var(--ks-color-primary)] transition"
    >
      <Plus size={11} /> {rotulo}
    </button>
  );
}

function Campo({ rotulo, valor, aoMudar, dica }: {
  rotulo: string; valor: string; aoMudar: (v: string) => void; dica?: string;
}) {
  return (
    <div className="space-y-1">
      <label className="text-xs text-[var(--ks-color-text-soft)] inline-flex items-center">
        {rotulo}
        {dica && <InfoTooltip text={dica} />}
      </label>
      <input inputMode="decimal" value={valor} onChange={(e) => { if (textoDecimalValido(e.target.value)) aoMudar(e.target.value); }} className="ks-field w-full" />
    </div>
  );
}

function Alternar({ ativo, rotulo, aoClicar }: { ativo: boolean; rotulo: string; aoClicar: () => void }) {
  return (
    <button
      type="button"
      onClick={aoClicar}
      className="px-4 py-2 rounded-full text-sm font-semibold border transition"
      style={ativo
        ? { background: "var(--ks-color-primary)", borderColor: "var(--ks-color-primary)", color: "#fff" }
        : { background: "transparent", borderColor: "var(--ks-color-border-subtle)", color: "var(--ks-color-text-muted)" }}
    >
      {rotulo}
    </button>
  );
}

/**
 * Escolher da lista OU cadastrar na hora, sem sair do passo.
 *
 * Insumo e máquina cadastram no MESMO modal das páginas de produção
 * (`aoCadastrar` abre ele): o formulário resumido que vivia aqui pedia a hora
 * de máquina seca, e quem não a sabe de cabeça chutava — o modal completo tem
 * o "calcular para mim". Mão de obra segue inline: a pergunta é só o salário,
 * e o sistema mastiga o valor do minuto sozinho.
 */
function Seletor({
  opcoes, vazio, rotulo, cadastro, aoEscolher, aoCriar, aoCadastrar,
}: {
  opcoes: Opcao[]; vazio: string; rotulo: string;
  cadastro: "insumo" | "maquina" | "pessoa";
  aoEscolher: (o: Opcao) => void;
  aoCriar?: (form: Record<string, string>) => Promise<Opcao | null>;
  /** Presente = "cadastrar novo" abre o modal completo com este nome. */
  aoCadastrar?: (nomeInicial: string) => void;
}) {
  const [aberto, setAberto] = useState(false);
  const [criando, setCriando] = useState(false);
  const [busca, setBusca] = useState("");
  const [form, setForm] = useState<Record<string, string>>({});
  const [salvandoNovo, setSalvandoNovo] = useState(false);
  const [erroNovo, setErroNovo] = useState("");
  const filtradas = opcoes.filter((o) => o.nome.toLowerCase().includes(busca.toLowerCase()));
  const raiz = useRef<HTMLDivElement>(null);

  const fechar = () => { setAberto(false); setCriando(false); setBusca(""); setForm({}); setErroNovo(""); };

  /* Clique fora (ou Esc) fecha: dropdown que so fecha pelo proprio botao
     prende a pessoa na tela. */
  useEffect(() => {
    if (!aberto) return;
    const aoClicarFora = (e: MouseEvent) => {
      if (raiz.current && !raiz.current.contains(e.target as Node)) fechar();
    };
    const aoEsc = (e: KeyboardEvent) => { if (e.key === "Escape") fechar(); };
    document.addEventListener("mousedown", aoClicarFora);
    document.addEventListener("keydown", aoEsc);
    return () => {
      document.removeEventListener("mousedown", aoClicarFora);
      document.removeEventListener("keydown", aoEsc);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [aberto]);

  const campo = (chave: string, rotuloDoCampo: string, placeholder?: string) => (
    <div className="space-y-1">
      <label className="text-[10px] text-[var(--ks-color-text-soft)]">{rotuloDoCampo}</label>
      <input
        inputMode="decimal"
        value={form[chave] ?? ""}
        placeholder={placeholder}
        onChange={(e) => { if (textoDecimalValido(e.target.value)) setForm((f) => ({ ...f, [chave]: e.target.value })); }}
        className="ks-field w-full text-sm"
      />
    </div>
  );

  /* O minuto mastigado: salário com encargos, dividido pelas horas que a
     pessoa realmente produz no mês. */
  const horaDaPessoa = () => {
    const salario = numeroDoCampo(form.salario ?? "");
    const encargos = numeroDoCampo(form.encargos ?? "");
    const horas = numeroDoCampo(form.horas ?? "") || 176;
    return salario > 0 ? duasCasas((salario * (1 + encargos / 100)) / horas) : 0;
  };

  const criar = async () => {
    if (!aoCriar) return;
    setSalvandoNovo(true);
    setErroNovo("");
    try {
      const opcao = await aoCriar(form);
      if (!opcao) { setErroNovo("Não foi possível cadastrar."); return; }
      aoEscolher(opcao);
      fechar();
    } finally {
      setSalvandoNovo(false);
    }
  };

  return (
    <div className="relative" ref={raiz}>
      <button
        type="button"
        onClick={() => (aberto ? fechar() : setAberto(true))}
        className="w-full flex items-center justify-center gap-2 py-2.5 rounded-xl border border-dashed border-[var(--ks-color-border-subtle)] text-sm font-semibold text-[var(--ks-color-text-muted)] hover:border-[var(--ks-color-primary)] hover:text-[var(--ks-color-primary)] transition"
      >
        <Plus size={15} /> {rotulo}
      </button>
      {aberto && (
        <div className="absolute z-20 mt-1 w-full rounded-xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-elevated)] shadow-lg p-2">
          {!criando ? (
            <>
              <input
                autoFocus
                value={busca}
                onChange={(e) => setBusca(e.target.value)}
                placeholder="Buscar…"
                className="ks-field w-full mb-2"
              />
              <div className="max-h-56 overflow-y-auto space-y-0.5">
                {filtradas.length === 0 ? (
                  <p className="text-xs text-[var(--ks-color-text-soft)] px-2 py-3 text-center">{vazio}</p>
                ) : (
                  filtradas.map((o) => (
                    <button
                      key={o.id}
                      type="button"
                      onClick={() => { aoEscolher(o); fechar(); }}
                      className="w-full flex items-center justify-between gap-2 px-2 py-1.5 rounded-lg text-left text-sm hover:bg-[var(--ks-color-bg-hover)]"
                    >
                      <span className="truncate">{o.nome}</span>
                      <span className="text-xs text-[var(--ks-color-text-soft)] shrink-0">
                        {dinheiro(o.custo)}{o.unidade ? `/${o.unidade}` : "/h"}
                      </span>
                    </button>
                  ))
                )}
              </div>
              <button
                type="button"
                onClick={() => {
                  if (aoCadastrar) { aoCadastrar(busca); fechar(); return; }
                  setCriando(true); setForm({ nome: busca });
                }}
                className="mt-2 w-full flex items-center justify-center gap-1.5 py-2 rounded-lg border border-dashed border-[var(--ks-color-primary)] text-xs font-semibold text-[var(--ks-color-primary)] hover:bg-[var(--ks-color-bg-hover)] transition"
              >
                <Plus size={13} />
                {cadastro === "insumo" ? "Cadastrar insumo novo" : cadastro === "maquina" ? "Cadastrar máquina nova" : "Cadastrar mão de obra"}
              </button>
            </>
          ) : (
            <div className="space-y-2 p-1">
              <div className="space-y-1">
                <label className="text-[10px] text-[var(--ks-color-text-soft)]">Nome</label>
                <input
                  autoFocus
                  value={form.nome ?? ""}
                  placeholder="Ex: Impressor"
                  onChange={(e) => setForm((f) => ({ ...f, nome: e.target.value }))}
                  className="ks-field w-full text-sm"
                />
              </div>

              {cadastro === "pessoa" && (
                <>
                  <div className="grid grid-cols-3 gap-2">
                    {campo("salario", "Salário/mês", "2.500")}
                    {campo("encargos", "Encargos %", "70")}
                    {campo("horas", "Horas/mês", "176")}
                  </div>
                  {horaDaPessoa() > 0 && (
                    <p className="text-[11px] text-[var(--ks-color-text-soft)]">
                      A hora sai a <strong className="text-[var(--ks-color-text-primary)]">{dinheiro(horaDaPessoa())}</strong>
                      {" "}— o minuto, {dinheiro(horaDaPessoa() / 60)}. Mastigado, sem calculadora.
                    </p>
                  )}
                </>
              )}

              {erroNovo && <p className="text-[11px] text-[var(--ks-color-status-danger)]">{erroNovo}</p>}

              <div className="flex gap-2 pt-1">
                <button type="button" onClick={() => setCriando(false)}
                        className="flex-1 py-2 rounded-lg border border-[var(--ks-color-border-subtle)] text-xs font-semibold text-[var(--ks-color-text-muted)]">
                  Voltar
                </button>
                <button type="button" onClick={() => void criar()} disabled={salvandoNovo}
                        className="flex-1 py-2 rounded-lg text-xs font-semibold text-white disabled:opacity-50"
                        style={{ background: "var(--ks-color-primary)" }}>
                  {salvandoNovo ? "Salvando…" : "Cadastrar e usar"}
                </button>
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

/* Material consome das cinco formas; tempo de máquina e de gente só conta por
   peça, por m² ou pelo contorno — minutos que "correm na largura" não existem
   no chão de fábrica, e oferecer a opção só gera estranheza. */
const BASES_DE_PROCESSO: BaseDeConsumo[] = ["por_peca", "por_m2", "por_perimetro"];

function BaseDoConsumo({
  base, porMedida, processo, aoMudar,
}: { base: BaseDeConsumo; porMedida: boolean; processo?: boolean; aoMudar: (b: BaseDeConsumo) => void }) {
  if (!porMedida) return null;
  const opcoes = (Object.entries(ROTULO_DA_BASE) as [BaseDeConsumo, string][])
    .filter(([valor]) => !processo || BASES_DE_PROCESSO.includes(valor) || valor === base);
  return (
    <div className="space-y-1">
      <label className="text-[10px] text-[var(--ks-color-text-soft)]">
        {processo ? "Os minutos contam" : "Consome"}
      </label>
      <select
        value={base}
        onChange={(e) => aoMudar(e.target.value as BaseDeConsumo)}
        className="ks-field text-xs"
        title="Este item acompanha o tamanho ou é fixo por peça?"
      >
        {opcoes.map(([valor, rotulo]) => (
          <option key={valor} value={valor}>{rotulo}</option>
        ))}
      </select>
    </div>
  );
}

/* O custo é editável na própria linha porque os números são sempre do usuário
   — e a linha que veio da biblioteca chega sem custo de propósito. */
function CampoDeCusto({
  valor, sufixo, aoMudar,
}: { valor: number; sufixo: string; aoMudar: (v: number) => void }) {
  const vazio = valor <= 0;
  return (
    <div className="w-28">
      <label className="text-[10px] text-[var(--ks-color-text-soft)]">Custo ({sufixo})</label>
      <CampoDecimal
        valor={valor}
        aoMudar={aoMudar}
        zeroVazio
        placeholder="0,00"
        className="ks-field w-full text-sm"
        style={vazio ? { borderColor: "var(--ks-color-status-warning)" } : undefined}
      />
    </div>
  );
}

function LinhaMaterial({
  linha, porMedida, larguraRef, alturaRef, aoMudar, aoRemover,
}: {
  linha: LinhaDeMaterial; porMedida: boolean;
  larguraRef: number; alturaRef: number;
  aoMudar: (l: LinhaDeMaterial) => void; aoRemover: () => void;
}) {
  const temAproveitamento = linha.aproveitamento !== undefined && linha.aproveitamento !== null;
  const temRodagem = Boolean(linha.larguraBobina) && linha.base === "por_m2" && porMedida;
  /* A pergunta da bobina, dita como o chão de fábrica pensa: roda junto com
     outros trabalhos (a regra) ou a peça ocupa a bobina sozinha (a exceção,
     com a perda saindo da geometria). */
  const definirRodagem = (rodagem: "casado" | "sozinho") => {
    if (rodagem === linha.rodagem) return;
    aoMudar({
      ...linha,
      rodagem,
      perdaEditada: false,
      perdaPct: rodagem === "sozinho" && linha.larguraBobina && larguraRef > 0 && alturaRef > 0
        ? perdaDeRodagem(linha.larguraBobina, larguraRef, alturaRef)
        : (linha.perdaCasada ?? 0),
    });
  };
  return (
    <div className="rounded-xl bg-[var(--ks-color-bg-inset)] p-3">
      <div className="flex items-center justify-between gap-2 mb-2">
        <span className="text-sm font-medium text-[var(--ks-color-text-primary)] truncate">{linha.nome}</span>
        <div className="flex items-center gap-2 shrink-0">
          {temRodagem && (
            <div className="flex items-center gap-1">
              <span className="text-[10px] text-[var(--ks-color-text-soft)] inline-flex items-center">
                roda
                <InfoTooltip text={`Como a peça roda na bobina de ${String(linha.larguraBobina).replace(".", ",")} m: CASADO junta com outros trabalhos e a perda é a típica do dia a dia (${String(linha.perdaCasada ?? 0).replace(".", ",")}%, editável). SOZINHO ocupa a largura inteira da bobina e a perda sai da geometria da peça.`} />
              </span>
              {(["casado", "sozinho"] as const).map((r) => (
                <button key={r} type="button" onClick={() => definirRodagem(r)}
                        className="px-2 py-0.5 rounded-full text-[10px] font-semibold border transition"
                        style={(linha.rodagem ?? "casado") === r
                          ? { background: "var(--ks-color-primary)", borderColor: "var(--ks-color-primary)", color: "#fff" }
                          : { borderColor: "var(--ks-color-border-subtle)", color: "var(--ks-color-text-muted)" }}>
                  {r}
                </button>
              ))}
            </div>
          )}
          <button type="button" onClick={aoRemover} className="text-[var(--ks-color-text-soft)] hover:text-[var(--ks-color-status-danger)]">
            <Trash2 size={14} />
          </button>
        </div>
      </div>
      <div className="flex flex-wrap items-end gap-2">
        <CampoDeCusto
          valor={linha.custoUnitario}
          sufixo={`R$/${linha.unidade}`}
          aoMudar={(v) => aoMudar({ ...linha, custoUnitario: v })}
        />
        {temAproveitamento ? (
          /* A pergunta do jeito que a gráfica pensa: quantas peças saem de uma
             folha. O consumo gravado é 1/N. */
          <div className="w-28">
            <label className="text-[10px] text-[var(--ks-color-text-soft)]">Peças por {linha.unidade}</label>
            <CampoDecimal valor={linha.aproveitamento ?? 0}
              aoMudar={(n) => aoMudar({ ...linha, aproveitamento: n, consumo: n > 0 ? Math.round((1 / n) * 10000) / 10000 : 0 })}
              className="ks-field w-full text-sm" />
          </div>
        ) : (
          <div className="w-24">
            <label className="text-[10px] text-[var(--ks-color-text-soft)]">Consumo ({linha.unidade})</label>
            <CampoDecimal valor={linha.consumo}
              aoMudar={(n) => aoMudar({ ...linha, consumo: n })}
              className="ks-field w-full text-sm" />
          </div>
        )}
        <BaseDoConsumo base={linha.base} porMedida={porMedida} aoMudar={(b) => aoMudar({ ...linha, base: b })} />
        <div className="w-20">
          <label className="text-[10px] text-[var(--ks-color-text-soft)] inline-flex items-center">
            Perda %
            {temRodagem && linha.rodagem === "sozinho" && !linha.perdaEditada && (
              <InfoTooltip text={`Calculada pela geometria: a peça ocupa sozinha a largura da bobina de ${String(linha.larguraBobina).replace(".", ",")} m. Edite se souber melhor — o número passa a ser seu.`} />
            )}
          </label>
          <CampoDecimal valor={linha.perdaPct ?? 0}
            aoMudar={(n) => aoMudar({ ...linha, perdaPct: n, perdaEditada: true })}
            className="ks-field w-full text-sm"
            style={temRodagem && linha.rodagem === "sozinho" && !linha.perdaEditada
              ? { borderColor: "var(--ks-brand-400)" } : undefined} />
        </div>
      </div>
    </div>
  );
}

function LinhaProcesso({
  linha, porMedida, aoMudar, aoRemover,
}: {
  linha: LinhaDeProcesso; porMedida: boolean;
  aoMudar: (l: LinhaDeProcesso) => void; aoRemover: () => void;
}) {
  return (
    <div className="rounded-xl bg-[var(--ks-color-bg-inset)] p-3">
      <div className="flex items-center justify-between gap-2 mb-2">
        <span className="text-sm font-medium text-[var(--ks-color-text-primary)] truncate">
          {linha.nome}
          <span className="ml-2 text-[10px] text-[var(--ks-color-text-soft)] uppercase">
            {linha.tipo === "maquina" ? "máquina" : "pessoa"}
          </span>
        </span>
        <button type="button" onClick={aoRemover} className="text-[var(--ks-color-text-soft)] hover:text-[var(--ks-color-status-danger)] shrink-0">
          <Trash2 size={14} />
        </button>
      </div>
      <div className="flex flex-wrap items-end gap-2">
        <CampoDeCusto
          valor={linha.custoHora}
          sufixo="R$/hora"
          aoMudar={(v) => aoMudar({ ...linha, custoHora: v })}
        />
        <div className="w-24">
          <label className="text-[10px] text-[var(--ks-color-text-soft)]">Minutos</label>
          <CampoDecimal valor={linha.minutos}
            aoMudar={(n) => aoMudar({ ...linha, minutos: n })}
            className="ks-field w-full text-sm" />
        </div>
        <BaseDoConsumo base={linha.base} porMedida={porMedida} processo aoMudar={(b) => aoMudar({ ...linha, base: b })} />
        <div className="w-24">
          <label className="text-[10px] text-[var(--ks-color-text-soft)] inline-flex items-center">
            Preparo (min)
          </label>
          <CampoDecimal valor={linha.minutosDeSetup ?? 0}
            aoMudar={(n) => aoMudar({ ...linha, minutosDeSetup: n })}
            className="ks-field w-full text-sm" />
        </div>
      </div>
    </div>
  );
}
