// ═══ GENESIS BUILD rbac-frota-completa · 2026-07-10 ═══
// Tidal — Solicitações (user view) + SolicitacoesOp (admin kanban)
// Tipos: manutencao | ideia | evento | compras | conteudo | outro
// Ideias viram prioridade por curtidas. Eventos aprovados vão pra Agenda.

const TIPOS = {
  manutencao: {pt:'manutenção',  en:'maintenance', icon:'tools',   color:'#A86A52', private:true},
  ideia:      {pt:'ideia',       en:'idea',        icon:'star',    color:'#C8A24A', private:false},
  evento:     {pt:'evento',      en:'event',       icon:'calendar',color:'#1F5755', private:false},
  compras:    {pt:'compras',     en:'shopping',    icon:'list',    color:'#5C8AB8', private:true},
  conteudo:   {pt:'conteúdo',    en:'content',     icon:'film',    color:'#7B6FA8', private:false},
  outro:      {pt:'outro',       en:'other',       icon:'flag',    color:'#7A8388', private:false},
};
const URGENCIAS = {
  low:  {pt:'baixa',  en:'low',    color:'#7A8388'},
  mid:  {pt:'média',  en:'medium', color:'#C8A24A'},
  high: {pt:'alta',   en:'high',   color:'#D9694B'},
};
const STATUS_LIST = ['open','triage','progress','done','rejected'];
const STATUS = {
  open:     {pt:'aberta',         en:'open',         dot:'#5C8AB8'},
  triage:   {pt:'em triagem',     en:'in triage',    dot:'#C8A24A'},
  progress: {pt:'em andamento',   en:'in progress',  dot:'#1F5755'},
  done:     {pt:'resolvida',      en:'resolved',     dot:'#7BB67E'},
  rejected: {pt:'arquivada',      en:'archived',     dot:'#A8A8A8'},
};

// ─── User view: minhas + mural ───
const Solicitacoes = ({state, setState}) => {
  const _staff = useMemo(() => (state.allUsers||[]).filter(u => u.role==='admin' || u.is_staff===true).sort((a,b)=>(a.nome||'').localeCompare(b.nome||'')), [state.allUsers]);
  const _canManage = (state.currentUser||{}).role==='admin' || (state.currentUser||{}).is_staff===true || ((state.currentUser||{}).permissoes||[]).includes('solicitacoes.moderar');
  const tt = useT();
  const lang = tt.lang;
  const toast = useToast();
  const isMobile = useMobile();
  const token = () => localStorage.getItem('genesis_token');
  const me = state.currentUser || {};

  const [tab, setTab] = useState('mural');
  const [filterTipo, setFilterTipo] = useState('all');
  const [filterStatus, setFilterStatus] = useState('all');
  const [sortBy, setSortBy] = useState('votos');
  const [opening, setOpening] = useState(null);
  const [requests, setRequests] = useState([]);
  const [loading, setLoading] = useState(true);

  const fetchRequests = async () => {
    try {
      const res = await fetch('/api/requests', { headers:{'Authorization':`Bearer ${token()}`} });
      if (res.ok) setRequests(await res.json());
      fetch('/api/requests/metricas/painel', { headers:{'Authorization':`Bearer ${token()}`} }).then(r=>r.ok?r.json():null).then(m=>m&&setMetricas(m)).catch(()=>{});
    } catch(e) { console.error(e); }
    finally { setLoading(false); }
  };

  useEffect(() => { fetchRequests(); }, []);

  const usersById = useMemo(() => {
    const map = {};
    (state.allUsers||[]).forEach(u => map[u.id] = u);
    return map;
  }, [state.allUsers]);

  const visibleRequests = useMemo(() => {
    return requests.filter(r => {
      if (tab === 'minhas') return r.autor === me.id;
      if (TIPOS[r.tipo]?.private && r.autor !== me.id) return false;
      return r.status !== 'rejected';
    }).filter(r => filterTipo === 'all' || r.tipo === filterTipo)
      .filter(r => tab !== 'mural' || filterStatus === 'all' || (filterStatus === 'andamento' ? ['triage','progress'].includes(r.status) : r.status === filterStatus))
      .sort((a,b) => {
        if (tab === 'mural' && sortBy === 'votos') { const dv = (b.votos||0) - (a.votos||0); if (dv !== 0) return dv; }
        return new Date(b.created_at||0) - new Date(a.created_at||0);
      });
  }, [requests, tab, filterTipo, filterStatus, sortBy, me.id]);

  const myCount = requests.filter(r => r.autor === me.id).length;

  const toggleLike = async (r) => {
    if (!me?.id) return;
    try {
      const res = await fetch(`/api/requests/${r.id}/votar`, { method:'POST', headers:{'Authorization':`Bearer ${token()}`} });
      const data = await res.json();
      if (res.ok) {
        setRequests(prev => prev.map(x => x.id === r.id ? {...x, votos: data.votos, votei: data.votei} : x));
        if (opening?.id === r.id) setOpening(prev => ({...prev, votos: data.votos, votei: data.votei}));
      }
    } catch(e) { toast.push({icon:'x', title: lang==='pt'?'Erro ao votar':'Vote failed'}); }
  };

  return (
    <main style={{padding: isMobile ? '16px 16px 80px' : '30px 36px 56px', flex:1}}>
      {/* Cabeçalho de página */}
      <div style={{marginBottom:22}}>
        <div className="t-eyebrow">{lang==='pt'?'pedidos e mural':'requests & wall'}</div>
        <h1 style={{fontFamily:'var(--fs)',fontSize:isMobile?30:38,letterSpacing:'-0.8px',lineHeight:1.05,marginTop:6}}>
          {lang==='pt'?<>Solicit<em style={{fontStyle:'italic',color:'var(--accent)'}}>ações.</em></>:<>Requ<em style={{fontStyle:'italic',color:'var(--accent)'}}>ests.</em></>}
        </h1>
      </div>

      {/* Tabs */}
      <div style={{display:'flex',gap:6,marginBottom:18,borderBottom:'1px solid var(--line-soft)',paddingBottom:0}}>
        {[
          {id:'mural',  pt:'mural',  en:'wall',  count:requests.filter(r=>!TIPOS[r.tipo]?.private&&r.status!=='rejected').length},
          {id:'minhas', pt:'minhas', en:'mine',  count:myCount},
          {id:'nova',   pt:'+ nova', en:'+ new'},
        ].map(t => (
          <button key={t.id} onClick={()=>setTab(t.id)}
            style={{
              background:'transparent',border:'none',padding:'10px 16px',
              fontFamily:'var(--fs)',fontStyle:'italic',fontSize:18,
              color: tab===t.id ? 'var(--accent)' : 'var(--muted)',
              borderBottom: tab===t.id ? '2px solid var(--accent)' : '2px solid transparent',
              cursor:'pointer',marginBottom:-1,transition:'all .15s'
            }}>
            {lang==='pt'?t.pt:t.en}
            {t.count!=null && t.count>0 && (
              <span style={{marginLeft:6,fontFamily:'var(--fm)',fontSize:11,fontStyle:'normal',opacity:0.6}}>{t.count}</span>
            )}
          </button>
        ))}
      </div>

      {tab === 'nova' ? (
        <NewRequestForm me={me} lang={lang} toast={toast} token={token} requests={requests} onVotar={(r)=>toggleLike(r)}
          onDone={()=>{ fetchRequests(); setTab('minhas'); }}/>
      ) : loading ? (
        // Skeleton loaders
        <div style={{display:'grid',gridTemplateColumns:isMobile?'1fr':'repeat(auto-fill,minmax(340px,1fr))',gap:14}}>
          {[1,2,3,4].map(i=>(
            <div key={i} style={{height:140,borderRadius:18,background:'linear-gradient(90deg,var(--cream-2) 25%,var(--cream-3) 50%,var(--cream-2) 75%)',backgroundSize:'200% 100%',animation:'shimmer 1.5s infinite'}}/>
          ))}
        </div>
      ) : (
        <>
          <div style={{display:'flex',gap:10,marginBottom:20,alignItems:'center',flexWrap:isMobile?'wrap':'nowrap'}}>
            {tab === 'mural' && (
              <div className="toggle" style={{display:'inline-flex',gap:3,background:'var(--cream-2)',borderRadius:11,padding:3,flexShrink:0}}>
                {[['votos', '🔥', lang==='pt'?'em alta':'trending'],['recentes', '🕐', lang==='pt'?'recentes':'newest']].map(([k, ic, l]) => (
                  <button key={k} onClick={()=>setSortBy(k)}
                    style={{display:'inline-flex',alignItems:'center',gap:5,padding:'6px 12px',borderRadius:9,border:'none',cursor:'pointer',fontFamily:'var(--fm)',fontSize:11.5,background:sortBy===k?'var(--surface)':'transparent',color:sortBy===k?'var(--ink)':'var(--muted)',fontWeight:sortBy===k?600:400,boxShadow:sortBy===k?'var(--shadow-sm)':'none'}}>
                    {ic} {l}
                  </button>
                ))}
              </div>
            )}
            <div style={{flex:1,minWidth:0,display:'flex',gap:5,overflowX:'auto',paddingBottom:2,WebkitOverflowScrolling:'touch'}}>
              <button onClick={()=>setFilterTipo('all')}
                style={{padding:'5px 12px',borderRadius:999,cursor:'pointer',fontFamily:'var(--fm)',fontSize:11,whiteSpace:'nowrap',flexShrink:0,border:'1px solid '+(filterTipo==='all'?'var(--ink)':'var(--line-soft)'),background:filterTipo==='all'?'var(--ink)':'var(--surface)',color:filterTipo==='all'?'var(--cream)':'var(--muted)'}}>
                {lang==='pt'?'todos':'all'}
              </button>
              {Object.entries(TIPOS).map(([k,v]) => (
                <button key={k} onClick={()=>setFilterTipo(filterTipo===k?'all':k)}
                  style={{display:'inline-flex',alignItems:'center',gap:5,padding:'5px 12px',borderRadius:999,cursor:'pointer',fontFamily:'var(--fm)',fontSize:11,whiteSpace:'nowrap',flexShrink:0,border:'1px solid '+(filterTipo===k?v.color:'var(--line-soft)'),background:filterTipo===k?v.color+'1A':'var(--surface)',color:filterTipo===k?v.color:'var(--muted)',fontWeight:filterTipo===k?600:400}}>
                  <Icon n={v.icon} size={10}/>{lang==='pt'?v.pt:v.en}
                </button>
              ))}
            </div>
            {tab === 'mural' && (
              <select className="select" value={filterStatus} onChange={e=>setFilterStatus(e.target.value)}
                style={{fontFamily:'var(--fm)',fontSize:11,padding:'6px 8px',flexShrink:0,maxWidth:130,color:filterStatus==='all'?'var(--muted)':'var(--ink)'}}>
                <option value="all">{lang==='pt'?'todas':'all statuses'}</option>
                <option value="open">{lang==='pt'?'abertas':'open'}</option>
                <option value="andamento">{lang==='pt'?'em andamento':'in progress'}</option>
                <option value="done">{lang==='pt'?'resolvidas':'resolved'}</option>
              </select>
            )}
          </div>

          <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(340px,1fr))',gap:14}}>
            {visibleRequests.map(r => (
              <RequestCard key={r.id} r={r} usersById={usersById} lang={lang}
                onClick={()=>setOpening(r)} onLike={()=>toggleLike(r)} me={me}/>
            ))}
            {visibleRequests.length === 0 && (
              <div className="empty" style={{padding:60,gridColumn:'1/-1',textAlign:'center'}}>
                {tab==='minhas'
                  ? (lang==='pt'?'você ainda não criou solicitações.':'no requests yet.')
                  : (lang==='pt'?'nada por aqui ainda.':'nothing here yet.')}
              </div>
            )}
          </div>
        </>
      )}

      {opening && (
        <RequestDetail r={opening} me={me} usersById={usersById} lang={lang} toast={toast}
          canManage={_canManage} staff={_staff}
          onAssign={async (uid) => {
            await fetch(`/api/requests/${opening.id}/assign`, { method:'PUT', headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`}, body: JSON.stringify({ user_id: uid }) });
            const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}});
            if (res.ok) { const all = await res.json(); setRequests(all); setOpening(all.find(x => x.id === opening.id) || opening); }
          }}
          
          onRefresh={async ()=>{ const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}}); if (res.ok) { const all = await res.json(); setRequests(all); setOpening(all.find(x => x.id === opening.id) || null); } }}
          onFundir={async (de)=>{
            if (!de || isNaN(de)) return;
            const rr = await fetch(`/api/requests/${opening.id}/duplicada`, {method:'POST',headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`},body:JSON.stringify({de})});
            const d = await rr.json();
            if (rr.ok) { toast.push({icon:'check', title: lang==='pt'?`Fundida com a #${de} — votos migrados 🔗`:`Merged into #${de} 🔗`}); const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}}); if (res.ok) setRequests(await res.json()); setOpening(null); }
            else toast.push({icon:'x', title: d.error || (lang==='pt'?'Não consegui fundir':'Merge failed')});
          }}
          onConverter={async (alvo) => {
            const r2 = await fetch(`/api/requests/${opening.id}/converter-${alvo}`, { method:'POST', headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`}, body: JSON.stringify({}) });
            const d2 = await r2.json().catch(()=>({}));
            if (r2.ok) toast.push({icon:'check', title: alvo==='faina' ? (lang==='pt'?`⚒ Virou a faina #${d2.faina_id}`:`⚒ Now task #${d2.faina_id}`) : (lang==='pt'?`🛒 Virou a PO REQ-${opening.id}`:`🛒 Now PO REQ-${opening.id}`)});
            else toast.push({icon:'x', title: d2.error || 'Erro'});
            const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}});
            if (res.ok) { const all = await res.json(); setRequests(all); setOpening(all.find(x => x.id === opening.id) || opening); }
          }}
          onConfirm={async (ok) => {
            const r2 = await fetch(`/api/requests/${opening.id}/confirm`, { method:'POST', headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`}, body: JSON.stringify({ ok }) });
            if (r2.ok) toast.push({icon: ok?'check':'bell', title: ok ? (lang==='pt'?'Valeu por confirmar! ✓':'Thanks for confirming! ✓') : (lang==='pt'?'Reaberta — o time foi avisado':'Reopened — the team was notified')});
            const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}});
            if (res.ok) { const all = await res.json(); setRequests(all); setOpening(all.find(x => x.id === opening.id) || opening); }
          }}
          token={token} onClose={()=>setOpening(null)}
          onLike={()=>toggleLike(opening)}
          onComment={async (msg, interno) => {
            await fetch(`/api/requests/${opening.id}/comment`, {
              method:'POST',
              headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`},
              body: JSON.stringify({msg, interno: !!interno})
            });
            await fetchRequests();
            // Atualiza o detail aberto
            const updated = await fetch(`/api/requests`, {headers:{'Authorization':`Bearer ${token()}`}});
            if (updated.ok) {
              const all = await updated.json();
              const fresh = all.find(x => x.id === opening.id);
              if (fresh) setOpening(fresh);
              setRequests(all);
            }
          }}
        />
      )}
    </main>
  );
};

// ─── card (estilo Canny: voto à esquerda · conteúdo · foto à direita) ───
const RequestCard = ({r, usersById, lang, onClick, onLike, me}) => {
  const tipo = TIPOS[r.tipo];
  const urg = URGENCIAS[r.urgencia];
  const status = STATUS[r.status];
  const author = usersById[r.autor];
  const liked = !!r.votei;
  const votavel = r.tipo === 'ideia' || r.tipo === 'evento' || r.tipo === 'conteudo';

  return (
    <div onClick={onClick} style={{
      background:'var(--surface)',border:'1px solid var(--line-soft)',borderRadius:18,
      padding:'14px 16px',cursor:'pointer',transition:'all .2s',
      boxShadow:'var(--shadow-sm)',
      display:'flex',gap:14,alignItems:'stretch'
    }}
    onMouseEnter={e=>{e.currentTarget.style.transform='translateY(-2px)';e.currentTarget.style.boxShadow='var(--shadow-lg)'}}
    onMouseLeave={e=>{e.currentTarget.style.transform='none';e.currentTarget.style.boxShadow='var(--shadow-sm)'}}>

      {/* caixa de voto — o coração do mural */}
      {votavel ? (
        <button onClick={e=>{e.stopPropagation();onLike()}}
          title={liked?(lang==='pt'?'tirar meu voto':'remove my vote'):(lang==='pt'?'eu também quero!':'me too!')}
          style={{
            display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',gap:1,
            width:50,alignSelf:'center',padding:'10px 0',borderRadius:13,cursor:'pointer',flexShrink:0,
            border:'1.5px solid '+(liked?'var(--teal)':'var(--line)'),
            background: liked?'var(--teal)':'var(--cream-2)',
            color: liked?'#fff':'var(--ink)',transition:'all .15s'
          }}>
          <span style={{fontSize:11,lineHeight:1,opacity:liked?1:0.55}}>▲</span>
          <span style={{fontFamily:'var(--fs)',fontSize:19,fontWeight:600,lineHeight:1.1,letterSpacing:'-0.5px'}}>{r.votos||0}</span>
        </button>
      ) : (
        <div style={{width:50,alignSelf:'center',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0,color:tipo.color,opacity:0.85}}>
          <div style={{width:38,height:38,borderRadius:12,background:tipo.color+'18',display:'grid',placeItems:'center'}}>
            <Icon n={tipo.icon} size={16}/>
          </div>
        </div>
      )}

      {/* conteúdo */}
      <div style={{flex:1,minWidth:0,display:'flex',flexDirection:'column',gap:5}}>
        <div style={{display:'flex',alignItems:'center',gap:6,flexWrap:'wrap'}}>
          <span style={{display:'inline-flex',alignItems:'center',gap:4,padding:'2px 8px',borderRadius:999,background:tipo.color+'16',color:tipo.color,fontFamily:'var(--fm)',fontSize:9.5,letterSpacing:0.8,textTransform:'uppercase',fontWeight:600}}>
            <Icon n={tipo.icon} size={9}/>{lang==='pt'?tipo.pt:tipo.en}
          </span>
          {urg && r.urgencia === 'high' && <span style={{fontFamily:'var(--fm)',fontSize:9.5,color:urg.color,fontWeight:600,textTransform:'uppercase',letterSpacing:0.8}}>{lang==='pt'?urg.pt:urg.en}</span>}
        </div>
        <div style={{fontFamily:'var(--fs)',fontSize:17,letterSpacing:'-0.3px',lineHeight:1.25}}>{r.titulo}</div>
        {(r.desc || r.descricao) && (
          <div style={{fontSize:12.5,color:'var(--ink-2)',lineHeight:1.5,display:'-webkit-box',WebkitLineClamp:2,WebkitBoxOrient:'vertical',overflow:'hidden'}}>
            {r.desc || r.descricao}
          </div>
        )}
        <div style={{display:'flex',alignItems:'center',gap:7,fontSize:10.5,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:'auto',paddingTop:3,flexWrap:'wrap'}}>
          <span style={{display:'inline-flex',alignItems:'center',gap:4}}>
            <span style={{width:6,height:6,borderRadius:'50%',background:status.dot,display:'inline-block'}}/>
            {lang==='pt'?status.pt:status.en}
          </span>
          {r.resposta_oficial && <span title={lang==='pt'?'tem resposta oficial 📣':'has official reply 📣'}>📣</span>}
          {r.duplicada_de && <span title={lang==='pt'?`fundida com a #${r.duplicada_de}`:`merged into #${r.duplicada_de}`}>🔗</span>}
          {r.link_tipo && <span title={r.link_tipo==='faina'?`faina #${r.link_id}`:`PO REQ-${r.id}`}>{r.link_tipo==='faina'?'⚒':'🛒'}</span>}
          <ReqIdade r={r} lang={lang}/>
          {author && <span style={{display:'inline-flex',alignItems:'center',gap:4}}>· <Avatar user={author} size="xs"/> {author.nome.split(' ')[0]}</span>}
          {r.responsavel_nome && <span style={{color:'var(--teal)'}}>→ {r.responsavel_nome.split(' ')[0]}</span>}
        </div>
      </div>

      {/* foto — quem tem, brilha; quem não tem, não deixa buraco */}
      {r.foto_url && (
        <img src={r.foto_url} alt="" loading="lazy"
          style={{width:76,height:76,borderRadius:13,objectFit:'cover',flexShrink:0,alignSelf:'center',border:'1px solid var(--line-soft)'}}/>
      )}
    </div>
  );
};

// ─── new request form ───
// compressão de foto no navegador (resize + JPEG) — poupa satélite e memória do celular
function _reqComprimirFoto(file, maxW = 1280, quality = 0.72) {
  return new Promise((resolve) => {
    if (!file || !file.type || !file.type.startsWith('image/')) { resolve(file); return; }
    const img = new Image();
    const url = URL.createObjectURL(file);
    img.onload = () => {
      URL.revokeObjectURL(url);
      let { width, height } = img;
      if (width > maxW) { height = Math.round(height * maxW / width); width = maxW; }
      const canvas = document.createElement('canvas');
      canvas.width = width; canvas.height = height;
      canvas.getContext('2d').drawImage(img, 0, 0, width, height);
      canvas.toBlob(b => resolve(b ? new File([b], (file.name||'foto').replace(/\.[^.]+$/,'') + '.jpg', { type: 'image/jpeg' }) : file), 'image/jpeg', quality);
    };
    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };
    img.src = url;
  });
}
const NewRequestForm = ({me, lang, toast, token, onDone, requests, onVotar}) => {
  const [tipo, setTipo] = useState('manutencao');
  const [titulo, setTitulo] = useState('');
  const [desc, setDesc] = useState('');
  const [local, setLocal] = useState('');
  const [urgencia, setUrgencia] = useState('low');
  const [saving, setSaving] = useState(false);
  const [fotoUrl, setFotoUrl] = useState(null);
  const [fotoBusy, setFotoBusy] = useState(false);
  const fotoRef = React.useRef(null);
  const minhaCabine = me && (me.cabine_atual || me.cab) ? String(me.cabine_atual || me.cab) : '';
  const trocarTipo = (t) => {
    setTipo(t);
    if (t === 'manutencao' && !local.trim() && minhaCabine) setLocal(lang==='pt' ? `Cabine ${minhaCabine}` : `Cabin ${minhaCabine}`);
  };
  const phTitulo = {
    manutencao: lang==='pt'?'ex.: torneira pingando, luz queimada…':'e.g.: dripping tap, dead light…',
    compras:    lang==='pt'?'o item — ex.: filtro de ar XYZ-99':'the item — e.g.: air filter XYZ-99',
    conteudo:   lang==='pt'?'nome do filme ou série…':'movie or series name…',
    ideia:      lang==='pt'?'sua ideia em poucas palavras…':'your idea in a few words…',
    evento:     lang==='pt'?'ex.: torneio de sinuca…':'e.g.: pool tournament…',
    outro:      lang==='pt'?'em poucas palavras…':'in a few words…',
  };
  const enviarFoto = async (file) => {
    if (!file) return;
    setFotoBusy(true);
    try {
      const comprimida = await _reqComprimirFoto(file);
      const fd = new FormData(); fd.append('photo', comprimida, comprimida.name || 'foto.jpg');
      const r = await fetch('/api/upload/photo', { method:'POST', headers:{ 'Authorization':`Bearer ${token()}` }, body: fd });
      const d = await r.json();
      if (r.ok && d.url) setFotoUrl(d.url);
      else toast.push({icon:'x', title: lang==='pt'?'Erro no upload':'Upload error'});
    } catch(e) { toast.push({icon:'x', title: lang==='pt'?'Erro no upload':'Upload error'}); }
    setFotoBusy(false);
  };

  const submit = async () => {
    if (!titulo.trim()) { toast.push({icon:'lock', title:lang==='pt'?'Título obrigatório':'Title required'}); return; }
    setSaving(true);
    try {
      const res = await fetch('/api/requests', {
        method:'POST',
        headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`},
        body: JSON.stringify({
          tipo, titulo:titulo.trim(), descricao:desc.trim(),
          local:local.trim(), urgencia, publico:!TIPOS[tipo].private, foto_url: fotoUrl
        })
      });
      if (res.ok) {
        toast.push({icon:'check', title:lang==='pt'?'Solicitação enviada!':'Request sent!',
          body: TIPOS[tipo].private
            ? (lang==='pt'?'visível só para você e operação':'visible to you and ops only')
            : (lang==='pt'?'publicado no mural':'posted to wall')});
        onDone();
      } else {
        const d = await res.json();
        toast.push({icon:'x', title:'Erro', body: d.error});
      }
    } catch(e) {
      toast.push({icon:'x', title:'Erro de conexão'});
    } finally { setSaving(false); }
  };

  const showUrg = tipo === 'manutencao';

  return (
    <div style={{maxWidth:680,background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:24,padding:'28px 32px'}}>
      <div style={{fontFamily:'var(--fs)',fontSize:30,letterSpacing:'-0.5px',marginBottom:6}}>
        {lang==='pt'?'nova solicitação':'new request'}
      </div>
      <div style={{fontSize:13,color:'var(--muted)',marginBottom:24}}>
        {lang==='pt'
          ? 'manutenção e compras ficam privadas. Ideias, eventos e conteúdo aparecem no mural pra todo mundo curtir.'
          : 'maintenance and shopping stay private. Ideas, events and content show up on the wall for everyone to like.'}
      </div>

      <Field label={lang==='pt'?'Tipo':'Type'}>
        <div style={{display:'grid',gridTemplateColumns:'repeat(3, 1fr)',gap:8}}>
          {Object.entries(TIPOS).map(([k,v]) => (
            <button key={k} type="button" onClick={()=>trocarTipo(k)}
              style={{
                padding:'12px 10px',borderRadius:12,
                border:`1px solid ${tipo===k?v.color:'var(--line-soft)'}`,
                background:tipo===k?v.color+'14':'transparent',
                color:tipo===k?v.color:'var(--ink-2)',
                fontFamily:'var(--fm)',fontSize:12,cursor:'pointer',transition:'all .15s',
                display:'flex',flexDirection:'column',alignItems:'center',gap:6
              }}>
              <Icon n={v.icon} size={18}/>
              <span>{lang==='pt'?v.pt:v.en}</span>
              {v.private && <span style={{fontSize:9,letterSpacing:0.8,opacity:0.6}}>{lang==='pt'?'privada':'private'}</span>}
            </button>
          ))}
        </div>
      </Field>

      <Field label={lang==='pt'?'Título':'Title'}>
        <input className="input" value={titulo} onChange={e=>setTitulo(e.target.value)}
          placeholder={phTitulo[tipo] || phTitulo.outro} style={{width:'100%'}}/>
        {(() => {
          // parecidas: palavras significativas do título contra o mural (evita duplicadas na origem)
          const palavras = titulo.toLowerCase().split(/\s+/).filter(w => w.length > 3);
          if (palavras.length === 0) return null;
          const parecidas = (requests || [])
            .filter(r => !TIPOS[r.tipo]?.private && r.status !== 'rejected')
            .map(r => ({ r, hits: palavras.filter(w => (r.titulo||'').toLowerCase().includes(w)).length }))
            .filter(x => x.hits > 0)
            .sort((a,b) => b.hits - a.hits || (b.r.votos||0) - (a.r.votos||0))
            .slice(0, 3);
          if (parecidas.length === 0) return null;
          return (
            <div style={{marginTop:8,background:'rgba(31,87,85,0.05)',border:'1px dashed var(--teal)',borderRadius:12,padding:'10px 13px'}}>
              <div style={{fontFamily:'var(--fm)',fontSize:10,letterSpacing:1,textTransform:'uppercase',color:'var(--teal)',marginBottom:7}}>
                {lang==='pt'?'já pediram algo parecido — que tal votar em vez de repetir?':'similar requests exist — vote instead of repeating?'}
              </div>
              <div style={{display:'grid',gap:5}}>
                {parecidas.map(({r}) => (
                  <div key={r.id} style={{display:'flex',alignItems:'center',gap:8}}>
                    <button type="button" onClick={()=>onVotar && onVotar(r)}
                      style={{display:'inline-flex',alignItems:'center',gap:5,padding:'4px 10px',borderRadius:999,cursor:'pointer',flexShrink:0,fontFamily:'var(--fm)',fontSize:11,fontWeight:600,border:'1.5px solid '+(r.votei?'var(--teal)':'var(--line)'),background:r.votei?'var(--teal)':'var(--surface)',color:r.votei?'#fff':'var(--ink)'}}>
                      ▲ {r.votos||0}
                    </button>
                    <span style={{fontSize:12.5,color:'var(--ink-2)',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{r.titulo}</span>
                  </div>
                ))}
              </div>
            </div>
          );
        })()}
      </Field>

      <Field label={lang==='pt'?'Descrição':'Description'}>
        <textarea className="input" value={desc} onChange={e=>setDesc(e.target.value)}
          rows={4} placeholder={lang==='pt'?'detalhes (opcional)…':'details (optional)…'} style={{width:'100%',resize:'vertical',fontFamily:'inherit'}}/>
      </Field>

      <Field label={lang==='pt'?'Local':'Location'}>
        <input className="input" value={local} onChange={e=>setLocal(e.target.value)}
          placeholder="Cab 902, Refeitório, Academia…" style={{width:'100%'}}/>
      </Field>

      <Field label={lang==='pt'?'Foto (opcional — vale mais que mil palavras)':'Photo (optional — worth a thousand words)'}>
        <input ref={fotoRef} type="file" accept="image/*" style={{display:'none'}}
          onChange={e=>{ enviarFoto(e.target.files && e.target.files[0]); e.target.value=''; }}/>
        {fotoUrl ? (
          <div style={{display:'flex',alignItems:'center',gap:10}}>
            <img src={fotoUrl} alt="" style={{width:74,height:74,objectFit:'cover',borderRadius:10,border:'1px solid var(--line-soft)'}}/>
            <button className="btn-tidal-sec rose" onClick={()=>setFotoUrl(null)}>{lang==='pt'?'remover':'remove'}</button>
          </div>
        ) : (
          <button className="btn-tidal-sec" disabled={fotoBusy} onClick={()=>fotoRef.current && fotoRef.current.click()}>📷 {fotoBusy ? '…' : (lang==='pt'?'anexar foto':'attach photo')}</button>
        )}
      </Field>

      {showUrg && (
        <Field label={lang==='pt'?'Urgência':'Urgency'}>
          <div style={{display:'flex',gap:8}}>
            {Object.entries(URGENCIAS).map(([k,v]) => (
              <button key={k} type="button" onClick={()=>setUrgencia(k)}
                style={{
                  padding:'8px 14px',borderRadius:10,
                  border:`1px solid ${urgencia===k?v.color:'var(--line-soft)'}`,
                  background:urgencia===k?v.color+'18':'transparent',
                  color:urgencia===k?v.color:'var(--ink-2)',
                  fontFamily:'var(--fm)',fontSize:12,cursor:'pointer'
                }}>
                {lang==='pt'?v.pt:v.en}
              </button>
            ))}
          </div>
        </Field>
      )}

      <div style={{display:'flex',gap:10,marginTop:24}}>
        <button className="btn" onClick={onDone}>{lang==='pt'?'Cancelar':'Cancel'}</button>
        <div style={{flex:1}}/>
        <button className="btn btn-primary" onClick={submit} disabled={saving}>
          <Icon n="check" size={14}/>{saving ? (lang==='pt'?'Enviando…':'Sending…') : (lang==='pt'?'Enviar':'Send')}
        </button>
      </div>
    </div>
  );
};

const Field = ({label, children}) => (
  <div style={{marginBottom:16}}>
    <div style={{fontFamily:'var(--fm)',fontSize:10,letterSpacing:1.2,textTransform:'uppercase',color:'var(--muted)',marginBottom:6}}>{label}</div>
    {children}
  </div>
);

// idade com cor progressiva (cinza → âmbar → vermelho)
const ReqIdade = ({r, lang}) => {
  const dias = Math.max(0, Math.floor((Date.now() - new Date(r.created_at).getTime()) / 86400000));
  const cor = dias <= 3 ? 'var(--muted-2)' : dias <= 10 ? 'var(--sun)' : 'var(--rose)';
  const done = r.status === 'done' || r.status === 'rejected';
  if (done) return null;
  return (
    <span title={lang==='pt'?`aberta há ${dias} dia${dias===1?'':'s'}`:`open for ${dias} day${dias===1?'':'s'}`}
      style={{fontFamily:'var(--fm)',fontSize:9.5,color:cor,fontWeight:dias>10?700:500,whiteSpace:'nowrap'}}>
      {dias===0 ? (lang==='pt'?'hoje':'today') : (lang==='pt'?`há ${dias}d`:`${dias}d ago`)}
    </span>
  );
};

// ─── detail modal ───
const RequestDetail = ({r, usersById, lang, toast, me, token, onClose, onLike, onComment, onMove, canManage, staff, onAssign, onConfirm, onConverter, onRefresh, onFundir}) => {
  const [respEdit, setRespEdit] = React.useState(null);
  const [comment, setComment] = useState('');
  const [sending, setSending] = useState(false);
  const [interno, setInterno] = useState(false);
  const [confirmando, setConfirmando] = useState(false);
  const tipo = TIPOS[r.tipo];
  const urg = URGENCIAS[r.urgencia];
  const author = usersById[r.autor];
  const isMine = r.autor === me?.id;
  const isAdmin = me?.role === 'admin' || me?.is_staff === true;

  const addComment = async () => {
    if (!comment.trim()) return;
    setSending(true);
    try { await onComment(comment.trim(), interno); setComment(''); setInterno(false); }
    finally { setSending(false); }
  };

  return (
    <div onClick={onClose} style={{
      position:'fixed',inset:0,background:'rgba(31,42,46,0.5)',backdropFilter:'blur(4px)',
      display:'flex',alignItems:'center',justifyContent:'center',zIndex:1000,padding:20
    }}>
      <div onClick={e=>e.stopPropagation()} style={{
        width:'100%',maxWidth:680,maxHeight:'90vh',background:'var(--cream)',borderRadius:24,
        overflow:'hidden',display:'flex',flexDirection:'column',animation:'slideIn .3s both'
      }}>
        <div style={{padding:'24px 28px',borderBottom:'1px solid var(--line-soft)',position:'relative'}}>
          <div style={{position:'absolute',top:0,left:0,right:0,height:3,background:tipo.color,borderRadius:'24px 24px 0 0'}}/>
          <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:8}}>
            <div style={{width:28,height:28,borderRadius:'50%',background:tipo.color+'20',color:tipo.color,display:'grid',placeItems:'center'}}>
              <Icon n={tipo.icon} size={14}/>
            </div>
            <div style={{fontFamily:'var(--fm)',fontSize:11,letterSpacing:1.4,textTransform:'uppercase',color:tipo.color,fontWeight:500}}>
              {lang==='pt'?tipo.pt:tipo.en}
            </div>
            <div style={{flex:1}}/>
            <div style={{display:'flex',alignItems:'center',gap:6,fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>
              <span style={{width:7,height:7,borderRadius:'50%',background:STATUS[r.status]?.dot,display:'inline-block'}}/>
              {lang==='pt'?STATUS[r.status]?.pt:STATUS[r.status]?.en}
              {r.resposta_oficial && <span title={lang==='pt'?'tem resposta oficial da gestão':'has an official reply'} style={{marginLeft:6}}>📣</span>}
              {r.duplicada_de && <span title={lang==='pt'?`fundida com a #${r.duplicada_de}`:`merged into #${r.duplicada_de}`} style={{marginLeft:6}}>🔗</span>}
            </div>
            <button className="btn ghost" onClick={onClose} style={{padding:'4px 8px'}}><Icon n="x" size={14}/></button>
          </div>
          <div style={{fontFamily:'var(--fs)',fontSize:28,letterSpacing:'-0.5px',lineHeight:1.2}}>{r.titulo}</div>
        </div>

        <div style={{flex:1,overflowY:'auto',padding:'20px 28px'}}>
          {r.descricao && (
            <div style={{fontSize:14,lineHeight:1.6,color:'var(--ink-2)',marginBottom:18}}>{r.descricao}</div>
          )}

          {/* resposta oficial da gestão — a palavra que a tripulação espera */}
          {(r.resposta_oficial || canManage) && (
              <div style={{marginBottom:18,background:'rgba(31,87,85,0.05)',border:'1.5px solid var(--teal)',borderRadius:14,padding:'12px 15px'}}>
                <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:r.resposta_oficial||respEdit!==null?6:0}}>
                  <div style={{fontFamily:'var(--fm)',fontSize:10,letterSpacing:1.2,textTransform:'uppercase',color:'var(--teal)',fontWeight:700}}>📣 {lang==='pt'?'Resposta oficial':'Official reply'}</div>
                  {canManage && respEdit===null && <button onClick={()=>setRespEdit(r.resposta_oficial||'')} style={{border:'none',background:'transparent',cursor:'pointer',color:'var(--teal)',fontFamily:'var(--fm)',fontSize:11}}>{r.resposta_oficial?(lang==='pt'?'editar':'edit'):(lang==='pt'?'+ responder':'+ reply')}</button>}
                </div>
                {respEdit !== null ? (
                  <div>
                    <textarea className="input" value={respEdit} onChange={e=>setRespEdit(e.target.value)} placeholder={lang==='pt'?'A posição da gestão sobre esta solicitação…':'Management position on this request…'} style={{width:'100%',boxSizing:'border-box',minHeight:70,resize:'vertical',fontSize:13,lineHeight:1.5}}/>
                    <div style={{display:'flex',gap:6,justifyContent:'flex-end',marginTop:6}}>
                      <button className="btn btn-sm" onClick={()=>setRespEdit(null)}>{lang==='pt'?'cancelar':'cancel'}</button>
                      <button className="btn btn-primary btn-sm" onClick={async ()=>{
                        const rr = await fetch(`/api/requests/${r.id}/resposta-oficial`, {method:'PUT',headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`},body:JSON.stringify({texto:respEdit})});
                        if (rr.ok) { toast.push({icon:'check',title:lang==='pt'?'Resposta oficial publicada 📣':'Official reply published 📣'}); setRespEdit(null); onRefresh && onRefresh(); }
                        else toast.push({icon:'x',title:lang==='pt'?'Não consegui salvar':'Could not save'});
                      }}>{lang==='pt'?'Publicar':'Publish'}</button>
                    </div>
                  </div>
                ) : r.resposta_oficial ? (
                  <div>
                    <div style={{fontSize:13.5,lineHeight:1.55,color:'var(--ink)',whiteSpace:'pre-wrap'}}>{r.resposta_oficial}</div>
                    <div style={{fontFamily:'var(--fm)',fontSize:10,color:'var(--muted)',marginTop:6}}>— {r.resposta_oficial_por_nome || 'gestão'}{r.resposta_oficial_em ? ` · ${new Date(r.resposta_oficial_em).toLocaleDateString(lang==='pt'?'pt-BR':'en-GB',{day:'2-digit',month:'short'})}` : ''}</div>
                  </div>
                ) : (
                  <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)',fontStyle:'italic'}}>{lang==='pt'?'ainda sem resposta oficial.':'no official reply yet.'}</div>
                )}
              </div>
            )}
            {r.duplicada_de && (
              <div style={{marginBottom:18,fontFamily:'var(--fm)',fontSize:12,color:'var(--muted)',background:'var(--cream-2)',border:'1px dashed var(--line)',borderRadius:10,padding:'9px 13px'}}>
                🔗 {lang==='pt'?<>Fundida com a solicitação <b>#{r.duplicada_de}</b> — os votos migraram pra lá.</>:<>Merged into request <b>#{r.duplicada_de}</b> — votes moved there.</>}
              </div>
            )}
          {r.foto_url && (
            <a href={r.foto_url} target="_blank" rel="noreferrer" style={{display:'inline-block',marginBottom:18}}>
              <img src={r.foto_url} alt="" style={{maxWidth:'100%',maxHeight:240,borderRadius:14,border:'1px solid var(--line-soft)',display:'block'}}/>
            </a>
          )}
          {r.link_tipo && (
            <div style={{marginBottom:18,display:'inline-flex',alignItems:'center',gap:7,fontFamily:'var(--fm)',fontSize:11.5,color:'var(--teal)',background:'rgba(31,87,85,0.1)',border:'1px solid var(--teal)',borderRadius:999,padding:'5px 14px'}}>
              {r.link_tipo==='faina' ? '⚒' : '🛒'} {lang==='pt'?'acompanhada como':'tracked as'} {r.link_tipo==='faina' ? `faina #${r.link_id}` : `PO REQ-${r.id}`} — {lang==='pt'?'o status herda de lá':'status inherits from there'}
            </div>
          )}
          {canManage && !r.link_tipo && r.status !== 'done' && r.status !== 'rejected' && (
            <div style={{marginBottom:18,display:'flex',gap:8,flexWrap:'wrap'}}>
              <button className="btn-tidal-sec" onClick={()=>onConverter && onConverter('faina')}>⚒ {lang==='pt'?'Virar faina no Bridge':'Turn into Bridge task'}</button>
              <button className="btn-tidal-sec" onClick={()=>{
                const de = prompt(lang==='pt'?'Fundir com a solicitação nº:':'Merge into request #:');
                if (de && onFundir) onFundir(parseInt(de));
              }}>🔗 {lang==='pt'?'Fundir duplicada':'Merge duplicate'}</button>
              {r.tipo === 'compras' && <button className="btn-tidal-sec" onClick={()=>onConverter && onConverter('po')}>🛒 {lang==='pt'?'Virar PO':'Turn into PO'}</button>}
            </div>
          )}

          <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(160px,1fr))',gap:12,marginBottom:20,padding:16,background:'var(--cream-2)',borderRadius:14}}>
            {author && (
              <Meta label={lang==='pt'?'Autor':'Author'}>
                <Avatar user={author} size="xs"/>{author.nome}
              </Meta>
            )}
            {r.local && <Meta label={lang==='pt'?'Local':'Location'}>{r.local}</Meta>}
            {r.created_at && <Meta label={lang==='pt'?'Criado':'Created'}>{new Date(r.created_at).toLocaleDateString(lang==='pt'?'pt-BR':'en-GB')} <ReqIdade r={r} lang={lang}/></Meta>}
            <Meta label={lang==='pt'?'Quem cuida':'Owner'}>
              {canManage ? (
                <select className="input select" value={r.responsavel_id || ''} onChange={e=>onAssign && onAssign(e.target.value ? parseInt(e.target.value) : null)}
                  style={{padding:'3px 8px',fontSize:12,maxWidth:170,boxSizing:'border-box'}}>
                  <option value="">{lang==='pt'?'— sem dono —':'— unassigned —'}</option>
                  {(staff||[]).map(u => <option key={u.id} value={u.id}>{u.nome}</option>)}
                </select>
              ) : (
                <span style={{color: r.responsavel_nome ? 'var(--teal)' : 'var(--muted)',fontStyle: r.responsavel_nome ? 'normal' : 'italic'}}>{r.responsavel_nome || (lang==='pt'?'sem dono ainda':'unassigned')}</span>
              )}
            </Meta>
            {urg && r.urgencia !== 'low' && (
              <Meta label={lang==='pt'?'Urgência':'Urgency'}>
                <span style={{color:urg.color,fontWeight:500}}>{lang==='pt'?urg.pt:urg.en}</span>
              </Meta>
            )}
          </div>

          {r.status === 'done' && r.confirmado == null && r.autor === me?.id && (
            <div style={{marginBottom:20,padding:'14px 18px',background:'rgba(123,182,126,0.12)',border:'1px solid var(--leaf)',borderRadius:14}}>
              <div style={{fontFamily:'var(--fs)',fontSize:16,marginBottom:4}}>{lang==='pt'?'Marcaram como resolvida — resolveu mesmo?':'Marked as resolved — did it actually solve it?'}</div>
              <div style={{fontFamily:'var(--fm)',fontSize:11.5,color:'var(--muted)',marginBottom:10}}>{lang==='pt'?'Sua confirmação fecha o ciclo (ou reabre se ainda não).':'Your confirmation closes the loop (or reopens it).'}</div>
              <div style={{display:'flex',gap:8}}>
                <button className="btn-tidal verde" disabled={confirmando} onClick={()=>{ setConfirmando(true); onConfirm && onConfirm(true); }}>👍 {lang==='pt'?'Resolveu':'Solved'}</button>
                <button className="btn-tidal-sec" disabled={confirmando} onClick={()=>{ setConfirmando(true); onConfirm && onConfirm(false); }}>↩ {lang==='pt'?'Ainda não — reabrir':'Not yet — reopen'}</button>
              </div>
            </div>
          )}
          {r.status === 'done' && r.confirmado === true && (
            <div style={{marginBottom:20,display:'inline-flex',alignItems:'center',gap:7,fontFamily:'var(--fm)',fontSize:11.5,color:'var(--leaf)',background:'rgba(123,182,126,0.12)',border:'1px solid var(--leaf)',borderRadius:999,padding:'5px 14px'}}>
              ✓ {lang==='pt'?'confirmado pelo autor':'confirmed by the author'}
            </div>
          )}

          {/* like */}
          {(r.tipo === 'ideia' || r.tipo === 'evento' || r.tipo === 'conteudo') && (() => {
            const liked = !!r.votei;
            return (
              <div style={{marginBottom:20,display:'flex',alignItems:'center',gap:10}}>
                <button className="btn" onClick={onLike}
                  style={{
                    borderColor: liked?'var(--accent-2)':'var(--line)',
                    color: liked?'var(--accent-2)':'var(--ink-2)',
                    background: liked?'rgba(217,105,75,0.08)':'transparent'
                  }}>
                  <span style={{fontSize:14,lineHeight:1}}>👍</span>{liked?(lang==='pt'?'votado':'voted'):(lang==='pt'?'eu também!':'me too!')}
                </button>
                <div style={{fontSize:12,color:'var(--muted)',fontFamily:'var(--fm)'}}>
                  {r.votos||0} {lang==='pt'?'votos':'votes'}
                  <span> · {lang==='pt'?'as mais votadas sobem no mural e viram prioridade':'top-voted rise on the wall and become priority'}</span>
                </div>
              </div>
            );
          })()}

          {/* mover status (admin) */}
          {onMove && isAdmin && (
            <div style={{marginBottom:20,padding:'14px 16px',background:'var(--cream-2)',borderRadius:14}}>
              <div className="t-eyebrow" style={{marginBottom:10}}>{lang==='pt'?'mover para':'move to'}</div>
              <div style={{display:'flex',gap:6,flexWrap:'wrap'}}>
                {[
                  {id:'open',     pt:'novas',        en:'new'},
                  {id:'triage',   pt:'triagem',      en:'triage'},
                  {id:'progress', pt:'andamento',    en:'progress'},
                  {id:'done',     pt:'resolvida',    en:'resolved'},
                ].map(s => (
                  <button key={s.id} onClick={()=>onMove(s.id)}
                    disabled={r.status===s.id}
                    style={{
                      padding:'7px 12px',borderRadius:10,cursor:r.status===s.id?'default':'pointer',
                      border:'1px solid var(--line)',fontFamily:'var(--fm)',fontSize:12,
                      background: r.status===s.id ? 'var(--ink)' : 'var(--cream)',
                      color: r.status===s.id ? 'white' : 'var(--ink)',
                      display:'flex',alignItems:'center',gap:5,
                      opacity: r.status===s.id ? 1 : 0.9,
                    }}>
                    <span style={{width:6,height:6,borderRadius:'50%',background:STATUS[s.id]?.dot}}/>
                    {lang==='pt'?s.pt:s.en}
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* comments */}
          <div style={{marginTop:24}}>
            <div style={{fontFamily:'var(--fs)',fontStyle:'italic',fontSize:18,marginBottom:12,color:'var(--accent)'}}>
              {lang==='pt'?'conversa':'thread'} <span style={{color:'var(--muted)',fontStyle:'normal',fontSize:13}}>{(r.comentarios||[]).length}</span>
            </div>
            {(r.comentarios||[]).map((c,i) => {
              const u = usersById[c.user_id];
              return (
                <div key={i} style={{display:'flex',gap:10,marginBottom:14,background: c.interno ? 'rgba(198,138,31,0.08)' : 'transparent',border: c.interno ? '1px dashed var(--sun)' : 'none',borderRadius: c.interno ? 10 : 0,padding: c.interno ? '8px 10px' : 0}}>
                  {u && <Avatar user={u} size="xs"/>}
                  <div style={{flex:1}}>
                    <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginBottom:2}}>
                      <strong style={{color:'var(--ink-2)',fontWeight:500}}>{u?.nome||c.user_id}</strong> · {c.when ? new Date(c.when).toLocaleDateString(lang==='pt'?'pt-BR':'en-GB') : ''}
                      {c.interno && <span style={{marginLeft:8,color:'#8B6313',fontWeight:600,letterSpacing:0.5}}>🔒 {lang==='pt'?'interno':'internal'}</span>}
                    </div>
                    <div style={{fontSize:13.5,lineHeight:1.5}}>{c.msg}</div>
                  </div>
                </div>
              );
            })}
            {(r.comentarios||[]).length === 0 && (
              <div style={{fontSize:13,color:'var(--muted)',fontStyle:'italic',fontFamily:'var(--fs)',paddingBottom:12}}>
                {lang==='pt'?'nenhum comentário ainda.':'no comments yet.'}
              </div>
            )}
            <div style={{display:'flex',gap:8,marginTop:12,borderTop:'1px solid var(--line-soft)',paddingTop:16,alignItems:'center'}}>
              <input className="input" value={comment} onChange={e=>setComment(e.target.value)}
                onKeyDown={e=>e.key==='Enter'&&addComment()}
                placeholder={interno ? (lang==='pt'?'nota interna — o autor não vê…':'internal note — the author cannot see…') : (lang==='pt'?'escreva uma mensagem…':'write a message…')}
                style={{flex:1,background: interno ? 'rgba(198,138,31,0.08)' : undefined,borderColor: interno ? 'var(--sun)' : undefined}}/>
              {canManage && (
                <button className={'btn-icone sm' + (interno ? '' : ' neutro')} onClick={()=>setInterno(v=>!v)} title={lang==='pt'?'nota interna (o autor não vê)':'internal note (hidden from the author)'}>🔒</button>
              )}
              <button className="btn-icone" onClick={addComment} disabled={sending} title={lang==='pt'?'enviar':'send'}>
                <Icon n="arrow_r" size={14}/>
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

const Meta = ({label, children}) => (
  <div>
    <div style={{fontFamily:'var(--fm)',fontSize:9.5,letterSpacing:1,textTransform:'uppercase',color:'var(--muted)',marginBottom:3}}>{label}</div>
    <div style={{fontSize:13,display:'flex',alignItems:'center',gap:6}}>{children}</div>
  </div>
);

// ─── ADMIN: Kanban ───
const SolicitacoesOp = ({state, setState}) => {
  const [metricas, setMetricas] = React.useState(null);
  const _staff = useMemo(() => (state.allUsers||[]).filter(u => u.role==='admin' || u.is_staff===true).sort((a,b)=>(a.nome||'').localeCompare(b.nome||'')), [state.allUsers]);
  const _canManage = (state.currentUser||{}).role==='admin' || (state.currentUser||{}).is_staff===true || ((state.currentUser||{}).permissoes||[]).includes('solicitacoes.moderar');
  const tt = useT();
  const lang = tt.lang;
  const toast = useToast();
  const isMobile = useMobile();
  const token = () => localStorage.getItem('genesis_token');
  const me = state.currentUser || {};

  const [filterTipo, setFilterTipo] = useState('all');
  const [opening, setOpening] = useState(null);
  const [dragId, setDragId] = useState(null);
  const [dragOver, setDragOver] = useState(null);
  const [requests, setRequests] = useState([]);
  const [loading, setLoading] = useState(true);
  const [mobileCol, setMobileCol] = useState('open'); // coluna ativa no mobile

  const usersById = useMemo(() => {
    const map = {};
    (state.allUsers||[]).forEach(u => map[u.id] = u);
    return map;
  }, [state.allUsers]);

  const fetchRequests = async () => {
    try {
      const res = await fetch('/api/requests', { headers:{'Authorization':`Bearer ${token()}`} });
      if (res.ok) setRequests(await res.json());
    } catch(e) { console.error(e); }
    finally { setLoading(false); }
  };

  useEffect(() => { fetchRequests(); }, []);

  const filtered = requests.filter(r => filterTipo === 'all' || r.tipo === filterTipo);

  const moveTo = async (id, status) => {
    try {
      const res = await fetch(`/api/requests/${id}/status`, {
        method:'PUT',
        headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`},
        body: JSON.stringify({status})
      });
      if (res.ok) {
        setRequests(prev => prev.map(r => r.id===id ? {...r, status} : r));
        if (opening?.id === id) setOpening(prev => ({...prev, status}));
        toast.push({icon:'check', title:lang==='pt'?`Movida para ${STATUS[status].pt}`:`Moved to ${STATUS[status].en}`});
      }
    } catch(e) { toast.push({icon:'x', title:'Erro ao mover'}); }
  };

  const cols = [
    {id:'open',     pt:'novas',          en:'new'},
    {id:'triage',   pt:'em triagem',     en:'in triage'},
    {id:'progress', pt:'em andamento',   en:'in progress'},
    {id:'done',     pt:'resolvidas',     en:'resolved'},
  ];

  const slaFor = (r) => {
    if (r.status === 'done' || r.status === 'rejected') return null;
    const created = new Date(r.criado);
    const days = Math.floor((Date.now() - created.getTime()) / 864e5);
    const target = r.urgencia === 'high' ? 1 : r.urgencia === 'mid' ? 3 : 7;
    return {days, target, over: days > target};
  };

  // Resumo
  const abertas = requests.filter(r => r.status !== 'done' && r.status !== 'rejected').length;
  const atrasadas = requests.filter(r => { const s = slaFor(r); return s && s.over; }).length;
  const resolvidas = requests.filter(r => r.status === 'done').length;

  return (
    <main style={{padding: isMobile ? '16px 16px 80px' : '30px 36px 56px', flex:1, overflow:'hidden', display:'flex', flexDirection:'column'}}>
      {/* Cabeçalho de página */}
      <div style={{marginBottom:18,flexShrink:0}}>
        <div className="t-eyebrow">{lang==='pt'?'painel operacional':'operations board'}</div>
        <h1 style={{fontFamily:'var(--fs)',fontSize:isMobile?28:36,letterSpacing:'-0.8px',lineHeight:1.05,marginTop:6}}>
          {lang==='pt'?<>Tri<em style={{fontStyle:'italic',color:'var(--accent)'}}>agem.</em></>:<>Tri<em style={{fontStyle:'italic',color:'var(--accent)'}}>age.</em></>}
        </h1>
      </div>

      {/* Resumo */}
      {!loading && (
        <div style={{display:'flex',gap:10,flexWrap:'wrap',marginBottom:16,flexShrink:0}}>
          <div style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:'2px solid var(--accent)',borderRadius:14,padding:'10px 16px',display:'flex',alignItems:'baseline',gap:7,boxShadow:'var(--shadow-sm)'}}>
            <span style={{fontFamily:'var(--fs)',fontSize:22,fontWeight:600,letterSpacing:'-0.5px',lineHeight:1}}>{abertas}</span>
            <span style={{fontSize:11.5,color:'var(--muted)',fontFamily:'var(--fm)'}}>{lang==='pt'?'abertas':'open'}</span>
          </div>
          <div style={{background:atrasadas>0?'rgba(217,105,75,0.12)':'var(--cream)',border:`1px solid ${atrasadas>0?'var(--accent)':'var(--line-soft)'}`,borderRadius:14,padding:'10px 16px',display:'flex',alignItems:'baseline',gap:7}}>
            <span style={{fontFamily:'var(--fs)',fontSize:22,fontWeight:600,letterSpacing:'-0.5px',lineHeight:1,color:atrasadas>0?'var(--accent-2)':'var(--ink)'}}>{atrasadas}</span>
            <span style={{fontSize:11.5,color:atrasadas>0?'var(--accent-2)':'var(--muted)',fontFamily:'var(--fm)'}}>{lang==='pt'?'atrasadas':'overdue'}</span>
          </div>
          <div style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:'2px solid var(--accent)',borderRadius:14,padding:'10px 16px',display:'flex',alignItems:'baseline',gap:7,boxShadow:'var(--shadow-sm)'}}>
            <span style={{fontFamily:'var(--fs)',fontSize:22,fontWeight:600,letterSpacing:'-0.5px',lineHeight:1,color:'var(--leaf)'}}>{resolvidas}</span>
            <span style={{fontSize:11.5,color:'var(--muted)',fontFamily:'var(--fm)'}}>{lang==='pt'?'resolvidas':'resolved'}</span>
          </div>
          {metricas && metricas.tempo_medio_dias > 0 && (
            <div style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:'2px solid var(--teal)',borderRadius:14,padding:'10px 16px',display:'flex',alignItems:'baseline',gap:7,boxShadow:'var(--shadow-sm)'}}>
              <span style={{fontFamily:'var(--fs)',fontSize:22,fontWeight:600,letterSpacing:'-0.5px',lineHeight:1,color:'var(--teal)'}}>{metricas.tempo_medio_dias}d</span>
              <span style={{fontSize:11.5,color:'var(--muted)',fontFamily:'var(--fm)'}}>{lang==='pt'?'tempo médio':'avg. time'}</span>
            </div>
          )}
          {metricas && metricas.top_votadas && metricas.top_votadas[0] && (
            <div title={metricas.top_votadas.map(t=>`${t.votos}× ${t.titulo}`).join(' · ')} style={{background:'rgba(31,87,85,0.05)',border:'1px solid var(--teal)',borderRadius:14,padding:'10px 16px',display:'flex',alignItems:'center',gap:8,maxWidth:280}}>
              <span style={{fontSize:14}}>👍</span>
              <span style={{fontSize:11.5,color:'var(--ink)',fontFamily:'var(--fm)',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{metricas.top_votadas[0].votos}× "{metricas.top_votadas[0].titulo}"</span>
            </div>
          )}
        </div>
      )}

      {/* Filtro por tipo */}
      <div style={{display:'flex',gap:8,marginBottom:16,flexWrap:'wrap',alignItems:'center',flexShrink:0}}>
        <button onClick={()=>setFilterTipo('all')} className={filterTipo==='all'?'btn btn-primary btn-sm':'btn btn-sm'}>
          {lang==='pt'?'todos':'all'} <span style={{opacity:0.6,marginLeft:4,fontFamily:'var(--fm)',fontSize:11}}>{requests.length}</span>
        </button>
        {Object.entries(TIPOS).map(([k,v]) => {
          const c = requests.filter(r=>r.tipo===k).length;
          return (
            <button key={k} onClick={()=>setFilterTipo(k)}
              className={filterTipo===k?'btn btn-primary btn-sm':'btn btn-sm'}
              style={filterTipo===k?{}:{borderColor:v.color+'40',color:v.color}}>
              <Icon n={v.icon} size={12}/>{lang==='pt'?v.pt:v.en} <span style={{opacity:0.6,marginLeft:4,fontFamily:'var(--fm)',fontSize:11}}>{c}</span>
            </button>
          );
        })}
      </div>

      {/* Seletor de coluna no mobile */}
      {isMobile && !loading && (
        <div style={{display:'flex',gap:6,marginBottom:14,flexShrink:0,overflowX:'auto',padding:4,background:'var(--cream-2)',borderRadius:12}}>
          {cols.map(col => {
            const n = filtered.filter(r => r.status === col.id).length;
            return (
              <button key={col.id} onClick={()=>setMobileCol(col.id)}
                style={{
                  flex:1,minWidth:'fit-content',padding:'8px 10px',borderRadius:9,border:'none',cursor:'pointer',
                  fontFamily:'var(--fm)',fontSize:11,whiteSpace:'nowrap',
                  background: mobileCol===col.id ? 'var(--cream)' : 'transparent',
                  color: mobileCol===col.id ? 'var(--ink)' : 'var(--muted)',
                  boxShadow: mobileCol===col.id ? '0 1px 4px rgba(31,42,46,0.1)' : 'none',
                  display:'flex',alignItems:'center',gap:5,justifyContent:'center'
                }}>
                <span style={{width:7,height:7,borderRadius:'50%',background:STATUS[col.id].dot}}/>
                {lang==='pt'?col.pt:col.en}
                <span style={{opacity:0.5}}>{n}</span>
              </button>
            );
          })}
        </div>
      )}

      {loading ? (
        // Skeleton loaders
        <div style={{flex:1,display:'grid',gridTemplateColumns:isMobile?'1fr':'repeat(4, 1fr)',gap:12,minHeight:0}}>
          {(isMobile?[1]:[1,2,3,4]).map(col=>(
            <div key={col} style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderRadius:18,padding:14,boxShadow:'var(--shadow-sm)'}}>
              <div style={{height:24,width:'60%',marginBottom:14,borderRadius:8,background:'linear-gradient(90deg,var(--cream-2) 25%,var(--cream-3) 50%,var(--cream-2) 75%)',backgroundSize:'200% 100%',animation:'shimmer 1.5s infinite'}}/>
              <div style={{display:'flex',flexDirection:'column',gap:8}}>
                {[1,2,3].map(i=>(
                  <div key={i} style={{height:80,borderRadius:12,background:'linear-gradient(90deg,var(--cream-2) 25%,var(--cream-3) 50%,var(--cream-2) 75%)',backgroundSize:'200% 100%',animation:'shimmer 1.5s infinite'}}/>
                ))}
              </div>
            </div>
          ))}
        </div>
      ) : (
      <div style={{flex:1,display:'grid',gridTemplateColumns:isMobile?'1fr':'repeat(4, 1fr)',gap:12,minHeight:0}}>
        {cols.filter(col => !isMobile || col.id === mobileCol).map(col => {
          const colReqs = filtered.filter(r => r.status === col.id);
          return (
            <div key={col.id}
              onDragOver={e=>{e.preventDefault();setDragOver(col.id)}}
              onDragLeave={()=>setDragOver(null)}
              onDrop={()=>{if(dragId)moveTo(dragId, col.id); setDragId(null); setDragOver(null);}}
              style={{
                background: dragOver===col.id ? 'var(--accent-soft)' : 'var(--cream)',
                border:`1px solid ${dragOver===col.id?'var(--accent)':'var(--line-soft)'}`,
                borderRadius:18,padding:14,display:'flex',flexDirection:'column',
                transition:'all .2s',minHeight:0
              }}>
              <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:12}}>
                <span style={{width:8,height:8,borderRadius:'50%',background:STATUS[col.id].dot,display:'inline-block'}}/>
                <div style={{fontFamily:'var(--fs)',fontStyle:'italic',fontSize:18,color:'var(--ink)'}}>
                  {lang==='pt'?col.pt:col.en}
                </div>
                <div style={{marginLeft:'auto',fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)',background:'var(--cream-2)',borderRadius:8,padding:'2px 9px',minWidth:18,textAlign:'center'}}>{colReqs.length}</div>
              </div>
              <div style={{flex:1,overflowY:'auto',display:'flex',flexDirection:'column',gap:8,paddingRight:4}}>
                {colReqs.map(r => {
                  const tipo = TIPOS[r.tipo];
                  const urg = URGENCIAS[r.urgencia];
                  const author = usersById[r.autor];
                  const sla = slaFor(r);
                  return (
                    <div key={r.id} draggable
                      onDragStart={()=>setDragId(r.id)}
                      onDragEnd={()=>{setDragId(null);setDragOver(null)}}
                      onClick={()=>setOpening(r)}
                      style={{
                        background:'#FDF7EE',border:`1px solid var(--line-soft)`,
                        borderRadius:12,padding:'12px 14px',cursor:'grab',
                        opacity:dragId===r.id?0.5:1,transition:'all .15s',
                        borderLeft:`3px solid ${tipo.color}`
                      }}
                      onMouseEnter={e=>e.currentTarget.style.background='var(--cream)'}
                      onMouseLeave={e=>e.currentTarget.style.background='#FDF7EE'}>
                      <div style={{display:'flex',alignItems:'center',gap:6,marginBottom:6}}>
                        <span style={{fontFamily:'var(--fm)',fontSize:9.5,letterSpacing:1.1,textTransform:'uppercase',color:tipo.color,fontWeight:500}}>
                          {lang==='pt'?tipo.pt:tipo.en}
                        </span>
                        {sla && sla.over && (
                          <span style={{fontFamily:'var(--fm)',fontSize:9,letterSpacing:0.5,color:'white',background:'var(--accent-2)',borderRadius:6,padding:'1px 6px',fontWeight:500}}>
                            {lang==='pt'?'ATRASADA':'OVERDUE'}
                          </span>
                        )}
                        {urg && r.urgencia !== 'low' && (
                          <span style={{fontFamily:'var(--fm)',fontSize:9.5,letterSpacing:0.8,color:urg.color,fontWeight:500,marginLeft:'auto'}}>
                            {lang==='pt'?urg.pt:urg.en}
                          </span>
                        )}
                      </div>
                      <div style={{fontSize:13,fontWeight:500,lineHeight:1.3,marginBottom:8}}>{r.titulo}</div>
                      <div style={{display:'flex',alignItems:'center',gap:6,fontSize:10.5,color:'var(--muted)',fontFamily:'var(--fm)'}}>
                        {author && <Avatar user={author} size="xs"/>}
                        <span style={{flex:1,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{r.local||r.autor_nome||''}</span>
                        {sla && (
                          <span style={{color:sla.over?'var(--accent-2)':'var(--muted)',fontWeight:sla.over?500:400}}>
                            {sla.days}d{sla.over?` >${sla.target}d`:''}
                          </span>
                        )}
                        {(r.comentarios||[]).length>0 && <span><Icon n="chat" size={10}/> {(r.comentarios||[]).length}</span>}
                      </div>
                    </div>
                  );
                })}
                {colReqs.length === 0 && (
                  <div style={{fontSize:11,color:'var(--muted)',fontStyle:'italic',textAlign:'center',padding:20,fontFamily:'var(--fm)'}}>
                    {lang==='pt'?'vazio':'empty'}
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>
      )}

      {opening && (
        <RequestDetail r={opening} me={me} usersById={usersById} lang={lang} toast={toast}
          canManage={_canManage} staff={_staff}
          onAssign={async (uid) => {
            await fetch(`/api/requests/${opening.id}/assign`, { method:'PUT', headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`}, body: JSON.stringify({ user_id: uid }) });
            const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}});
            if (res.ok) { const all = await res.json(); setRequests(all); setOpening(all.find(x => x.id === opening.id) || opening); }
          }}
          
          onRefresh={async ()=>{ const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}}); if (res.ok) { const all = await res.json(); setRequests(all); setOpening(all.find(x => x.id === opening.id) || null); } }}
          onFundir={async (de)=>{
            if (!de || isNaN(de)) return;
            const rr = await fetch(`/api/requests/${opening.id}/duplicada`, {method:'POST',headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`},body:JSON.stringify({de})});
            const d = await rr.json();
            if (rr.ok) { toast.push({icon:'check', title: lang==='pt'?`Fundida com a #${de} — votos migrados 🔗`:`Merged into #${de} 🔗`}); const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}}); if (res.ok) setRequests(await res.json()); setOpening(null); }
            else toast.push({icon:'x', title: d.error || (lang==='pt'?'Não consegui fundir':'Merge failed')});
          }}
          onConverter={async (alvo) => {
            const r2 = await fetch(`/api/requests/${opening.id}/converter-${alvo}`, { method:'POST', headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`}, body: JSON.stringify({}) });
            const d2 = await r2.json().catch(()=>({}));
            if (r2.ok) toast.push({icon:'check', title: alvo==='faina' ? (lang==='pt'?`⚒ Virou a faina #${d2.faina_id}`:`⚒ Now task #${d2.faina_id}`) : (lang==='pt'?`🛒 Virou a PO REQ-${opening.id}`:`🛒 Now PO REQ-${opening.id}`)});
            else toast.push({icon:'x', title: d2.error || 'Erro'});
            const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}});
            if (res.ok) { const all = await res.json(); setRequests(all); setOpening(all.find(x => x.id === opening.id) || opening); }
          }}
          onConfirm={async (ok) => {
            const r2 = await fetch(`/api/requests/${opening.id}/confirm`, { method:'POST', headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`}, body: JSON.stringify({ ok }) });
            if (r2.ok) toast.push({icon: ok?'check':'bell', title: ok ? (lang==='pt'?'Valeu por confirmar! ✓':'Thanks for confirming! ✓') : (lang==='pt'?'Reaberta — o time foi avisado':'Reopened — the team was notified')});
            const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}});
            if (res.ok) { const all = await res.json(); setRequests(all); setOpening(all.find(x => x.id === opening.id) || opening); }
          }}
          token={token} onClose={()=>setOpening(null)}
          onMove={(status)=>moveTo(opening.id, status)}
          onLike={async ()=>{
            await fetch(`/api/requests/${opening.id}/like`, {method:'POST', headers:{'Authorization':`Bearer ${token()}`}});
            setRequests(prev => prev.map(x => x.id===opening.id ? {...x, curtidas:(x.curtidas||0)+1} : x));
            setOpening(prev => ({...prev, curtidas:(prev.curtidas||0)+1}));
          }}
          onComment={async (msg, interno) => {
            await fetch(`/api/requests/${opening.id}/comment`, {
              method:'POST',
              headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`},
              body: JSON.stringify({msg, interno: !!interno})
            });
            await fetchRequests();
            const res = await fetch('/api/requests', {headers:{'Authorization':`Bearer ${token()}`}});
            if (res.ok) {
              const all = await res.json();
              setRequests(all);
              setOpening(all.find(x => x.id === opening.id) || opening);
            }
          }}
        />
      )}
    </main>
  );
};

Object.assign(window, {Solicitacoes, SolicitacoesOp, TIPOS_REQ:TIPOS});
