/* ==========================================================================
 * ADMIN NEXT — page-gamesort.jsx  (เรียงลำดับเกมหน้าเว็บ — ลากสลับ เมาส์+มือถือ)
 * บนสุด = ลูกค้าเห็นก่อน  ·  เรียงค่าย แล้วเข้าไปเรียงเกมในค่าย
 * ========================================================================== */

/* ตัวลาก pointer-based ( port จาก gmMakeDraggable ต้นฉบับ)
 * container = กล่องรายการ, selector = แต่ละใบ, onCommit(order[]) = ตอนปล่อย
 * กดค้าง 130ms หรือลากเกิน 6px ถึงเริ่ม จะได้ไม่กินการกดปุ่ม */
function gsMakeDraggable(grid, selector, dataAttr, onCommit) {
  if (!grid) return () => {};
  let src=null, ghost=null, dx=0, dy=0, sx=0, sy=0, armed=false, dragging=false, holdTimer=null, lastOver=null;
  const renumber=()=>{ grid.querySelectorAll(".gs-pos").forEach((el,i)=>{ el.textContent=i+1; }); };
  const begin=(el,x,y)=>{ dragging=true; src=el; const r=el.getBoundingClientRect(); dx=x-r.left; dy=y-r.top;
    ghost=el.cloneNode(true); Object.assign(ghost.style,{position:"fixed",zIndex:9999,width:r.width+"px",left:r.left+"px",top:r.top+"px",pointerEvents:"none",opacity:"0.92",transform:"scale(1.03)",boxShadow:"0 16px 40px rgba(0,0,0,.35)",margin:0});
    document.body.appendChild(ghost); el.style.opacity="0.35"; document.body.style.userSelect="none"; };
  const moveGhost=(x,y)=>{ if(ghost){ ghost.style.left=(x-dx)+"px"; ghost.style.top=(y-dy)+"px"; } };
  const under=(x,y)=>{ if(ghost)ghost.style.display="none"; const el=document.elementFromPoint(x,y); if(ghost)ghost.style.display=""; const c=el&&el.closest?el.closest(selector):null; return (c&&c!==src&&grid.contains(c))?c:null; };
  const finish=(commit)=>{ clearTimeout(holdTimer); if(ghost){ghost.remove();ghost=null;} if(src)src.style.opacity=""; grid.querySelectorAll(selector).forEach(el=>el.style.outline=""); document.body.style.userSelect="";
    const was=dragging; armed=false; dragging=false; const s=src; src=null; lastOver=null;
    if(was&&commit&&typeof onCommit==="function"){ const order=[...grid.querySelectorAll(selector)].map(el=>el.dataset[dataAttr]); onCommit(order); } };
  const onMove=(e)=>{ if(!armed&&!dragging)return; const x=e.clientX,y=e.clientY;
    if(!dragging){ if(Math.abs(x-sx)<6&&Math.abs(y-sy)<6)return; clearTimeout(holdTimer); begin(src,x,y); }
    e.preventDefault(); moveGhost(x,y); const over=under(x,y);
    if(over!==lastOver){ if(lastOver)lastOver.style.outline=""; if(over)over.style.outline="2px solid var(--accent)"; lastOver=over; }
    if(over){ const all=[...grid.querySelectorAll(selector)]; if(all.indexOf(src)<all.indexOf(over))over.after(src); else over.before(src); renumber(); } };
  const onUp=()=>finish(true), onCancel=()=>finish(false);
  const downs=[];
  grid.querySelectorAll(selector).forEach(el=>{ const h=(e)=>{ if(e.pointerType==="mouse"&&e.button!==0)return; if(e.target.closest("button")||e.target.closest("a"))return; src=el; sx=e.clientX; sy=e.clientY; armed=true; holdTimer=setTimeout(()=>{ if(armed&&!dragging)begin(el,sx,sy); },130); };
    el.addEventListener("pointerdown",h); downs.push([el,h]); });
  document.addEventListener("pointermove",onMove,{passive:false}); document.addEventListener("pointerup",onUp); document.addEventListener("pointercancel",onCancel);
  return ()=>{ downs.forEach(([el,h])=>el.removeEventListener("pointerdown",h)); document.removeEventListener("pointermove",onMove,{passive:false}); document.removeEventListener("pointerup",onUp); document.removeEventListener("pointercancel",onCancel); };
}

function GameSortPage({ toast }) {
  const [cats,setCats]=useState([]),[cat,setCat]=useState("");
  const [prov,setProv]=useState("");
  const [rows,setRows]=useState([]),[total,setTotal]=useState(0),[limit,setLimit]=useState(120);
  const [provs,setProvs]=useState([]);
  const [loading,setLoading]=useState(true),[err,setErr]=useState(null);
  const [dirty,setDirty]=useState(false),[provDirty,setProvDirty]=useState(false),[busy,setBusy]=useState(false);
  const gridRef=useRef(null);
  const load=useCallback(async(category,provider,lim)=>{ setLoading(true); setErr(null);
    try{ const qs=new URLSearchParams({limit:String(lim)}); if(category)qs.set("category",category); if(provider)qs.set("provider",provider);
      const [st,d]=await Promise.all([api("/games/stats").catch(()=>({categories:[]})), api("/games/ordered?"+qs.toString())]);
      setCats(((st.categories||[]).filter(c=>c.category&&Number(c.enabled||0)>0)).sort((a,b)=>Number(b.enabled||0)-Number(a.enabled||0)));
      setRows(d.rows||[]); setTotal(Number(d.total||0)); setProvs(d.providers||[]); setDirty(false); setProvDirty(false);
    }catch(e){ if(e.status===401||e.status===403){window.__nxAuthFail&&window.__nxAuthFail();} setErr(e.status===403?"ไม่มีสิทธิ์เรียงลำดับเกม":(e.message||"โหลดไม่สำเร็จ")); }
    finally{ setLoading(false); } },[]);
  useEffect(()=>{ load(cat,prov,limit); },[cat,prov,limit,load]);

  const confirmLeave=()=> (!dirty&&!provDirty) || window.confirm("ยังไม่ได้บันทึกลำดับ ถ้าเปลี่ยนตอนนี้ลำดับที่ลากไว้จะหาย จะไปต่อไหม");
  const goCat=(c)=>{ if(!confirmLeave())return; setLimit(120); setProv(""); setCat(c||""); };
  const goProv=(p)=>{ if(!confirmLeave())return; setLimit(120); setProv(p||""); };

  // attach drag after each render
  useEffect(()=>{
    if(loading||err) return;
    const grid=gridRef.current; if(!grid) return;
    let cleanup;
    if(prov){ cleanup=gsMakeDraggable(grid,".gs-gamecard","gameId",(order)=>{ const by=new Map(rows.map(g=>[String(g.id),g])); const next=order.map(id=>by.get(String(id))).filter(Boolean); if(next.length===rows.length && next.some((g,i)=>g!==rows[i])){ setRows(next); setDirty(true); } }); }
    else { cleanup=gsMakeDraggable(grid,".gs-provcard","provider",(order)=>{ const by=new Map(provs.map(p=>[String(p.provider),p])); const next=order.map(pv=>by.get(String(pv))).filter(Boolean); if(next.length===provs.length && next.some((p,i)=>p!==provs[i])){ setProvs(next); setProvDirty(true); } }); }
    return cleanup;
  },[loading,err,prov,rows,provs]);

  const turnOff=async(g)=>{ try{ await api("/games/"+g.id+"/toggle",{method:"POST",body:{enabled:false}}); setRows(rs=>rs.filter(x=>String(x.id)!==String(g.id))); toast("ปิดเกมแล้ว"); }catch(e){ toast(e.message||"ไม่สำเร็จ"); } };
  const saveGames=async()=>{ const items=rows.map((g,i)=>({id:g.id,sortOrder:i+1})); if(!items.length)return; setBusy(true);
    try{ const r=await api("/games/bulk-sort-order",{method:"POST",body:{items}}); toast("บันทึกลำดับแล้ว "+((r&&r.updated)||items.length)+" เกม"); load(cat,prov,limit); }catch(e){ toast(e.message||"บันทึกไม่สำเร็จ"); }finally{ setBusy(false); } };
  const saveProvs=async()=>{ const items=provs.map((p,i)=>({provider:p.provider,sortOrder:i+1})).filter(x=>x.provider); if(!items.length)return; setBusy(true);
    try{ const r=await api("/providers/bulk-order",{method:"POST",body:{items}}); toast("บันทึกลำดับค่ายแล้ว "+((r&&r.updated)||items.length)+" รายการ"); load(cat,prov,limit); }catch(e){ toast(e.message||"บันทึกไม่สำเร็จ"); }finally{ setBusy(false); } };

  if(err) return <HistErr msg={err} onRetry={()=>load(cat,prov,limit)}/>;

  const rail=(<nav className="flex shrink-0 flex-row gap-1 overflow-x-auto lg:w-44 lg:flex-col">
    <button onClick={()=>goCat("")} className={"flex shrink-0 items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors "+(cat?"text-fg-2 hover:bg-sunken":"bg-accent-soft text-accent")}><Ic d={I.grid} size={15}/> ทั้งหมด</button>
    {cats.map(c=>(<button key={c.category} onClick={()=>goCat(c.category)} className={"flex shrink-0 items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors "+(cat===c.category?"bg-accent-soft text-accent":"text-fg-2 hover:bg-sunken")}><Ic d={I.game} size={15}/> {GM_CAT_LABEL[c.category]||c.category} <span className="ml-auto text-xs text-faint">{Number(c.enabled||0).toLocaleString()}</span></button>))}
  </nav>);
  const crumb=(<div className="flex items-center gap-2 text-sm">
    <button onClick={()=>goProv("")} className="text-fg-2 hover:text-accent">ทุกค่าย</button>
    {prov && <><Ic d={I.chevR} size={13} cls="text-faint"/><span className="font-semibold">{(provs.find(p=>p.provider===prov)||{}).provider_name||prov}</span>
      <button onClick={()=>goProv("")} className="ml-1 flex h-7 items-center gap-1 rounded-lg border border-line px-2 text-xs text-fg-2 hover:bg-sunken"><Ic d={I.back} size={13}/> ย้อนกลับ</button></>}
  </div>);

  return (<div className="space-y-3">
    <div className="rounded-xl border border-line bg-accent-soft/40 px-4 py-2.5 text-xs text-fg-2">
      <Ic d={I.up} size={14} cls="inline text-accent"/> <b>{prov?"บนสุด = ลูกค้าเห็นก่อน":"ค่ายบนสุด = ลูกค้าเห็นก่อน"}</b> — กดค้างที่การ์ดแล้วลากสลับตำแหน่งได้ (เมาส์/นิ้ว){prov?"":" หรือกดที่รูปค่ายเพื่อเข้าไปเรียงเกมข้างใน"}
    </div>
    <div className="flex flex-col gap-4 lg:flex-row">
      {rail}
      <div className="min-w-0 flex-1 space-y-3">
        {crumb}
        {loading ? <div className="grid grid-cols-2 gap-3 sm:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">{Array.from({length:8}).map((_,i)=><div key={i} className="skel h-36 w-full"/>)}</div>
        : !prov ? (
          provs.length===0 ? <div className="rounded-xl border border-dashed border-line py-16 text-center text-sm text-muted">ไม่มีค่ายที่เปิดเกมไว้ในหมวดนี้</div>
          : <>
            {provDirty && <div className="flex items-center gap-2 rounded-xl border border-accent/40 bg-accent-soft px-4 py-2.5 text-sm">
              <Ic d={I.up} size={15} cls="text-accent"/><span className="text-fg-2">ลำดับค่ายถูกแก้ไข — ค่ายแรกคือค่ายที่ลูกค้าเห็นก่อน</span>
              <span className="ml-auto flex gap-2"><button onClick={()=>load(cat,prov,limit)} className="h-8 rounded-lg border border-line px-3 text-xs font-medium text-fg-2 hover:bg-surface">ยกเลิก</button><button disabled={busy} onClick={saveProvs} className="h-8 rounded-lg bg-accent px-3 text-xs font-semibold text-accent-fg disabled:opacity-50">บันทึกลำดับค่าย</button></span>
            </div>}
            <div ref={gridRef} className="grid grid-cols-2 gap-3 sm:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
              {provs.map((x,i)=>(<div key={x.provider} data-provider={x.provider} className="gs-provcard relative cursor-grab select-none rounded-xl border border-line bg-surface p-3 shadow-sm active:cursor-grabbing">
                <span className="gs-pos absolute left-2 top-2 grid h-5 w-5 place-items-center rounded-full bg-sunken text-[10px] font-bold text-muted">{i+1}</span>
                <span className="absolute right-2 top-2 text-faint"><Ic d={I.up} size={13}/></span>
                <button onClick={()=>goProv(x.provider)} className="block w-full text-center">
                  <div className="mx-auto grid h-14 w-full place-items-center overflow-hidden rounded-lg bg-sunken">{x.logo_url?<img src={x.logo_url} loading="lazy" className="h-full w-full object-contain" onError={(e)=>e.target.style.display='none'}/>:<Ic d={I.game} size={22} cls="text-faint"/>}</div>
                  <div className="mt-1.5 truncate text-sm font-medium">{x.provider_name||x.provider}</div>
                </button>
                <div className="mt-0.5 text-center text-[11px] text-muted">{String(x.sources||"").split(",").filter(Boolean).map(s=>gmSrc(s)).join(" + ")} · <b className="text-success">{Number(x.n||0).toLocaleString()} เกม</b></div>
              </div>))}
            </div>
          </>
        ) : (
          rows.length===0 ? <div className="rounded-xl border border-dashed border-line py-16 text-center text-sm text-muted">ไม่มีเกมที่เปิดอยู่ของค่ายนี้</div>
          : <>
            {dirty && <div className="flex items-center gap-2 rounded-xl border border-accent/40 bg-accent-soft px-4 py-2.5 text-sm">
              <Ic d={I.up} size={15} cls="text-accent"/><span className="text-fg-2">ลำดับเกมถูกแก้ไข — เกมแรกคือเกมที่ลูกค้าเห็นก่อน</span>
              <span className="ml-auto flex gap-2"><button onClick={()=>load(cat,prov,limit)} className="h-8 rounded-lg border border-line px-3 text-xs font-medium text-fg-2 hover:bg-surface">ยกเลิก</button><button disabled={busy} onClick={saveGames} className="h-8 rounded-lg bg-accent px-3 text-xs font-semibold text-accent-fg disabled:opacity-50">บันทึกลำดับ</button></span>
            </div>}
            <div ref={gridRef} className="grid grid-cols-2 gap-3 sm:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
              {rows.map((g,i)=>(<article key={g.id} data-game-id={g.id} className="gs-gamecard relative cursor-grab select-none rounded-xl border border-line bg-surface p-2 shadow-sm active:cursor-grabbing">
                <span className="gs-pos absolute left-2 top-2 z-10 grid h-5 w-5 place-items-center rounded-full bg-sunken text-[10px] font-bold text-muted">{i+1}</span>
                <span className="absolute right-2 top-2 z-10 text-faint"><Ic d={I.up} size={13}/></span>
                {g.is_hot && <span className="absolute right-2 top-8 z-10 rounded bg-danger px-1 py-0.5 text-[9px] font-bold text-white" style={{background:"var(--danger)"}}>HOT</span>}
                {!g.is_hot && g.is_new && <span className="absolute right-2 top-8 z-10 rounded px-1 py-0.5 text-[9px] font-bold text-white" style={{background:"var(--info)"}}>NEW</span>}
                <div className="grid aspect-[2/3] w-full place-items-center overflow-hidden rounded-lg bg-sunken">{g.image_url?<img src={g.image_url} loading="lazy" className="h-full w-full object-contain" onError={(e)=>{e.target.style.display='none'}}/>:<Ic d={I.game} size={26} cls="text-faint"/>}</div>
                <div className="mt-1.5 truncate text-xs font-medium" title={g.name}>{g.name||"-"}</div>
                <div className="truncate text-[10px] text-faint">{g.provider_name||g.provider} · {gmSrc(g.source)}</div>
                <div className="text-[10px] text-faint">{Number(g.plays||0)>0?"เล่น "+Number(g.plays).toLocaleString("th-TH"):"ยังไม่มีสถิติ"}</div>
                <div className="mt-1"><button onClick={()=>turnOff(g)} title="ปิดเกมนี้" className="inline-flex h-6 items-center gap-1 rounded-full border-transparent px-2.5 text-[11px] font-medium text-white" style={{background:"var(--success)"}}>เปิด</button></div>
              </article>))}
            </div>
            <div className="text-center text-xs text-muted">{total>rows.length ? <>แสดง {rows.length} จาก {total.toLocaleString()} เกม <button onClick={()=>setLimit(l=>Math.min(500,l+120))} className="ml-1 rounded-lg border border-line px-2.5 py-1 font-medium text-fg-2 hover:bg-sunken">โหลดเพิ่ม 120</button></> : "แสดงครบทั้ง "+rows.length.toLocaleString()+" เกมแล้ว"}</div>
          </>
        )}
      </div>
    </div>
  </div>);
}
