/* ===== Quadro de Espaço — tela inicial do Dono (celular primeiro) =====
   Regras: o sistema avisa e nunca bloqueia; troca forçada pede motivo.
   Três estados operacionais: com espaço · carregado sem destino · na fila da usina.
   Dados fictícios, sem banco. */

navItems.splice(0,0,{id:'espaco',label:'Quadro de espaço',short:'Espaço',icon:'▤',tag:'novo'});

const QE_PRODUTOS=['Diesel S10','Diesel S500','Gasolina C','Etanol'];
const qeRotulo={livre:'Tem espaço',cheio:'Sem espaço',semdestino:'Sem destino',fila:'Na fila'};
const qeEstadoLongo={livre:'Tem espaço',cheio:'Sem espaço',semdestino:'Carregado sem destino',fila:'Na fila da usina'};

/* Situação de cada caminhão hoje (fictícia). Capacidade, placa e motorista vêm do cadastro da frota. */
const initialEspaco=[
  {id:'TRK-05',pos:'Bebedouro',saidaH:12.3,modo:'rota',local:'Em rota · saiu 10:40',base:'Base Barretos',rota:['Bebedouro','Monte Azul Paulista'],cargas:[
    {posto:'Posto Avenida',cidade:'Bebedouro',prod:'Diesel S10',litros:20000,janela:'até 14:00',st:'a caminho'}]},
  {id:'TRK-09',pos:'Olímpia',saidaH:12.8,modo:'rota',local:'Em rota · saiu 10:10',base:'Base Barretos',rota:['Olímpia','Severínia'],cargas:[
    {posto:'Posto Imperial',cidade:'Olímpia',prod:'Etanol',litros:27000,janela:'até 15:00',st:'a caminho'}]},
  {id:'TRK-07',pos:'Barretos',saidaH:15,modo:'rota',local:'Carregando na base · saída 15:00',base:'Base Barretos',rota:['Bebedouro','Monte Azul Paulista'],cargas:[
    {posto:'Posto Avenida',cidade:'Bebedouro',prod:'Diesel S10',litros:20000,janela:'até 17:00',st:'programado'}]},
  {id:'TRK-06',pos:'Olímpia',saidaH:11.8,modo:'semdestino',local:'Carregado na Usina Olímpia · aguardando destino',base:'Usina Olímpia',rota:['Olímpia','Barretos','Colina'],cargas:[],produtoCarregado:'Etanol',carregado:45000},
  {id:'TRK-11',pos:'Guaíra',saidaH:14.5,modo:'fila',local:'Fila da Usina Guaíra · 4º da fila',base:'Usina Guaíra',rota:['Guaíra','Barretos'],cargas:[],produtoCarregado:'Etanol',carregado:0},
  {id:'TRK-12',pos:'Severínia',saidaH:11.9,modo:'rota',local:'Retornando vazio · passou por Severínia',base:'Base Barretos',rota:['Severínia','Barretos'],cargas:[]},
  {id:'TRK-04',pos:'Bebedouro',saidaH:12.6,modo:'rota',local:'Em rota · saiu 09:50',base:'Base Barretos',rota:['Bebedouro','Monte Azul Paulista'],cargas:[
    {posto:'Posto Avenida',cidade:'Bebedouro',prod:'Diesel S10',litros:30000,janela:'até 14:00',st:'entregue'},
    {posto:'Posto Santa Rita',cidade:'Monte Azul Paulista',prod:'Diesel S10',litros:15000,janela:'até 16:00',st:'a caminho'}]},
  {id:'TRK-10',pos:'Terra Roxa',saidaH:12.9,modo:'rota',local:'Em rota · saiu 10:30',base:'Base Barretos',rota:['Terra Roxa','Viradouro'],cargas:[
    {posto:'Rede Terra Roxa',cidade:'Terra Roxa',prod:'Gasolina C',litros:45000,janela:'até 16:00',st:'a caminho'}]},
  {id:'TRK-02',pos:'Barretos',saidaH:13.3,modo:'rota',local:'Carregando na base · saída 13:20',base:'Base Barretos',rota:['Guaíra'],cargas:[
    {posto:'Auto Posto Guaíra',cidade:'Guaíra',prod:'Gasolina C',litros:45000,janela:'sem restrição',st:'programado'}]},
  {id:'TRK-01',pos:'Barretos',saidaH:11.5,modo:'rota',local:'Na garagem · Barretos',base:'Base Barretos',rota:['Barretos'],cargas:[]},
  {id:'TRK-08',pos:'Barretos',saidaH:11.5,modo:'rota',local:'Na garagem · Barretos',base:'Base Barretos',rota:['Barretos'],cargas:[]},
];

const qeOcupado=(t)=>t.cargas.reduce((s,x)=>s+x.litros,0)+(t.carregado||0);
const qeLivre=(t)=>t.cap-qeOcupado(t);
function qeEstado(t){
  if(t.modo==='fila') return 'fila';
  if(t.modo==='semdestino') return 'semdestino';
  return qeLivre(t)>0?'livre':'cheio';
}

/* Avisos de encaixe: sempre informativos, nunca impedem. */
function qeNaBase(t,bases,garagem){ return (bases||[]).some(b=>b.city===qePos(t))||qePos(t)===garagem; }
function qeAvisos(t,{cidade,prod,litros,janela},bases,garagem){
  const av=[], lv=qeLivre(t);
  const temProdSolto=t.modo==='semdestino'&&t.produtoCarregado===prod&&(t.carregado||0)>=litros;
  if(bases&&!temProdSolto&&!qeNaBase(t,bases,garagem))
    av.push({t:'warn',txt:'Já está na estrada: para levar esta carga teria de passar numa base antes'});
  if(litros>lv) av.push({t:'crit',txt:`Passa ${fmt(litros-lv)} L do que cabe no ${t.id}`});
  if(!t.rota.includes(cidade)) av.push({t:'warn',txt:`${cidade} está fora da rota de hoje (${t.rota.join(' → ')})`});
  const atual=t.produtoCarregado||(t.cargas[0]&&t.cargas[0].prod);
  if(atual&&atual!==prod) av.push({t:'warn',txt:`Está com ${atual} a bordo · conferir compartimento`});
  if(qeEstado(t)==='fila') av.push({t:'warn',txt:'Ainda sem hora de saída da usina'});
  if(janela&&janela!=='sem restrição') av.push({t:'warn',txt:`O posto recebe ${janela} · conferir se a rota chega a tempo`});
  return av;
}

/* ===== Comparação de decisões: o que fazer com um pedido novo =====
   Regra do negócio: caminhão só entrega o que carregou numa base. Por isso as saídas possíveis são:
   1. usar um caminhão que já está carregado e sem destino;
   2. aumentar a carga de um caminhão que ainda não saiu da base;
   3. mandar um caminhão novo carregar;
   4. trocar uma entrega de caminhão para abrir espaço/rota.
   Números estimados: R$ 7,20/km, 55 km/h, distância linha reta × 1,3. */
const qePos=(t)=>t.pos||t.rota[0]||'Barretos';
const qeUltima=(t)=>{const c=t.cargas.filter(x=>x.st!=='entregue');return c.length?c[c.length-1].cidade:qePos(t);};
const qePendentes=(t)=>t.cargas.filter(x=>x.st!=='entregue').length;
const qeHora=(h)=>clock(h-NOW_H);
const qeDesvio=(de,cidade,garagem)=>Math.max(0,Math.round(dist(de,cidade)+dist(cidade,garagem)-dist(de,garagem)));

function qeBaseDe(bases,prod,de,para){
  const ok=bases.filter(b=>b.products.includes(prod));
  if(!ok.length) return null;
  return ok.slice().sort((a,b)=>(dist(de,a.city)+dist(a.city,para))-(dist(de,b.city)+dist(b.city,para)))[0];
}

/* 1. Caminhão carregado e sem destino: o produto já está a bordo */
function qePlanoSemDestino(t,garagem,{cidade,prod,litros}){
  if(t.modo!=='semdestino'||t.produtoCarregado!==prod) return null;
  if((t.carregado||0)<litros) return null;
  const de=qePos(t);
  const km=Math.round(dist(de,cidade)+dist(cidade,garagem)-dist(de,garagem));
  const chegada=(t.saidaH||NOW_H)+dist(de,cidade)/VEL_MEDIA;
  const av=[];
  const sobra=(t.carregado||0)-litros;
  if(sobra>0) av.push({t:'warn',txt:`Sobram ${fmt(sobra)} L de ${prod} a bordo, ainda sem cliente`});
  return {tipo:'carregado',t,km,custo:km*CUSTO_KM,chegada,sobra,av,
    resumo:`Já está carregado com ${prod} em ${de} e sem destino: não precisa passar em base nenhuma`};
}

/* 2. Aumentar a carga de quem ainda não saiu da base */
function qePlanoAumentar(t,bases,garagem,{cidade,prod,litros}){
  if(t.modo!=='rota'||qePendentes(t)===0) return null;
  const naBase=bases.some(b=>b.city===qePos(t))||qePos(t)===garagem;
  const jaSaiu=(t.saidaH||NOW_H)<=NOW_H;
  if(!naBase||jaSaiu) return null;               // já está na estrada: não dá para pôr mais produto
  if(qeLivre(t)<litros) return null;
  const prodAtual=t.cargas[0]&&t.cargas[0].prod;
  const ult=qeUltima(t);
  const naRota=t.rota.includes(cidade);
  const km=naRota?0:qeDesvio(ult,cidade,garagem);
  const chegada=(t.saidaH||NOW_H)+dist(qePos(t),cidade)/VEL_MEDIA+0.5*qePendentes(t);
  const av=[];
  if(prodAtual&&prodAtual!==prod) av.push({t:'warn',txt:`Vai com ${prodAtual} a bordo · precisa de compartimento separado`});
  if(!naRota) av.push({t:'warn',txt:`${cidade} está fora da rota de hoje (${t.rota.join(' → ')})`});
  return {tipo:'aumentar',t,km,custo:km*CUSTO_KM,chegada,sobra:qeLivre(t)-litros,av,
    resumo:naRota?`Ainda carrega na base (saída ${qeHora(t.saidaH||NOW_H)}) e já vai a ${cidade}: entra na mesma viagem`
                 :`Ainda carrega na base (saída ${qeHora(t.saidaH||NOW_H)}): dá para aumentar a carga e desviar ${km} km`};
}

/* 3. Mandar um caminhão novo carregar na base */
function qePlanoNovo(t,bases,garagem,{cidade,prod,litros}){
  if(qeOcupado(t)>0||t.modo==='semdestino') return null;
  if(t.cap<litros) return null;
  const de=qePos(t), base=qeBaseDe(bases,prod,de,cidade);
  if(!base) return null;
  const km=Math.round(Math.max(0,dist(de,base.city)+dist(base.city,cidade)+dist(cidade,garagem)-dist(de,garagem)));
  const chegada=(t.saidaH||NOW_H)+dist(de,base.city)/VEL_MEDIA+base.loadH+dist(base.city,cidade)/VEL_MEDIA;
  const ocup=Math.round(litros/t.cap*100);
  const av=[];
  if(qeEstado(t)==='fila') av.push({t:'warn',txt:'Ainda está na fila da usina, sem hora de saída'});
  if(ocup<60) av.push({t:'warn',txt:`Viagem inteira com só ${ocup}% do tanque ocupado`});
  return {tipo:'novo',t,km,custo:km*CUSTO_KM,chegada,ocup,base,av,
    resumo:`Sai de ${de}, carrega na ${base.name} e entrega · ${ocup}% do tanque`};
}

/* 4. Tirar uma entrega de quem já vai à cidade, para abrir espaço lá */
function qePlanoTroca(board,bases,garagem,{cidade,prod,litros}){
  let melhor=null;
  for(const alvo of board){
    if(alvo.modo!=='rota'||!alvo.rota.includes(cidade)) continue;
    if(qeLivre(alvo)>=litros) continue;
    const naBase=bases.some(b=>b.city===qePos(alvo))||qePos(alvo)===garagem;
    if(!naBase||(alvo.saidaH||NOW_H)<=NOW_H) continue;   // só dá para remanejar antes de sair
    alvo.cargas.forEach((carga,i)=>{
      if(carga.st==='entregue'||carga.cidade===cidade) return;      // trocar pela própria cidade não resolve
      if(qeLivre(alvo)+carga.litros<litros) return;
      for(const dest of board){
        if(dest.id===alvo.id||qeLivre(dest)<carga.litros) continue;
        const destNaBase=bases.some(b=>b.city===qePos(dest))||qePos(dest)===garagem;
        if(dest.modo==='fila'||(!destNaBase&&qeOcupado(dest)===0)) continue; // vazio na estrada teria de voltar à base
        const prodDest=dest.produtoCarregado||(dest.cargas[0]&&dest.cargas[0].prod);
        if(prodDest&&prodDest!==carga.prod) continue;
        const naRota=dest.rota.includes(carga.cidade);
        const km=naRota?0:qeDesvio(qeUltima(dest),carga.cidade,garagem);
        const chegada=(alvo.saidaH||NOW_H)+dist(qePos(alvo),cidade)/VEL_MEDIA+0.5*Math.max(0,qePendentes(alvo)-1);
        const chegadaMov=(dest.saidaH||NOW_H)+dist(qePos(dest),carga.cidade)/VEL_MEDIA+0.5*qePendentes(dest);
        const av=[];
        if(!naRota) av.push({t:'warn',txt:`${carga.cidade} está fora da rota do ${dest.id} (${dest.rota.join(' → ')})`});
        if(carga.janela&&carga.janela!=='sem restrição'&&carga.janela!=='a confirmar')
          av.push({t:'warn',txt:`${carga.posto} recebe ${carga.janela} · o ${dest.id} chega ~${qeHora(chegadaMov)}`});
        const p={tipo:'troca',t:alvo,carga,idx:i,dest,km,custo:km*CUSTO_KM,chegada,chegadaMov,av,
          resumo:`${carga.posto} (${fmt(carga.litros)} L) sai do ${alvo.id} e vai no ${dest.id}; o espaço que abre recebe este pedido`};
        if(!melhor||p.custo<melhor.custo||(p.custo===melhor.custo&&p.av.length<melhor.av.length)) melhor=p;
      }
    });
  }
  return melhor;
}

function qeComparar(board,bases,garagem,pedido){
  const cand=[];
  board.forEach(t=>{
    const a=qePlanoSemDestino(t,garagem,pedido); if(a) cand.push(a);
    const b=qePlanoAumentar(t,bases,garagem,pedido); if(b) cand.push(b);
    const c=qePlanoNovo(t,bases,garagem,pedido); if(c) cand.push(c);
  });
  const troca=qePlanoTroca(board,bases,garagem,pedido); if(troca) cand.push(troca);
  const porTipo={};
  cand.forEach(o=>{ o.risco=o.av.filter(a=>a.t==='crit').length*2+o.av.filter(a=>a.t==='warn').length;
    o.peso=o.custo+o.risco*40;
    if(!porTipo[o.tipo]||o.peso<porTipo[o.tipo].peso) porTipo[o.tipo]=o; });
  const ops=Object.values(porTipo).sort((a,b)=>a.peso-b.peso||a.chegada-b.chegada);
  return {ops,melhor:ops[0]};
}

const qeTituloOp=(o)=>o.tipo==='carregado'?`Usar o ${o.t.id}, já carregado`
  :o.tipo==='aumentar'?`Aumentar a carga do ${o.t.id}`
  :o.tipo==='novo'?`Mandar o ${o.t.id} carregar`
  :`Trocar entrega no ${o.t.id}`;
const qeSubOp={carregado:'Produto já está a bordo, só falta destino',aumentar:'Entra numa viagem que ainda não saiu',novo:'Uma viagem a mais na praça',troca:'Remaneja o que já estava programado'};

function qeVeredito(ops){
  if(!ops.length) return '';
  const [a,b]=ops;
  if(!b) return `Hoje só existe um caminho viável: ${qeTituloOp(a)}.`;
  const dif=Math.round(b.custo-a.custo), min=Math.round((b.chegada-a.chegada)*60);
  const partes=[];
  if(dif>0) partes.push(`custa ${brl(dif)} a menos`);
  if(min>0) partes.push(`chega ${min} min antes`);
  if(b.av.length>a.av.length) partes.push('tem menos ressalva');
  if(!partes.length) partes.push('empata em custo e horário, e é a mais simples de executar');
  return `${qeTituloOp(a)}: ${partes.join(', ')} que ${qeTituloOp(b)}.`;
}

function QeGauge({t}) {
  const cap=t.cap, oc=qeOcupado(t), lv=qeLivre(t), est=qeEstado(t);
  const excedeu=Math.max(0,oc-cap);
  return <>
    <div className="qe-gauge">
      {t.cargas.map((x,i)=><i key={i} className="qe-seg" style={{flex:x.litros}} title={`${x.posto} · ${fmt(x.litros)} L`}/>)}
      {t.carregado>0&&<i className="qe-seg" style={{flex:t.carregado}} title={`${t.produtoCarregado} · ${fmt(t.carregado)} L`}/>}
      {lv>0&&<i className="qe-free" style={{flex:lv}}/>}
      {excedeu>0&&<i className="qe-over" style={{flex:excedeu}} title={`${fmt(excedeu)} L acima da capacidade`}/>}
    </div>
    <div className="qe-legend"><span>{fmt(oc)} / {fmt(cap)} L</span><b className={`e-${est}`}>{excedeu>0?`excedido ${fmt(excedeu)} L`:est==='fila'?'aguardando carregar':(lv>0?`livre ${fmt(lv)} L`:'sem espaço')}</b></div>
  </>;
}

function QeCard({t,aberto,destaque,onToggle,onPedido,onTrocar}) {
  const est=qeEstado(t);
  const prods=t.cargas.length?[...new Set(t.cargas.map(x=>x.prod))].join(' + '):(t.produtoCarregado||'vazio');
  return <article className={`qe-truck e-${est}${destaque?" qe-novo":""}`}>
    <button className="qe-btn" onClick={onToggle} aria-expanded={aberto}>
      <div className="qe-head"><b className="qe-id">{t.id}</b><span className="qe-placa">{t.plate}</span><span className="qe-status">{qeRotulo[est]}</span></div>
      <p className="qe-meta"><b>{prods}</b> · {t.local}</p>
      <QeGauge t={t}/>
    </button>
    {aberto&&<div className="qe-detail">
      <p className="qe-eyebrow">Motorista {t.driver} · carrega em {t.base}</p>
      {t.cargas.length?t.cargas.map((x,i)=><div key={i} className="qe-stop"><i className={`qe-dot ${x.st==='entregue'?'done':x.st==='a caminho'?'now':''}`}/><div><b>{x.posto}</b><small>{x.cidade} · {x.prod} · recebe {x.janela} · {x.st}</small></div><span className="qe-vol">{fmt(x.litros)} L</span></div>)
        :<p className="qe-empty">{est==='fila'?'Ainda na fila da usina: sem produto a bordo e sem hora de saída.':est==='semdestino'?`Carregado com ${t.produtoCarregado}, sem entrega definida. É a carga que precisa de cliente.`:'Sem entrega programada. O caminhão está livre.'}</p>}
      <div className="qe-actions"><button className="secondary" onClick={onPedido}>Encaixar pedido</button><button className="secondary" onClick={onTrocar}>Trocar entrega</button></div>
    </div>}
  </article>;
}

function Espaco({fleet,clients,bases,orders,onAtender,notify,go,readOnly}) {
  const capOf=(id)=>{const f=fleet.find(x=>x.id===id);return f?f:{cap:30000,plate:'—',driver:'—'};};
  const [board,setBoard]=useState(()=>initialEspaco.map(t=>({...t,...(()=>{const f=capOf(t.id);return {cap:f.cap,plate:f.plate,driver:f.driver};})()})));
  const [tab,setTab]=useState('espaco');
  const [filtro,setFiltro]=useState('todos');
  const [aberto,setAberto]=useState('TRK-06');
  const [log,setLog]=useState([]);
  const [destaque,setDestaque]=useState('');
  const oficina=fleet.filter(t=>t.status==='manutencao').length;
  const garagem=(bases.find(b=>b.garage)||bases[0]||{city:'Barretos'}).city;
  const [origem,setOrigem]=useState(null); // pedido do vendedor que está sendo atendido

  /* ---- novo pedido ---- */
  const [ped,setPed]=useState({clientId:clients[0]?clients[0].id:'',prod:QE_PRODUTOS[0],litros:'8.000',preco:'5,62',prazo:'14 dias'});
  const [opcoes,setOpcoes]=useState(null);
  const setP=(k,v)=>setPed(f=>({...f,[k]:v}));
  const buscar=()=>{
    const c=clients.find(x=>x.id===ped.clientId); const litros=parseInt(String(ped.litros).replace(/\D/g,''),10);
    if(!c) return notify('Escolha um posto.');
    if(!litros||litros<1000) return setOpcoes({erro:'Informe o volume em litros (mínimo 1.000 L).'});
    const janela=c.note&&/hor|até|acesso/i.test(c.note)?'janela a confirmar':'sem restrição';
    const lista=board.filter(t=>qeLivre(t)>0).map(t=>({t,lv:qeLivre(t),av:qeAvisos(t,{cidade:c.city,prod:ped.prod,litros,janela:null},bases,garagem)}))
      .sort((a,b)=>a.av.length-b.av.length||b.lv-a.lv);
    const comp=qeComparar(board,bases,garagem,{cidade:c.city,prod:ped.prod,litros});
    setOpcoes({cliente:c,litros,janela,lista,comp});
  };
  const abrirPedido=(o)=>{
    const c=clients.find(x=>x.name===o.client)||clients.find(x=>x.city===o.city);
    setOrigem(o); setTab('pedido'); setOpcoes(null);
    setPed(f=>({...f,clientId:c?c.id:f.clientId,prod:o.product,litros:fmt(o.volume)}));
    window.scrollTo(0,0);
  };
  const concluir=(msg)=>{ if(origem&&onAtender) onAtender(origem.id); setOrigem(null); setOpcoes(null); setTab('espaco'); window.scrollTo(0,0); notify(msg); };

  /* Decisão 2: mandar um caminhão novo carregar na base */
  const aplicarNovo=(plano,cliente,litros)=>{
    if(readOnly) return notify('Modo visitante: você pode olhar tudo, mas não alterar.');
    setBoard(cur=>cur.map(x=>x.id!==plano.t.id?x:{...x,modo:'rota',base:plano.base.name,
      local:`Programado · carrega na ${plano.base.name}`,
      rota:[...new Set([...x.rota,cliente.city])],
      cargas:[...x.cargas,{posto:cliente.name,cidade:cliente.city,prod:ped.prod,litros,janela:'a confirmar',st:'programado'}]}));
    setAberto(plano.t.id); setDestaque(plano.t.id);
    setLog(cur=>[{at:clock(0),txt:`${cliente.name} · ${fmt(litros)} L: caminhão novo ${plano.t.id} carregando na ${plano.base.name}`,motivo:'Caminhão novo',avisos:plano.av.length},...cur]);
    concluir(`${plano.t.id} programado para carregar na ${plano.base.name}.`);
  };

  /* Decisão 3: trocar uma entrega de caminhão para abrir espaço */
  const aplicarTroca=(plano,cliente,litros)=>{
    if(readOnly) return notify('Modo visitante: você pode olhar tudo, mas não alterar.');
    setBoard(cur=>cur.map(x=>{
      if(x.id===plano.t.id) return {...x,cargas:[...x.cargas.filter((_,i)=>i!==plano.idx),
        {posto:cliente.name,cidade:cliente.city,prod:ped.prod,litros,janela:'a confirmar',st:'programado'}],
        rota:[...new Set([...x.rota,cliente.city])]};
      if(x.id===plano.dest.id) return {...x,modo:x.modo==='semdestino'?'rota':x.modo,
        carregado:x.modo==='semdestino'?Math.max(0,(x.carregado||0)-plano.carga.litros):x.carregado,
        rota:[...new Set([...x.rota,plano.carga.cidade])],
        cargas:[...x.cargas,plano.carga]};
      return x;
    }));
    setAberto(plano.t.id); setDestaque(plano.t.id);
    setLog(cur=>[{at:clock(0),txt:`${plano.carga.posto} · ${fmt(plano.carga.litros)} L de ${plano.t.id} para ${plano.dest.id}, abrindo espaço para ${cliente.name}`,motivo:'Abrir espaço',avisos:plano.av.length},...cur]);
    concluir(`Entrega remanejada: ${plano.carga.posto} vai no ${plano.dest.id} e ${cliente.name} entra no ${plano.t.id}.`);
  };

  const encaixar=(t,cliente,litros)=>{
    if(readOnly) return notify('Modo visitante: você pode olhar tudo, mas não alterar.');
    setBoard(cur=>cur.map(x=>x.id!==t.id?x:{...x,
      modo:x.modo==='semdestino'?'rota':x.modo,
      carregado:x.modo==='semdestino'?Math.max(0,(x.carregado||0)-litros):x.carregado,
      rota:x.rota.includes(cliente.city)?x.rota:[...x.rota,cliente.city],
      cargas:[...x.cargas,{posto:cliente.name,cidade:cliente.city,prod:ped.prod,litros,janela:'a confirmar',st:'programado'}]}));
    setAberto(t.id); setDestaque(t.id);
    concluir(`${fmt(litros)} L de ${ped.prod} encaixados no ${t.id}.`);
  };

  /* ---- trocar entrega ---- */
  const [troca,setTroca]=useState({carga:null,de:null,idx:null,para:null,motivo:''});
  const resetTroca=()=>setTroca({carga:null,de:null,idx:null,para:null,motivo:''});
  const confirmar=(avisos)=>{
    if(readOnly) return notify('Modo visitante: você pode olhar tudo, mas não alterar.');
    const {de,idx,para,carga,motivo}=troca;
    setBoard(cur=>cur.map(x=>{
      if(x.id===de.id) return {...x,cargas:x.cargas.filter((_,i)=>i!==idx)};
      if(x.id===para.id) return {...x,modo:x.modo==='semdestino'?'rota':x.modo,
        carregado:x.modo==='semdestino'?Math.max(0,(x.carregado||0)-carga.litros):x.carregado,
        rota:x.rota.includes(carga.cidade)?x.rota:[...x.rota,carga.cidade],
        cargas:[...x.cargas,carga]};
      return x;
    }));
    if(avisos.length) setLog(cur=>[{at:clock(0),txt:`${carga.posto} · ${fmt(carga.litros)} L de ${de.id} para ${para.id}`,motivo,avisos:avisos.length},...cur]);
    setAberto(para.id); setDestaque(para.id); resetTroca(); setTab('espaco'); window.scrollTo(0,0);
    notify(motivo?`Entrega trocada para ${para.id} · ${motivo}`:`Entrega trocada para ${para.id}.`);
  };

  const ordemEstado={semdestino:0,fila:1,livre:2,cheio:3};
  const visiveis=board.filter(t=>filtro==='todos'||qeEstado(t)===filtro)
    .slice().sort((a,b)=>(b.id===destaque)-(a.id===destaque)||ordemEstado[qeEstado(a)]-ordemEstado[qeEstado(b)]||qeLivre(b)-qeLivre(a));
  const totalLivre=board.filter(t=>qeEstado(t)==='livre').reduce((s,t)=>s+qeLivre(t),0);
  const nSem=board.filter(t=>qeEstado(t)==='semdestino').length;
  const nFila=board.filter(t=>qeEstado(t)==='fila').length;
  const motivos=['Cliente pediu','Falta de produto','Fila na usina','Caminhão quebrou','Outro'];

  return <div className="content qe-content">
    <div className="page-heading"><div><p>OPERAÇÃO DO DIA</p><h1>Quadro de espaço</h1><span>{fmt(totalLivre)} L livres em rota · {nSem} carregado sem destino · {nFila} na fila da usina{oficina?` · ${oficina} na oficina`:''}</span></div><div className="heading-actions"><button className="secondary" onClick={()=>go('logistica')}>Central Logística</button></div></div>

    <div className="qe-tabs" role="tablist">
      {[['espaco','Espaço'],['pedido','Encaixar pedido'],['trocar','Trocar entrega']].map(([k,l])=>
        <button key={k} role="tab" aria-selected={tab===k} className={tab===k?'on':''} onClick={()=>{setTab(k);if(k==='trocar')resetTroca();if(k==='pedido')setOpcoes(null);}}>{l}</button>)}
    </div>

    {tab==='espaco'&&<>
      {!!(orders&&orders.length)&&<section className="qe-fila">
        <p className="qe-eyebrow">Pedidos do vendedor esperando decisão</p>
        <div className="qe-fila-list">{orders.slice(0,4).map(o=><button key={o.id} className="qe-fila-card" onClick={()=>abrirPedido(o)}>
          <b>{o.client}</b><small>{o.city} · {o.product} · {o.window}</small><span>{fmt(o.volume)} L</span><em>Decidir →</em>
        </button>)}</div>
      </section>}
      <div className="qe-filters">{[['todos','Todos'],['livre','Com espaço'],['semdestino','Sem destino'],['fila','Na usina'],['cheio','Sem espaço']].map(([k,l])=>
        <button key={k} className={filtro===k?'on':''} aria-pressed={filtro===k} onClick={()=>setFiltro(k)}>{l}</button>)}</div>
      <div className="qe-list">
        {visiveis.map(t=><QeCard key={t.id} t={t} destaque={destaque===t.id} aberto={aberto===t.id} onToggle={()=>setAberto(aberto===t.id?null:t.id)} onPedido={()=>{setTab('pedido');setOpcoes(null);}} onTrocar={()=>{resetTroca();setTab('trocar');}}/>)}
        {!visiveis.length&&<p className="qe-empty">Nenhum caminhão neste filtro.</p>}
      </div>
      {log.length>0&&<section className="panel qe-log"><div className="panel-title"><div><h2>Decisões registradas</h2><p>Trocas e caminhões extras, com o motivo informado</p></div></div><ul className="ev-list">{log.map((l,i)=><li key={i} className="warn"><span>{l.at}</span><p>{l.txt} · <b>{l.motivo}</b> <small>({l.avisos} {l.avisos===1?'aviso':'avisos'})</small></p></li>)}</ul></section>}
      <p className="calc-note">Dados fictícios de demonstração. O quadro mostra o que cabe em cada caminhão; a programação detalhada continua na Central Logística.</p>
    </>}

    {tab==='pedido'&&<section className="panel qe-form">
      <div className="panel-title"><div><h2>{origem?`Pedido ${origem.id}`:'Encaixar pedido'}</h2><p>{origem?`Enviado por ${origem.seller||'vendedor'} · ${origem.client} · ${origem.window}${origem.note?` · ${origem.note}`:''}`:'Cinco campos. O sistema mostra onde cabe e avisa das ressalvas — quem escolhe é você.'}</p></div>{origem&&<button className="secondary" onClick={()=>{setOrigem(null);setOpcoes(null);}}>Limpar</button>}</div>
      <div className="form-grid">
        <label className="wide">Posto<select id="qe-posto" value={ped.clientId} onChange={e=>setP('clientId',e.target.value)}>{clients.map(c=><option key={c.id} value={c.id}>{c.name} · {c.city}</option>)}</select></label>
        <label>Produto<select id="qe-prod" value={ped.prod} onChange={e=>setP('prod',e.target.value)}>{QE_PRODUTOS.map(p=><option key={p}>{p}</option>)}</select></label>
        <label>Litros<input id="qe-litros" value={ped.litros} onChange={e=>setP('litros',e.target.value)} inputMode="numeric"/></label>
        <label>Preço R$/L<input id="qe-preco" value={ped.preco} onChange={e=>setP('preco',e.target.value)} inputMode="decimal"/></label>
        <label>Prazo<select id="qe-prazo" value={ped.prazo} onChange={e=>setP('prazo',e.target.value)}>{['À vista','7 dias','14 dias','21 dias','28 dias'].map(p=><option key={p}>{p}</option>)}</select></label>
      </div>
      <div className="form-actions"><button className="primary" onClick={buscar}>Ver onde cabe</button></div>
      {opcoes&&opcoes.erro&&<p className="qe-alert crit"><i>!</i>{opcoes.erro}</p>}
      {opcoes&&opcoes.comp&&!!opcoes.comp.ops.length&&<div className="qe-compare">
        <p className="qe-eyebrow">Como atender este pedido · {fmt(opcoes.litros)} L de {ped.prod} para {opcoes.cliente.city}</p>
        <div className="qe-ops">{opcoes.comp.ops.map((o,i)=><div key={o.tipo} className={`qe-op${i===0?' best':''}`}>
          <div className="qe-op-head"><b>{qeTituloOp(o)}</b>{i===0&&<em>Recomendado</em>}</div>
          <small className="qe-op-sub">{qeSubOp[o.tipo]}</small>
          <div className="qe-op-nums">
            <p><span>Km a mais</span><b>{o.km} km</b></p>
            <p><span>Custo estimado</span><b className={i===0?'ok-num':''}>{brl(o.custo)}</b></p>
            <p><span>Chega por volta de</span><b>{qeHora(o.chegada)}</b></p>
            <p><span>{o.tipo==='novo'?'Ocupação do tanque':o.tipo==='troca'?'Entrega remanejada':'Sobra depois'}</span><b>{o.tipo==='novo'?`${o.ocup}%`:o.tipo==='troca'?`${fmt(o.carga.litros)} L`:`${fmt(o.sobra||0)} L`}</b></p>
          </div>
          <p className="qe-op-resumo">{o.resumo}</p>
          {o.av.map((a,j)=><p key={j} className={`qe-op-av ${a.t}`}>{a.t==='crit'?'!':'⚠'} {a.txt}</p>)}
          <button className={i===0?'primary':'secondary'} onClick={()=>o.tipo==='novo'?aplicarNovo(o,opcoes.cliente,opcoes.litros):o.tipo==='troca'?aplicarTroca(o,opcoes.cliente,opcoes.litros):encaixar(o.t,opcoes.cliente,opcoes.litros)}>
            {o.tipo==='troca'?'Fazer a troca':o.tipo==='novo'?`Programar ${o.t.id}`:`Usar o ${o.t.id}`}
          </button>
        </div>)}</div>
        <p className="qe-verdito">{qeVeredito(opcoes.comp.ops)}</p>
        <p className="calc-note">Conta da demonstração: R$ 7,20 por km, 55 km/h, distância aproximada e tempo de carregamento de cada base. Nenhuma opção fica bloqueada — a decisão é sua.</p>
      </div>}
      {opcoes&&opcoes.lista&&<div className="qe-result">
        <p className="qe-eyebrow">Todos os caminhões com espaço para {fmt(opcoes.litros)} L de {ped.prod} para {opcoes.cliente.city}</p>
        {!opcoes.lista.length&&<p className="qe-alert crit"><i>!</i>Nenhum caminhão com espaço agora. Dá para trocar uma entrega ou mandar carregar mais um.</p>}
        {opcoes.lista.map(({t,lv,av})=><button key={t.id} className="qe-pick" onClick={()=>encaixar(t,opcoes.cliente,opcoes.litros)}>
          <span className="qe-pick-main"><b>{t.id} · {qeEstadoLongo[qeEstado(t)]}</b><small>{av.length?av.map(a=>a.txt).join(' · '):'Encaixa sem ressalva'}</small></span>
          <span className="qe-pick-vol">{fmt(lv)} L<small>LIVRE</small></span>
        </button>)}
        {!!opcoes.lista.length&&<p className="calc-note">Quem não tem ressalva aparece em cima. Nenhuma opção fica bloqueada.</p>}
      </div>}
    </section>}

    {tab==='trocar'&&<section className="panel qe-form">
      {!troca.carga&&<>
        <div className="panel-title"><div><h2>Trocar entrega</h2><p>Passo 1 · qual entrega muda de caminhão</p></div></div>
        <div className="qe-picks">{board.flatMap(t=>t.cargas.map((x,i)=>x.st==='entregue'?null:
          <button key={t.id+i} className="qe-pick" onClick={()=>setTroca({carga:x,de:t,idx:i,para:null,motivo:''})}>
            <span className="qe-pick-main"><b>{x.posto}</b><small>{t.id} · {x.prod} · {x.cidade} · recebe {x.janela}</small></span>
            <span className="qe-pick-vol">{fmt(x.litros)} L</span></button>).filter(Boolean))}</div>
      </>}
      {troca.carga&&!troca.para&&<>
        <div className="panel-title"><div><h2>Trocar entrega</h2><p>Passo 2 · para qual caminhão</p></div></div>
        <p className="qe-alert ok"><i>→</i><b>{troca.carga.posto}</b> · {fmt(troca.carga.litros)} L de {troca.carga.prod}, hoje no {troca.de.id}.</p>
        <div className="qe-picks">{board.filter(t=>t.id!==troca.de.id).map(t=>
          <button key={t.id} className="qe-pick" onClick={()=>setTroca(cur=>({...cur,para:t}))}>
            <span className="qe-pick-main"><b>{t.id} · {qeEstadoLongo[qeEstado(t)]}</b><small>{t.local}</small></span>
            <span className="qe-pick-vol">{fmt(Math.max(0,qeLivre(t)))} L<small>LIVRE</small></span></button>)}</div>
        <div className="form-actions"><button className="secondary" onClick={resetTroca}>Cancelar</button></div>
      </>}
      {troca.carga&&troca.para&&(()=>{
        const av=qeAvisos(troca.para,{cidade:troca.carga.cidade,prod:troca.carga.prod,litros:troca.carga.litros,janela:troca.carga.janela});
        return <>
          <div className="panel-title"><div><h2>Trocar entrega</h2><p>Passo 3 · confirmar</p></div></div>
          <p className="qe-alert ok"><i>→</i><b>{troca.carga.posto}</b> · {fmt(troca.carga.litros)} L de {troca.carga.prod} · {troca.de.id} → {troca.para.id}</p>
          {av.length?av.map((a,i)=><p key={i} className={`qe-alert ${a.t}`}><i>{a.t==='crit'?'!':'⚠'}</i>{a.txt}</p>)
            :<p className="qe-alert ok"><i>✓</i>Sem ressalva: cabe e está na rota.</p>}
          {!!av.length&&<><p className="qe-eyebrow">Por que está trocando?</p>
            <div className="qe-filters">{motivos.map(m=><button key={m} className={troca.motivo===m?'on':''} aria-pressed={troca.motivo===m} onClick={()=>setTroca(cur=>({...cur,motivo:m}))}>{m}</button>)}</div></>}
          <div className="form-actions">
            <button className="secondary" onClick={resetTroca}>Cancelar</button>
            <button className={av.length?'danger':'primary'} disabled={!!av.length&&!troca.motivo} onClick={()=>confirmar(av)}>{av.length?'Trocar mesmo assim':'Confirmar troca'}</button>
          </div>
          {!!av.length&&!troca.motivo&&<p className="calc-note">Escolha o motivo para registrar a troca. O aviso não impede: só fica registrado.</p>}
        </>;
      })()}
    </section>}
  </div>;
}
