const { useState, useEffect, useRef, useMemo } = React;

/* ————— Tokens ————— */
const T = { ink:"#23262B", bg:"#F4F4F1", card:"#FFF", line:"#E4E3DE", accent:"#EE6C1E",
            ok:"#2E7D4F", warn:"#C43A2F", blue:"#2E6BD6", muted:"#7C8088" };
const CATS = { "Plâtrerie":"#8A8F98", "Peinture":"#2E6BD6", "Isolation":"#D9A421",
               "Outillage":"#4A4E55", "Consommables":"#7A5C3E" };
const UNITS = ["u","m²","ml","L","kg","sac","rouleau","carton","palette"];
const MVT = {
  entree:{label:"Entrée",desc:"Fournisseur → Dépôt",color:"#2E7D4F",sign:"+"},
  sortie:{label:"Sortie",desc:"Dépôt → Chantier",color:"#EE6C1E",sign:"−"},
  retour:{label:"Retour dépôt",desc:"Chantier → Dépôt",color:"#2E6BD6",sign:"+"},
  retour_fournisseur:{label:"Retour fournisseur",desc:"Dépôt → Fournisseur",color:"#7A5C3E",sign:"−"},
  perte:{label:"Perte / Casse",desc:"Consommé sur chantier",color:"#C43A2F",sign:"−"},
};
const STATUTS = { offre:"Offre", ouvert:"En cours", suspendu:"Suspendu", termine:"Terminé", archive:"Archivé" };
const estActif = (c) => c.actif !== false && !["termine","archive"].includes(c.statut);
const adresseComplete = (c) => [c.adresse, [c.npa, c.localite].filter(Boolean).join(" ")].filter(Boolean).join(", ");
const norm = (s) => (s||"").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"");
const n = (v) => Number(v||0);
const fmtDate = (iso) => { const d=new Date(iso);
  return d.toLocaleDateString("fr-CH",{day:"2-digit",month:"2-digit"})+" "+d.toLocaleTimeString("fr-CH",{hour:"2-digit",minute:"2-digit"}); };

/* ————— Client API ————— */
async function api(path, opts = {}) {
  const r = await fetch("/api" + path, {
    credentials: "include",
    headers: { "Content-Type": "application/json" },
    ...opts,
    body: opts.body ? JSON.stringify(opts.body) : undefined,
  });
  if (r.status === 401) {
    const j = await r.json().catch(() => ({}));
    location.href = (j.portail || "https://portail.amsh.ch") + "?redirect=" + encodeURIComponent(location.href);
    throw new Error("redirection portail");
  }
  const data = r.headers.get("content-type")?.includes("json") ? await r.json() : await r.text();
  if (!r.ok) throw new Error(data.detail || data.error || "Erreur serveur");
  return data;
}

/* ————— UI ————— */
const Btn = ({children,onClick,kind="primary",small,disabled,full}) => (
  <button onClick={onClick} disabled={disabled}
    className={`font-semibold rounded-lg active:opacity-70 disabled:opacity-40 ${full?"w-full ":""}${small?"px-3 py-1.5 text-sm":"px-4 py-2.5 text-[15px]"}`}
    style={kind==="primary"?{background:T.accent,color:"#fff"}
      :kind==="danger"?{background:"#fff",color:T.warn,border:`1.5px solid ${T.warn}`}
      :{background:"#fff",color:T.ink,border:`1.5px solid ${T.line}`}}>{children}</button>
);
const Field = ({label,children}) => (
  <label className="block mb-3">
    <span className="block text-[11px] font-bold uppercase tracking-wider mb-1" style={{color:T.muted}}>{label}</span>
    {children}
  </label>
);
const inputCls = "w-full rounded-lg px-3 py-2.5 text-[15px] bg-white";
const inputStyle = { border:`1.5px solid ${T.line}`, color:T.ink };
const Chip = ({cat}) => (
  <span className="text-[10px] font-bold uppercase tracking-wide px-2 py-0.5 rounded"
    style={{background:(CATS[cat]||"#888")+"22",color:CATS[cat]||"#888"}}>{cat}</span>
);
const Sheet = ({title,onClose,children}) => (
  <div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center"
    style={{background:"rgba(20,22,25,.45)"}} onClick={onClose}>
    <div className="w-full sm:max-w-md max-h-[90vh] overflow-y-auto rounded-t-2xl sm:rounded-2xl p-5"
      style={{background:T.bg}} onClick={(e)=>e.stopPropagation()}>
      <div className="flex items-center justify-between mb-4">
        <h2 className="disp text-2xl" style={{color:T.ink}}>{title}</h2>
        <button onClick={onClose} className="text-2xl leading-none px-2" style={{color:T.muted}}>×</button>
      </div>
      {children}
    </div>
  </div>
);

/* ————— Scanner ————— */
function Scanner({ onDetect, onClose, hint }) {
  const videoRef = useRef(null);
  const [err,setErr]=useState(""); const [manual,setManual]=useState(""); const [ready,setReady]=useState(false);
  useEffect(()=>{ let stream,timer,stopped=false;
    (async()=>{ try{
      stream = await navigator.mediaDevices.getUserMedia({video:{facingMode:"environment"}});
      if(stopped){stream.getTracks().forEach(t=>t.stop());return;}
      const v=videoRef.current; v.srcObject=stream; await v.play(); setReady(true);
      if("BarcodeDetector" in window){
        const det=new window.BarcodeDetector({formats:["qr_code","ean_13","ean_8","code_128","code_39","upc_a","upc_e","data_matrix"]});
        timer=setInterval(async()=>{ if(stopped||!videoRef.current)return;
          try{ const c=await det.detect(videoRef.current);
            if(c.length&&!stopped){stopped=true;clearInterval(timer);onDetect(c[0].rawValue);} }catch{} },300);
      } else setErr("Ce navigateur ne détecte pas les codes automatiquement (iPhone/Safari). Visez le code puis saisissez-le ci-dessous.");
    }catch{ setErr("Caméra inaccessible. Autorisez l'appareil photo, ou saisissez le code à la main."); } })();
    return ()=>{ stopped=true; clearInterval(timer); if(stream) stream.getTracks().forEach(t=>t.stop()); };
  },[]);
  return (
    <Sheet title="Scanner un code" onClose={onClose}>
      <div className="rounded-xl overflow-hidden mb-3 relative" style={{background:"#000",minHeight:220}}>
        <video ref={videoRef} playsInline muted className="w-full" style={{maxHeight:320,objectFit:"cover"}} />
        {ready&&!err&&<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
          <div style={{width:"70%",height:110,border:`3px solid ${T.accent}`,borderRadius:12,boxShadow:"0 0 0 999px rgba(0,0,0,.25)"}}/></div>}
      </div>
      {hint&&!err&&<p className="text-[13px] mb-3" style={{color:T.muted}}>{hint}</p>}
      {err&&<p className="text-[13px] mb-3 font-medium" style={{color:T.warn}}>{err}</p>}
      <div className="flex gap-2">
        <input className={inputCls} style={inputStyle} placeholder="Ou saisir le code…" value={manual} onChange={e=>setManual(e.target.value)} />
        <Btn onClick={()=>manual.trim()&&onDetect(manual.trim())} disabled={!manual.trim()}>OK</Btn>
      </div>
    </Sheet>
  );
}

/* ————— Sélecteur d'article ————— */
function ArticlePicker({articles,value,onChange,onScanRequest}) {
  const [open,setOpen]=useState(!value); const [q,setQ]=useState("");
  const sel = articles.find(a=>a.id===value);
  const filtered = useMemo(()=>{ const nq=norm(q);
    return articles.filter(a=>!nq||norm(a.designation).includes(nq)||(a.code_barres&&a.code_barres.includes(q.trim()))).slice(0,50);
  },[articles,q]);
  if(!open&&sel) return (
    <div className="flex items-center gap-2 rounded-lg p-3 bg-white" style={{border:`1.5px solid ${T.line}`}}>
      <div className="min-w-0 flex-1">
        <p className="font-semibold text-[14px] truncate">{sel.designation}</p>
        <p className="text-[11px]" style={{color:T.muted}}>{n(sel.qte_depot)} {sel.unite} au dépôt{sel.code_barres?` · ${sel.code_barres}`:""}</p>
      </div>
      <Btn small kind="secondary" onClick={()=>{setOpen(true);setQ("");}}>Changer</Btn>
    </div>
  );
  return (
    <div className="rounded-lg bg-white p-2" style={{border:`1.5px solid ${T.line}`}}>
      <div className="flex gap-2 mb-2">
        <input autoFocus className={inputCls} style={inputStyle} placeholder="Tapez pour chercher…" value={q} onChange={e=>setQ(e.target.value)} />
        <button onClick={onScanRequest} className="shrink-0 rounded-lg px-3 font-semibold text-[13px]" style={{background:T.ink,color:"#fff"}}>📷</button>
      </div>
      <div className="max-h-56 overflow-y-auto">
        {filtered.length===0&&<p className="text-[13px] p-2" style={{color:T.muted}}>Aucun article trouvé pour « {q} ».</p>}
        {filtered.map(a=>(
          <button key={a.id} onClick={()=>{onChange(a.id);setOpen(false);}}
            className="w-full text-left px-2 py-2 rounded-lg flex justify-between items-center gap-2"
            style={{background:a.id===value?T.accent+"14":"transparent"}}>
            <span className="min-w-0">
              <span className="block text-[14px] font-medium truncate">{a.designation}</span>
              <span className="block text-[11px]" style={{color:T.muted}}>{a.categorie}{a.code_barres?` · ${a.code_barres}`:""}</span>
            </span>
            <span className="shrink-0 text-[13px] font-semibold" style={{color:n(a.qte_depot)<n(a.seuil)?T.warn:T.ink}}>{n(a.qte_depot)} {a.unite}</span>
          </button>
        ))}
      </div>
    </div>
  );
}

/* ————— App ————— */
function App() {
  const [d,setD]=useState(null); const [err,setErr]=useState("");
  const [tab,setTab]=useState("stock"); const [sheet,setSheet]=useState(null);
  const [search,setSearch]=useState(""); const [catFilter,setCatFilter]=useState("Tous");
  const [toast,setToast]=useState("");

  const charger = async () => { try { setD(await api("/bootstrap")); } catch(e){ setErr(e.message); } };
  useEffect(()=>{ charger(); },[]);
  const ping = (m)=>{ setToast(m); setTimeout(()=>setToast(""),2400); };
  const peut = (p)=> d?.me?.droits?.includes(p);

  if(err) return <div className="min-h-screen flex items-center justify-center p-6 text-center">
    <p style={{color:T.warn}}>{err}</p></div>;
  if(!d) return <div className="min-h-screen flex items-center justify-center"><p style={{color:T.muted}}>Chargement du stock…</p></div>;

  const chantierNom = (id)=> d.chantiers.find(c=>c.id===id)?.nom || "";
  const surChantier = (a)=> Object.values(a.chantiers||{}).reduce((s,v)=>s+n(v),0);
  const alerts = d.articles.filter(a=>n(a.qte_depot)<n(a.seuil));
  const valeurDepot = d.articles.reduce((s,a)=>s+n(a.prix)*n(a.qte_depot),0);
  const filtered = d.articles.filter(a=>(catFilter==="Tous"||a.categorie===catFilter)
    && (norm(a.designation).includes(norm(search))||(a.code_barres&&a.code_barres.includes(search.trim()))));

  const handleScanned = async (code) => {
    const back = sheet?.back;
    if(back?.type==="article"){ setSheet({...back, scannedCode:code}); return; }
    const found = d.articles.find(a=>a.code_barres===code);
    if(back?.type==="mvt"){
      if(found){ setSheet({type:"mvt",payload:{...back.payload,article_id:found.id}}); ping(`Article trouvé : ${found.designation}`); }
      else if(peut("articles")){ setSheet({type:"article",payload:null,prefillCode:code,back}); ping("Code inconnu — créez l'article"); }
      else { setSheet(back); ping("Code inconnu"); }
    } else {
      if(found){ setSheet({type:"article",payload:found}); ping(`Trouvé : ${found.designation}`); }
      else if(peut("articles")){ setSheet({type:"article",payload:null,prefillCode:code}); ping("Code inconnu — nouvel article"); }
      else { setSheet(null); ping("Code inconnu"); }
    }
  };

  return (
    <div className="min-h-screen pb-24" style={{background:T.bg,color:T.ink}}>
      <header style={{background:T.ink}}>
        <div className="px-4 pt-4 pb-3 flex items-center justify-between">
          <div>
            <h1 className="disp text-3xl leading-none" style={{color:"#fff"}}>Dépôt<span style={{color:T.accent}}>·</span>Stock</h1>
            <p className="text-[11px] mt-0.5" style={{color:"#9BA0A8"}}>Plâtrerie · Peinture · Isolation</p>
          </div>
          <div className="flex items-center gap-2">
            <button onClick={()=>setSheet({type:"scan"})} className="rounded-lg px-3 py-2 text-[18px]" style={{background:"#33373D"}} aria-label="Scanner">📷</button>
            <a href="https://portail.amsh.ch" className="text-right rounded-lg px-3 py-1.5 block" style={{background:"#33373D",color:"#fff"}}>
              <span className="block text-[13px] font-semibold">{d.me.nom}</span>
              <span className="block text-[10px]" style={{color:"#9BA0A8"}}>{d.me.role_stock} · portail ↗</span>
            </a>
          </div>
        </div>
        <div className="hazard" />
      </header>

      {alerts.length>0 && tab!=="inventaire" && (
        <button onClick={()=>setTab("inventaire")} className="w-full text-left px-4 py-2 text-[13px] font-semibold" style={{background:"#FBEAE8",color:T.warn}}>
          ⚠ {alerts.length} article{alerts.length>1?"s":""} sous le seuil — voir l'inventaire
        </button>
      )}

      <main className="px-4 pt-4 max-w-2xl mx-auto">
        {tab==="stock" && <>
          <div className="flex gap-2 mb-3">
            <input className={inputCls} style={inputStyle} placeholder="Chercher (nom ou code)…" value={search} onChange={e=>setSearch(e.target.value)} />
            {peut("articles") && <Btn onClick={()=>setSheet({type:"article"})}>+ Article</Btn>}
          </div>
          <div className="flex gap-1.5 mb-4 overflow-x-auto pb-1">
            {["Tous",...Object.keys(CATS)].map(c=>(
              <button key={c} onClick={()=>setCatFilter(c)} className="shrink-0 text-[12px] font-semibold px-3 py-1.5 rounded-full"
                style={catFilter===c?{background:T.ink,color:"#fff"}:{background:"#fff",color:T.muted,border:`1px solid ${T.line}`}}>{c}</button>
            ))}
          </div>
          {filtered.length===0 && <p className="text-center py-10 text-[14px]" style={{color:T.muted}}>Aucun article.</p>}
          <div className="space-y-2">
            {filtered.map(a=>{ const sc=surChantier(a); const low=n(a.qte_depot)<n(a.seuil); return (
              <button key={a.id} onClick={()=>setSheet({type:"article",payload:a})} className="w-full text-left rounded-xl p-3.5"
                style={{background:T.card,border:`1px solid ${low?T.warn+"66":T.line}`}}>
                <div className="flex items-start justify-between gap-2">
                  <div className="min-w-0">
                    <p className="font-semibold text-[15px] truncate">{a.designation}</p>
                    <div className="mt-1 flex items-center gap-2">
                      <Chip cat={a.categorie}/>
                      {a.code_barres&&<span className="text-[10px]" style={{color:T.muted}}>▯ {a.code_barres}</span>}
                      {low&&<span className="text-[10px] font-bold" style={{color:T.warn}}>SOUS SEUIL</span>}
                    </div>
                  </div>
                  <div className="text-right shrink-0">
                    <p className="disp text-2xl leading-none" style={{color:low?T.warn:T.ink}}>{n(a.qte_depot)}<span className="text-sm ml-0.5" style={{color:T.muted}}>{a.unite}</span></p>
                    <p className="text-[11px]" style={{color:T.muted}}>{sc>0?`+ ${sc} sur chantier`:"dépôt"}</p>
                  </div>
                </div>
              </button> );})}
          </div>
        </>}

        {tab==="flux" && <>
          <div className="grid grid-cols-2 gap-2 mb-4">
            {Object.keys(MVT).filter(t=>peut("mouvement")&&(t!=="retour_fournisseur"||peut("retour_fournisseur"))
              &&(d.me.role_stock!=="ouvrier"||["sortie","retour","perte"].includes(t))).map(t=>(
              <button key={t} onClick={()=>setSheet({type:"mvt",payload:{type:t}})} className="rounded-xl p-3 text-left" style={{background:T.card,border:`1px solid ${T.line}`}}>
                <span className="disp text-lg block" style={{color:MVT[t].color}}>{MVT[t].label}</span>
                <span className="text-[11px]" style={{color:T.muted}}>{MVT[t].desc}</span>
              </button>
            ))}
            {peut("mouvement") && <button onClick={()=>setSheet({type:"scan",back:{type:"mvt",payload:{type:"sortie"}}})} className="rounded-xl p-3 text-left" style={{background:T.ink}}>
              <span className="disp text-lg block" style={{color:"#fff"}}>📷 Scanner</span>
              <span className="text-[11px]" style={{color:"#9BA0A8"}}>Identifier l'article</span>
            </button>}
          </div>
          <h3 className="disp text-xl mb-2">Derniers mouvements</h3>
          {d.mouvements.length===0 && <p className="text-[14px] py-6 text-center" style={{color:T.muted}}>Aucun mouvement enregistré.</p>}
          <div className="space-y-2">
            {d.mouvements.map(m=>{ const cfg=MVT[m.type]||MVT.sortie; return (
              <div key={m.id} className="rounded-xl p-3" style={{background:T.card,border:`1px solid ${T.line}`}}>
                <div className="flex justify-between items-baseline">
                  <p className="text-[14px] font-semibold">{m.designation}</p>
                  <span className="disp text-lg" style={{color:cfg.color}}>{cfg.sign}{n(m.qte)}</span>
                </div>
                <p className="text-[12px] mt-0.5" style={{color:T.muted}}>
                  <span className="font-semibold" style={{color:cfg.color}}>{cfg.label}</span>
                  {m.chantier_nom?` · ${m.chantier_nom}`:""}{m.fournisseur?` · ${m.fournisseur}`:""} · {m.auteur_libelle} · {fmtDate(m.cree_le)}
                </p>
              </div> );})}
          </div>
        </>}

        {tab==="chantiers" && <>
          <div className="flex justify-between items-center mb-3">
            <h3 className="disp text-xl">Chantiers & bricoles</h3>
            {peut("chantiers") && <Btn small onClick={()=>setSheet({type:"chantier"})}>+ Chantier</Btn>}
          </div>
          <div className="space-y-2">
            {d.chantiers.map(c=>{
              const rows=d.articles.filter(a=>n(a.chantiers?.[c.id])>0);
              const macs=d.machines.filter(m=>m.chantier_id===c.id);
              const done=!estActif(c);
              return (
                <button key={c.id} onClick={()=>peut("chantiers")&&setSheet({type:"chantier",payload:c})}
                  className="w-full text-left rounded-xl p-3.5" style={{background:T.card,border:`1px solid ${T.line}`,opacity:done?.6:1}}>
                  <div className="flex justify-between items-start gap-2">
                    <div className="min-w-0">
                      <p className="font-semibold text-[15px] truncate">{c.nature==="Bricole"?"🔧 ":"🏗 "}{c.nom}</p>
                      <p className="text-[12px] mt-0.5" style={{color:T.muted}}>
                        {c.code&&<b>{c.code}</b>}{adresseComplete(c)?` · ${adresseComplete(c)}`:""}{c.technicien?` · ${c.technicien}`:""}</p>
                      {(rows.length>0||macs.length>0)&&<p className="text-[11px] mt-1" style={{color:T.accent}}>
                        {rows.length>0&&`${rows.length} article${rows.length>1?"s":""} sur place`}
                        {rows.length>0&&macs.length>0&&" · "}
                        {macs.length>0&&`${macs.length} machine${macs.length>1?"s":""}`}</p>}
                    </div>
                    <span className="text-[10px] font-bold uppercase tracking-wide px-2 py-1 rounded shrink-0"
                      style={{background:(done?T.muted:c.nature==="Bricole"?T.blue:T.ok)+"1c",color:done?T.muted:c.nature==="Bricole"?T.blue:T.ok}}>
                      {done?STATUTS[c.statut]||c.statut:c.nature}</span>
                  </div>
                </button> );})}
          </div>
        </>}

        {tab==="machines" && <>
          <div className="flex justify-between items-center mb-3">
            <h3 className="disp text-xl">Parc machines</h3>
            {peut("machines") && <Btn small onClick={()=>setSheet({type:"machine"})}>+ Machine</Btn>}
          </div>
          <div className="space-y-2">
            {d.machines.map(m=>{ const col=m.statut==="Dépôt"?T.ok:m.statut==="Sur chantier"?T.accent:T.warn; return (
              <button key={m.id} onClick={()=>peut("machines")&&setSheet({type:"machine",payload:m})}
                className="w-full text-left rounded-xl p-3.5" style={{background:T.card,border:`1px solid ${T.line}`}}>
                <div className="flex justify-between items-start gap-2">
                  <div>
                    <p className="font-semibold text-[15px]">{m.nom}</p>
                    <p className="text-[12px] mt-0.5" style={{color:T.muted}}>
                      {[m.chantier_nom||m.lieu_libre, m.detenteur_lib, m.note].filter(Boolean).join(" · ")||"Disponible"}</p>
                  </div>
                  <span className="text-[10px] font-bold uppercase tracking-wide px-2 py-1 rounded shrink-0"
                    style={{background:col+"1c",color:col}}>{m.statut}</span>
                </div>
              </button> );})}
          </div>
        </>}

        {tab==="inventaire" && <>
          <div className="grid grid-cols-2 gap-2 mb-4">
            <div className="rounded-xl p-3.5" style={{background:T.card,border:`1px solid ${T.line}`}}>
              <p className="disp text-3xl leading-none">{d.articles.length}</p>
              <p className="text-[11px] mt-1" style={{color:T.muted}}>références en stock</p>
            </div>
            <div className="rounded-xl p-3.5" style={{background:T.card,border:`1px solid ${T.line}`}}>
              <p className="disp text-3xl leading-none">{valeurDepot.toLocaleString("fr-CH",{maximumFractionDigits:0})} CHF</p>
              <p className="text-[11px] mt-1" style={{color:T.muted}}>valeur du dépôt</p>
            </div>
          </div>
          {alerts.length>0 && (
            <div className="rounded-xl p-3.5 mb-4" style={{background:"#FBEAE8",border:`1px solid ${T.warn}44`}}>
              <p className="disp text-lg mb-1" style={{color:T.warn}}>À recommander</p>
              {alerts.map(a=><p key={a.id} className="text-[13px] py-0.5">{a.designation} — <b>{n(a.qte_depot)} {a.unite}</b> (seuil {n(a.seuil)})</p>)}
            </div>
          )}
          {Object.keys(CATS).map(c=>{ const arts=d.articles.filter(a=>a.categorie===c); if(!arts.length) return null; return (
            <div key={c} className="mb-4">
              <div className="mb-1.5"><Chip cat={c}/></div>
              <div className="rounded-xl overflow-hidden" style={{background:T.card,border:`1px solid ${T.line}`}}>
                {arts.map((a,i)=>{ const sc=surChantier(a); return (
                  <div key={a.id} className="flex justify-between px-3.5 py-2.5 text-[13px]" style={{borderTop:i?`1px solid ${T.line}`:"none"}}>
                    <span className="truncate pr-2">{a.designation}</span>
                    <span className="shrink-0 font-semibold">{n(a.qte_depot)}{sc>0&&<span style={{color:T.muted}}> +{sc} cht.</span>} {a.unite}</span>
                  </div> );})}
              </div>
            </div> );})}
          {d.chantiers.map(c=>{
            const rows=d.articles.filter(a=>n(a.chantiers?.[c.id])>0);
            const macs=d.machines.filter(m=>m.chantier_id===c.id);
            if(!rows.length&&!macs.length) return null;
            return (
              <div key={c.id} className="rounded-xl p-3.5 mb-2" style={{background:T.card,border:`1px solid ${T.line}`}}>
                <p className="font-bold text-[14px]">{c.nature==="Bricole"?"🔧":"🏗"} {c.nom} <span className="font-normal" style={{color:T.muted}}>· {c.code||""}</span></p>
                {adresseComplete(c)&&<p className="text-[11px] mb-1" style={{color:T.muted}}>{adresseComplete(c)}</p>}
                {rows.map(a=><p key={a.id} className="text-[13px] py-0.5" style={{color:T.muted}}>{a.designation} — {n(a.chantiers[c.id])} {a.unite}</p>)}
                {macs.map(m=><p key={m.id} className="text-[13px] py-0.5" style={{color:T.accent}}>⚙ {m.nom}{m.detenteur_lib?` (${m.detenteur_lib})`:""}</p>)}
              </div> );})}
          {peut("export") && <a href="/api/inventaire.csv" className="block w-full text-center font-semibold rounded-lg px-4 py-2.5 text-[15px]" style={{background:T.accent,color:"#fff"}}>⬇ Exporter l'inventaire (CSV)</a>}
          <p className="text-[11px] mt-2 mb-6" style={{color:T.muted}}>Export à date : stock dépôt et chantiers, machines, répertoire chantiers.</p>
        </>}
      </main>

      {toast && <div className="fixed bottom-24 left-1/2 -translate-x-1/2 px-4 py-2 rounded-full text-[13px] font-semibold z-[60] whitespace-nowrap" style={{background:T.ink,color:"#fff"}}>{toast}</div>}

      <nav className="fixed bottom-0 left-0 right-0 z-40" style={{background:T.ink}}>
        <div className="hazard" style={{height:4}} />
        <div className="flex max-w-2xl mx-auto">
          {[["stock","▦","Stock"],["flux","⇄","Flux"],["chantiers","🏗","Chantiers"],["machines","⚙","Machines"],["inventaire","☰","Invent."]].map(([id,ic,l])=>(
            <button key={id} onClick={()=>setTab(id)} className="flex-1 py-2.5 text-center" style={{color:tab===id?T.accent:"#9BA0A8"}}>
              <span className="block text-base leading-none">{ic}</span>
              <span className="disp text-[12px]">{l}</span>
            </button>
          ))}
        </div>
      </nav>

      {sheet?.type==="scan" && <Scanner hint="Visez le code-barres ou QR de l'article." onClose={()=>setSheet(sheet.back||null)} onDetect={handleScanned} />}

      {sheet?.type==="article" && (
        <ArticleSheet key={sheet.payload?.id||"new"} article={sheet.payload} prefillCode={sheet.prefillCode}
          scannedCode={sheet.scannedCode} draft={sheet.draft} peut={peut}
          onScanRequest={(draft)=>setSheet({type:"scan",back:{type:"article",payload:sheet.payload,draft}})}
          onClose={()=>setSheet(null)}
          onSave={async(a)=>{ try{
            const body={designation:a.designation,categorie:a.categorie,unite:a.unite,code_barres:a.code_barres,
              seuil:a.seuil,prix:a.prix,qte_depot:a.qte_depot};
            const saved = sheet.payload ? await api(`/articles/${sheet.payload.id}`,{method:"PUT",body})
                                        : await api("/articles",{method:"POST",body});
            await charger(); ping("Article enregistré ✓");
            if(!sheet.payload && sheet.back?.type==="mvt") setSheet({type:"mvt",payload:{...sheet.back.payload,article_id:saved.id}});
            else setSheet(null);
          }catch(e){ ping("⚠ "+e.message); } }}
          onDelete={sheet.payload?async()=>{ if(!confirm("Archiver cet article ?"))return;
            await api(`/articles/${sheet.payload.id}`,{method:"DELETE"}); await charger(); setSheet(null); ping("Article archivé"); }:null}
          onMove={sheet.payload&&peut("mouvement")?(t)=>setSheet({type:"mvt",payload:{type:t,article_id:sheet.payload.id}}):null}
        />
      )}

      {sheet?.type==="mvt" && (
        <MvtSheet init={sheet.payload} articles={d.articles} chantiers={d.chantiers} me={d.me}
          onScanRequest={(p)=>setSheet({type:"scan",back:{type:"mvt",payload:p}})}
          onQuickChantier={(p)=>setSheet({type:"chantier",back:{type:"mvt",payload:p}})}
          onClose={()=>setSheet(null)}
          onSave={async(m)=>{ try{ await api("/mouvements",{method:"POST",body:m}); await charger(); setSheet(null); ping("Mouvement enregistré ✓"); }
            catch(e){ ping("⚠ "+e.message); } }}
        />
      )}

      {sheet?.type==="chantier" && (
        <ChantierSheet chantier={sheet.payload} onClose={()=>setSheet(sheet.back?{type:"mvt",payload:sheet.back.payload}:null)}
          onSave={async(c)=>{ try{
            const saved = sheet.payload ? await api(`/chantiers/${sheet.payload.id}`,{method:"PUT",body:c})
                                        : await api("/chantiers",{method:"POST",body:c});
            await charger(); ping("Chantier enregistré ✓");
            if(sheet.back?.type==="mvt") setSheet({type:"mvt",payload:{...sheet.back.payload,chantier_id:saved.id}});
            else setSheet(null);
          }catch(e){ ping("⚠ "+e.message); } }}
        />
      )}

      {sheet?.type==="machine" && (
        <MachineSheet machine={sheet.payload} chantiers={d.chantiers} onClose={()=>setSheet(null)}
          onSave={async(m)=>{ try{
            sheet.payload ? await api(`/machines/${sheet.payload.id}`,{method:"PUT",body:m})
                          : await api("/machines",{method:"POST",body:m});
            await charger(); setSheet(null); ping("Machine enregistrée ✓");
          }catch(e){ ping("⚠ "+e.message); } }}
          onDelete={sheet.payload?async()=>{ if(!confirm("Supprimer cette machine ?"))return;
            await api(`/machines/${sheet.payload.id}`,{method:"DELETE"}); await charger(); setSheet(null); }:null}
        />
      )}
    </div>
  );
}

/* ————— Feuilles ————— */
function ArticleSheet({article,prefillCode,scannedCode,draft,peut,onScanRequest,onClose,onSave,onDelete,onMove}) {
  const base = draft || (article ? {
    designation:article.designation, categorie:article.categorie, unite:article.unite,
    code_barres:article.code_barres||"", seuil:n(article.seuil), prix:article.prix??"", qte_depot:n(article.qte_depot)
  } : { designation:"",categorie:"Plâtrerie",unite:"u",code_barres:prefillCode||"",seuil:0,prix:"",qte_depot:0 });
  const [a,setA]=useState(scannedCode?{...base,code_barres:scannedCode}:base);
  const set=(k,v)=>setA({...a,[k]:v});
  const lecture = !peut("articles");
  return (
    <Sheet title={article?"Article":"Nouvel article"} onClose={onClose}>
      {onMove && (
        <div className="grid grid-cols-2 gap-2 mb-4">
          <Btn small kind="secondary" onClick={()=>onMove("entree")}>Entrée</Btn>
          <Btn small kind="secondary" onClick={()=>onMove("sortie")}>Sortie</Btn>
          <Btn small kind="secondary" onClick={()=>onMove("retour")}>Retour dépôt</Btn>
          {peut("retour_fournisseur")&&<Btn small kind="secondary" onClick={()=>onMove("retour_fournisseur")}>Retour fourn.</Btn>}
        </div>
      )}
      {lecture ? (
        <p className="text-[13px] mb-2" style={{color:T.muted}}>Consultation seule — la fiche article est gérée par le magasinier.</p>
      ) : <>
        <Field label="Désignation"><input className={inputCls} style={inputStyle} value={a.designation} onChange={e=>set("designation",e.target.value)} placeholder="Ex. Plaque BA13 250×120" /></Field>
        <Field label="Code-barres / QR">
          <div className="flex gap-2">
            <input className={inputCls} style={inputStyle} value={a.code_barres} onChange={e=>set("code_barres",e.target.value)} placeholder="Scannez ou saisissez" />
            <button onClick={()=>onScanRequest(a)} className="shrink-0 rounded-lg px-3 font-semibold text-[13px]" style={{background:T.ink,color:"#fff"}}>📷</button>
          </div>
        </Field>
        <div className="grid grid-cols-2 gap-3">
          <Field label="Catégorie">
            <select className={inputCls} style={inputStyle} value={a.categorie} onChange={e=>set("categorie",e.target.value)}>
              {Object.keys(CATS).map(c=><option key={c}>{c}</option>)}</select>
          </Field>
          <Field label="Unité">
            <select className={inputCls} style={inputStyle} value={a.unite} onChange={e=>set("unite",e.target.value)}>
              {UNITS.map(u=><option key={u}>{u}</option>)}</select>
          </Field>
          {!article && <Field label="Quantité initiale au dépôt">
            <input type="number" className={inputCls} style={inputStyle} value={a.qte_depot} onChange={e=>set("qte_depot",Number(e.target.value))} /></Field>}
          <Field label="Seuil d'alerte"><input type="number" className={inputCls} style={inputStyle} value={a.seuil} onChange={e=>set("seuil",Number(e.target.value))} /></Field>
        </div>
        <Field label="Prix unitaire CHF (optionnel)">
          <input type="number" step="0.01" className={inputCls} style={inputStyle} value={a.prix} onChange={e=>set("prix",e.target.value===""?"":Number(e.target.value))} /></Field>
        {article && <p className="text-[12px] mb-3" style={{color:T.muted}}>Stock actuel : {n(article.qte_depot)} {article.unite} au dépôt. Il se modifie uniquement par un mouvement.</p>}
        <div className="flex gap-2 mt-2">
          <Btn onClick={()=>a.designation.trim()&&onSave(a)} disabled={!a.designation.trim()}>Enregistrer</Btn>
          {onDelete&&<Btn kind="danger" onClick={onDelete}>Archiver</Btn>}
        </div>
      </>}
    </Sheet>
  );
}

function MvtSheet({init,articles,chantiers,me,onScanRequest,onQuickChantier,onClose,onSave}) {
  const [m,setM]=useState({ type:init.type||"sortie", article_id:init.article_id||"", qte:init.qte||"",
    chantier_id:init.chantier_id||"", fournisseur:init.fournisseur||"", note:init.note||"" });
  const art = articles.find(a=>a.id===m.article_id);
  const needCh = ["sortie","retour","perte"].includes(m.type);
  const needFo = ["entree","retour_fournisseur"].includes(m.type);
  const enCours = chantiers.filter(estActif);
  const chOptions = m.type==="sortie" ? enCours : enCours.filter(c=>n(art?.chantiers?.[c.id])>0);
  const qte = Number(m.qte);
  const maxOut = ["sortie","retour_fournisseur"].includes(m.type) ? n(art?.qte_depot)
    : needCh && m.type!=="sortie" ? n(art?.chantiers?.[m.chantier_id]) : Infinity;
  const invalid = !art || !(qte>0) || (needCh && !m.chantier_id) || qte>maxOut;
  const types = Object.keys(MVT).filter(t=>(t!=="retour_fournisseur"||me.droits.includes("retour_fournisseur"))
    && (me.role_stock!=="ouvrier"||["sortie","retour","perte"].includes(t)));
  return (
    <Sheet title={MVT[m.type].label} onClose={onClose}>
      <Field label="Type de mouvement">
        <select className={inputCls} style={inputStyle} value={m.type} onChange={e=>setM({...m,type:e.target.value,chantier_id:""})}>
          {types.map(k=><option key={k} value={k}>{MVT[k].label} ({MVT[k].desc})</option>)}</select>
      </Field>
      <Field label="Article">
        <ArticlePicker articles={articles} value={m.article_id} onChange={id=>setM({...m,article_id:id})} onScanRequest={()=>onScanRequest(m)} />
      </Field>
      <Field label={`Quantité${art?` (${art.unite})`:""}`}>
        <input type="number" inputMode="decimal" className={inputCls} style={inputStyle} value={m.qte} onChange={e=>setM({...m,qte:e.target.value})} placeholder="0" />
      </Field>
      {needFo && <Field label={m.type==="retour_fournisseur"?"Fournisseur (retour à)":"Fournisseur"}>
        <input className={inputCls} style={inputStyle} value={m.fournisseur} onChange={e=>setM({...m,fournisseur:e.target.value})} placeholder="Ex. Sabag, Gétaz…" /></Field>}
      {needCh && <Field label="Chantier / Bricole">
        <select className={inputCls} style={inputStyle} value={m.chantier_id}
          onChange={e=>e.target.value==="__new"?onQuickChantier(m):setM({...m,chantier_id:e.target.value})}>
          <option value="">— Choisir —</option>
          {chOptions.map(c=><option key={c.id} value={c.id}>{c.code?c.code+" · ":""}{c.nom}
            {m.type!=="sortie"&&art?` (${n(art.chantiers[c.id])} ${art.unite} sur place)`:""}</option>)}
          {m.type==="sortie"&&<option value="__new">＋ Nouveau chantier…</option>}
        </select></Field>}
      <Field label="Note (optionnel)"><input className={inputCls} style={inputStyle} value={m.note} onChange={e=>setM({...m,note:e.target.value})} /></Field>
      {qte>maxOut && maxOut!==Infinity && <p className="text-[13px] mb-3 font-semibold" style={{color:T.warn}}>Quantité supérieure au disponible ({maxOut} {art?.unite}).</p>}
      <Btn full onClick={()=>onSave({...m,qte})} disabled={invalid}>Valider le mouvement</Btn>
    </Sheet>
  );
}

function ChantierSheet({chantier,onClose,onSave}) {
  const [c,setC]=useState(chantier||{nom:"",code:"",adresse:"",npa:"",localite:"",nature:"Chantier",technicien:"",statut:"ouvert"});
  const set=(k,v)=>setC({...c,[k]:v});
  return (
    <Sheet title={chantier?"Chantier":"Nouveau chantier"} onClose={onClose}>
      <div className="grid grid-cols-2 gap-3">
        <Field label="N° de chantier"><input className={inputCls} style={inputStyle} value={c.code||""} onChange={e=>set("code",e.target.value)} placeholder="Ex. 2026-021" /></Field>
        <Field label="Nature"><select className={inputCls} style={inputStyle} value={c.nature||"Chantier"} onChange={e=>set("nature",e.target.value)}>
          <option>Chantier</option><option>Bricole</option></select></Field>
      </div>
      <Field label="Nom"><input className={inputCls} style={inputStyle} value={c.nom} onChange={e=>set("nom",e.target.value)} placeholder="Ex. Villa Perret" /></Field>
      <Field label="Adresse"><input className={inputCls} style={inputStyle} value={c.adresse||""} onChange={e=>set("adresse",e.target.value)} placeholder="Rue et numéro" /></Field>
      <div className="grid grid-cols-3 gap-3">
        <Field label="NPA"><input className={inputCls} style={inputStyle} value={c.npa||""} onChange={e=>set("npa",e.target.value)} placeholder="1110" /></Field>
        <div className="col-span-2">
          <Field label="Localité"><input className={inputCls} style={inputStyle} value={c.localite||""} onChange={e=>set("localite",e.target.value)} placeholder="Morges" /></Field>
        </div>
      </div>
      {c.nature==="Bricole" && <Field label="Technicien en charge">
        <input className={inputCls} style={inputStyle} value={c.technicien||""} onChange={e=>set("technicien",e.target.value)} /></Field>}
      <Field label="Statut"><select className={inputCls} style={inputStyle} value={c.statut||"ouvert"} onChange={e=>set("statut",e.target.value)}>
        {Object.entries(STATUTS).map(([k,l])=><option key={k} value={k}>{l}</option>)}</select></Field>
      <p className="text-[12px] mb-3" style={{color:T.muted}}>Ce chantier est partagé avec CHANTIER·H et les autres applications.</p>
      <Btn onClick={()=>c.nom.trim()&&onSave(c)} disabled={!c.nom.trim()}>Enregistrer</Btn>
    </Sheet>
  );
}

function MachineSheet({machine,chantiers,onClose,onSave,onDelete}) {
  const [m,setM]=useState(machine||{nom:"",numero_serie:"",code_barres:"",statut:"Dépôt",chantier_id:null,lieu_libre:"",detenteur_lib:"",note:""});
  const set=(k,v)=>setM({...m,[k]:v});
  return (
    <Sheet title={machine?"Machine":"Nouvelle machine"} onClose={onClose}>
      <Field label="Désignation"><input className={inputCls} style={inputStyle} value={m.nom} onChange={e=>set("nom",e.target.value)} placeholder="Ex. Airless Graco 390" /></Field>
      <div className="grid grid-cols-2 gap-3">
        <Field label="N° de série"><input className={inputCls} style={inputStyle} value={m.numero_serie||""} onChange={e=>set("numero_serie",e.target.value)} /></Field>
        <Field label="Statut"><select className={inputCls} style={inputStyle} value={m.statut} onChange={e=>set("statut",e.target.value)}>
          <option>Dépôt</option><option>Sur chantier</option><option>En réparation</option></select></Field>
      </div>
      {m.statut==="Sur chantier" && <>
        <Field label="Chantier"><select className={inputCls} style={inputStyle} value={m.chantier_id||""} onChange={e=>set("chantier_id",e.target.value||null)}>
          <option value="">— Choisir —</option>
          {chantiers.filter(estActif).map(c=><option key={c.id} value={c.id}>{c.code?c.code+" · ":""}{c.nom}</option>)}
        </select></Field>
        <Field label="Confiée à"><input className={inputCls} style={inputStyle} value={m.detenteur_lib||""} onChange={e=>set("detenteur_lib",e.target.value)} placeholder="Ex. Karim (technicien)" /></Field>
      </>}
      {m.statut==="En réparation" && <Field label="Lieu (atelier / SAV)">
        <input className={inputCls} style={inputStyle} value={m.lieu_libre||""} onChange={e=>set("lieu_libre",e.target.value)} /></Field>}
      <Field label="Note"><input className={inputCls} style={inputStyle} value={m.note||""} onChange={e=>set("note",e.target.value)} placeholder="État, entretien, accessoires…" /></Field>
      <div className="flex gap-2 mt-2">
        <Btn onClick={()=>m.nom.trim()&&onSave({...m,chantier_id:m.statut==="Sur chantier"?m.chantier_id:null})} disabled={!m.nom.trim()}>Enregistrer</Btn>
        {onDelete&&<Btn kind="danger" onClick={onDelete}>Supprimer</Btn>}
      </div>
    </Sheet>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
