/* ==========================================================================
 * ADMIN NEXT — page-members.jsx  (แยกจาก index.html เดิม; โหลดผ่าน <script type="text/babel">)
 * โหลดตามลำดับใน index.html — ทุกไฟล์แชร์ scope เดียวกัน (babel-standalone)
 * ========================================================================== */

/* ---------- หน้าสมาชิก (ข้อมูลจริง) ---------- */
function mapUser(r) {
  const banned = r.is_blacklisted || r.status === "suspended" || r.status === "banned";
  const days = (r.login_days || []);
  return {
    id: r.id, name: r.full_name || r.username || ("#"+r.id), username: r.username, ambUsername: r.amb_username || "", phone: r.phone || "-", phoneHidden: r.phone_hidden,
    acct: r.bank_account_no || "-", bankCode: r.bank_code, bankName: bankName(r.bank_code), accountName: r.bank_account_name || "",
    banned, status: r.status, depCount: Number(r.deposit_count || 0), depAmount: Number(r.deposit_amount || 0),
    balance: Number(r.balance || 0), star: Number(r.star || 0), createdAt: r.created_at,
    marketing: r.source_channel_name || r.marketing_org_name || "",
    loginset: new Set(days),
  };
}
function CreditAdjustModal({ user, onClose, onDone, toast }) {
  const [type,setType]=useState("direct");
  const [amt,setAmt]=useState("");
  const [reason,setReason]=useState("");
  const [busy,setBusy]=useState(false);
  // โบนัส + เทิร์น
  const [turnMode,setTurnMode]=useState("multiplier");   // multiplier | fixed
  const [turnType,setTurnType]=useState("deposit");        // deposit | credit | revolving
  const [turnValue,setTurnValue]=useState("1");            // เท่า (multiplier) หรือ ยอดบาท (fixed)
  const [wdrType,setWdrType]=useState("max_by_turnover");  // max_by_turnover | fixed_max_withdraw
  const [maxWdr,setMaxWdr]=useState("");
  // เติมเงิน (direct): บัญชีรับเงิน + สลิป + เวลาสลิป
  const [accounts,setAccounts]=useState([]);
  const [accountId,setAccountId]=useState("");
  const [slip,setSlip]=useState(null);              // {data,name,mime}
  const [slipMode,setSlipMode]=useState("manual");  // slip2go | manual (เวลาสลิป)
  const [slipTime,setSlipTime]=useState("");        // datetime-local
  useEffect(()=>{ api("/deposit-accounts").then(rows=>setAccounts((Array.isArray(rows)?rows:[]).filter(a=>a.is_active!==false&&!a.deleted_at))).catch(()=>{}); },[]);
  const acctLabel=(a)=> a.type==="wallet"
    ? [a.wallet_phone,a.account_name].filter(Boolean).join(" · ")
    : [(a.bank_name||a.bank_code),a.account_no,a.account_name].filter(Boolean).join(" · ");
  const onSlipPick=(e)=>{ const f=e.target.files&&e.target.files[0]; if(!f){setSlip(null);return;} const r=new FileReader(); r.onload=()=>setSlip({data:String(r.result||""),name:f.name||"slip.jpg",mime:f.type||"image/jpeg"}); r.readAsDataURL(f); };
  const TYPES=[["direct","เติมเงิน","var(--success)"],["bonus","โบนัส","var(--warn)"],["deduct","ตัดเครดิต","var(--danger)"]];
  const submit=async(allowDup=false)=>{
    const b=parseFloat(amt); if(!b||b<=0){toast(type==="bonus"?"กรอกจำนวนโบนัสให้ถูกต้อง":"กรอกจำนวนเงินให้ถูกต้อง");return;}
    if(!reason.trim()){toast("กรุณากรอกหมายเหตุ");return;}
    let body={ amount:Math.round(b*100), adjustType:type, reason:reason.trim() };
    if(type==="bonus"){
      const tv=parseFloat(turnValue)||0;
      if(tv<=0){toast(turnMode==="fixed"?"กรอกยอดเทิร์นให้ถูกต้อง":"กรอกจำนวนเท่าเทิร์นให้ถูกต้อง");return;}
      body.turnoverMode=turnMode;
      body.withdrawType=wdrType;
      body.maxWithdraw= wdrType==="fixed_max_withdraw" ? Math.round((parseFloat(maxWdr)||0)*100) : 0;
      if(turnMode==="fixed") body.turnoverAmount=Math.round(tv*100);
      else { body.turnoverMultiplier=tv; body.turnoverType=turnType; }
    } else if(type==="direct"){
      if(accountId) body.accountId=accountId;
      if(slip){ body.slipImageData=slip.data; body.slipFilename=slip.name; body.slipMimetype=slip.mime; }
      if(slip && slipMode==="slip2go"){ body.slipTimeMode="slip2go"; }
      else if(slipTime){ body.slipTimeMode="manual"; body.slipTimeManual=slipTime; }
      if(allowDup) body.allowDuplicateSlip=true;
    }
    setBusy(true);
    try{ await api("/users/"+user.id+"/adjust",{method:"POST",body});
      toast((type==="deduct"?"ตัดเครดิต":type==="bonus"?"เติมโบนัส":"เติมเครดิต")+" "+b.toLocaleString("th-TH")+" บาท สำเร็จ"); onDone&&onDone(); onClose();
    }catch(e){
      const err=e&&e.data&&e.data.error;
      if(err==="slip_already_used"){ setBusy(false); if(window.confirm("สลิปนี้เคยใช้แล้ว ยืนยันเติมซ้ำ?")) return submit(true); return; }
      if(err==="bank_mismatch"||err==="account_mismatch"){ toast("บัญชีผู้รับบนสลิปไม่ตรงกับบัญชีที่เลือก — สลับเป็น 'กรอกเวลาเอง' หรือเลือกบัญชีให้ถูก"); }
      else toast((e&&e.message)||"ไม่สำเร็จ");
    }finally{setBusy(false);}
  };
  const inp="mt-1 h-10 w-full rounded-lg border border-line bg-sunken px-3 text-sm text-fg placeholder:text-faint focus:border-accent focus:bg-surface focus:outline-none focus:ring-4 focus:ring-[var(--accent-ring)]";
  const seg=(val,set,opts)=>(<div className="mt-1 flex gap-1 rounded-lg border border-line bg-sunken p-0.5">{opts.map(([k,l])=>(<button key={k} type="button" onClick={()=>set(k)} className={"flex-1 rounded-md px-2 py-1.5 text-xs font-medium transition-colors "+(val===k?"bg-accent text-accent-fg shadow-sm":"text-fg-2 hover:text-fg")}>{l}</button>))}</div>);
  const btnColor = type==="deduct"?"var(--danger)":type==="bonus"?"var(--warn)":"var(--success)";
  return (<div className="fixed inset-0 z-[80] flex items-center justify-center p-4 fade-in" onMouseDown={()=>!busy&&onClose()}>
    <div className="absolute inset-0 bg-black/55"/>
    <div className="pop relative max-h-[92vh] w-full max-w-sm overflow-y-auto rounded-2xl border border-line bg-surface2 p-5 shadow-lg" onMouseDown={(e)=>e.stopPropagation()}>
      <div className="text-base font-semibold">ปรับเครดิตสมาชิก</div>
      <p className="mt-0.5 truncate text-xs text-muted">{user.name} · {user.username}</p>
      <div className="mt-3 grid grid-cols-3 gap-2">{TYPES.map(([k,l,c])=>(<button key={k} onClick={()=>setType(k)} className={"rounded-lg border px-2 py-2 text-sm font-medium transition-colors "+(type===k?"text-white":"border-line text-fg-2 hover:bg-sunken")} style={type===k?{background:c,borderColor:c}:{}}>{l}</button>))}</div>
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">{type==="bonus"?"จำนวนโบนัส (บาท)":"จำนวนเงิน (บาท)"}</span><input type="number" inputMode="decimal" className={inp+" tnum"} value={amt} onChange={(e)=>setAmt(e.target.value)} placeholder="0.00"/></label>
      {type==="bonus" && (<>
        <div className="mt-3"><span className="text-xs font-medium text-fg-2">รูปแบบเทิร์น</span>{seg(turnMode,setTurnMode,[["multiplier","จำนวนเท่า"],["fixed","ระบุยอด"]])}</div>
        {turnMode==="multiplier" ? (<>
          <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">จำนวนเท่า (× ของเครดิตตั้งต้น)</span><input type="number" inputMode="decimal" min="1" className={inp+" tnum"} value={turnValue} onChange={(e)=>setTurnValue(e.target.value)} placeholder="1"/></label>
          <div className="mt-3"><span className="text-xs font-medium text-fg-2">ชนิดเทิร์น</span>{seg(turnType,setTurnType,[["deposit","เทิร์นโอเวอร์"],["credit","เครดิต"],["revolving","หมุนเวียน"]])}</div>
        </>) : (
          <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">ยอดเทิร์นที่ต้องทำ (บาท)</span><input type="number" inputMode="decimal" min="0" className={inp+" tnum"} value={turnValue} onChange={(e)=>setTurnValue(e.target.value)} placeholder="0.00"/></label>
        )}
        <div className="mt-3"><span className="text-xs font-medium text-fg-2">การถอน</span>{seg(wdrType,setWdrType,[["max_by_turnover","ครบเทิร์นถอนได้"],["fixed_max_withdraw","จำกัดถอนสูงสุด"]])}</div>
        {wdrType==="fixed_max_withdraw" && <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">ถอนได้สูงสุด (บาท)</span><input type="number" inputMode="decimal" min="0" className={inp+" tnum"} value={maxWdr} onChange={(e)=>setMaxWdr(e.target.value)} placeholder="0.00"/></label>}
      </>)}
      {type==="direct" && (<>
        <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">บัญชีรับเงิน (ไม่บังคับ)</span>
          <select className={inp} value={accountId} onChange={(e)=>setAccountId(e.target.value)}>
            <option value="">— ไม่ระบุบัญชี —</option>
            {accounts.map(a=><option key={a.id} value={a.id}>{acctLabel(a)}</option>)}
          </select></label>
        <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">แนบสลิป (ไม่บังคับ)</span>
          <input type="file" accept="image/*" onChange={onSlipPick} className="mt-1 block w-full text-xs text-fg-2 file:mr-3 file:rounded-lg file:border-0 file:bg-accent-soft file:px-3 file:py-2 file:text-xs file:font-medium file:text-accent"/></label>
        {slip && <div className="mt-2 flex items-center gap-2 rounded-lg border border-line bg-sunken p-2"><img src={slip.data} alt="slip" className="h-12 w-12 rounded object-cover"/><span className="truncate text-xs text-muted">{slip.name}</span><button type="button" onClick={()=>setSlip(null)} className="ml-auto grid h-6 w-6 place-items-center rounded text-muted hover:bg-surface"><Ic d={I.x} size={13}/></button></div>}
        {slip && <div className="mt-3"><span className="text-xs font-medium text-fg-2">เวลาสลิป</span>{seg(slipMode,setSlipMode,[["slip2go","อ่านจากสลิป"],["manual","กรอกเอง"]])}</div>}
        {(!slip || slipMode==="manual") && <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">เวลาสลิป (ไม่บังคับ)</span><input type="datetime-local" className={inp} value={slipTime} onChange={(e)=>setSlipTime(e.target.value)}/></label>}
      </>)}
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">หมายเหตุ *</span><input required className={inp} value={reason} onChange={(e)=>setReason(e.target.value)} placeholder="เหตุผลการปรับเครดิต"/></label>
      <div className="mt-4 flex items-center justify-end gap-2">
        <button onClick={onClose} className="h-9 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken">ยกเลิก</button>
        <button disabled={busy} onClick={()=>submit(false)} className="h-9 rounded-lg px-4 text-sm font-semibold text-white disabled:opacity-50" style={{background:btnColor}}>{busy?"กำลังทำ…":"ยืนยัน"}</button>
      </div>
    </div>
  </div>);
}
function SimulateTurnoverModal({ userId, username, required, progress, onClose, onDone, toast }) {
  const [syncAmb,setSyncAmb]=useState(true);
  const [addAmount,setAddAmount]=useState("0");
  const [note,setNote]=useState("admin simulate");
  const [busy,setBusy]=useState(false);
  const pct = required>0 ? Math.min(100, Math.round(progress/required*1000)/10) : 100;
  const remaining = Math.max(0, required-progress);
  const inp="mt-1 h-10 w-full rounded-lg border border-line bg-sunken px-3 text-sm text-fg placeholder:text-faint focus:border-accent focus:bg-surface focus:outline-none focus:ring-4 focus:ring-[var(--accent-ring)]";
  const submit=async()=>{
    const add=parseFloat(addAmount)||0;
    if(!syncAmb && add<=0){ toast("เลือก Sync จาก AMB หรือระบุจำนวนเทิร์นที่จะเพิ่ม"); return; }
    setBusy(true);
    try{
      const data=await api("/users/"+userId+"/simulate-turnover",{method:"POST",body:{syncFromAmb:syncAmb,addAmount:add,note:note.trim()||"admin simulate"}});
      const parts=[];
      if(data.syncedFromAmb!=null) parts.push("Sync "+Number(data.syncedFromAmb).toLocaleString("th-TH")+" บาท");
      if(data.addedManually) parts.push("เพิ่มเอง "+Number(data.addedManually).toLocaleString("th-TH")+" บาท");
      toast((data.cleared?"จำลองเทิร์นสำเร็จ + ครบเทิร์นแล้ว! ":"จำลองเทิร์นสำเร็จ ")+(parts.length?"("+parts.join(" · ")+")":""));
      onDone&&onDone(); onClose();
    }catch(e){ toast(e.message||"ไม่สำเร็จ"); }finally{ setBusy(false); }
  };
  return (<div className="fixed inset-0 z-[80] flex items-center justify-center p-4 fade-in" onMouseDown={()=>!busy&&onClose()}>
    <div className="absolute inset-0 bg-black/55"/>
    <div className="pop relative w-full max-w-sm rounded-2xl border border-line bg-surface2 p-5 shadow-lg" onMouseDown={(e)=>e.stopPropagation()}>
      <div className="flex items-center gap-2 text-base font-semibold"><span className="grid h-7 w-7 place-items-center rounded-lg text-accent" style={{background:"var(--accent-soft)"}}><Ic d={I.refresh} size={16}/></span> จำลองเทิร์น</div>
      <p className="mt-0.5 truncate text-xs text-muted">{username}</p>
      <div className="mt-3 rounded-lg bg-sunken p-3">
        <div className="flex items-center justify-between text-[11px] text-muted"><span>เทิร์นปัจจุบัน</span><span className="tnum font-semibold text-fg-2">{money2(progress)} / {money2(required)} <span className="text-accent">({pct}%)</span></span></div>
        <div className="mt-1 h-2 overflow-hidden rounded-full bg-line"><div className="h-full rounded-full" style={{width:pct+"%",background:pct>=100?"var(--success)":"var(--accent)"}}/></div>
        <div className="mt-1 text-[11px] text-faint">ต้องการอีก <b className="text-warn">{money2(remaining)}</b></div>
      </div>
      <label className="mt-3 flex items-center gap-2 rounded-lg border border-line px-3 py-2.5 text-sm">
        <input type="checkbox" checked={syncAmb} onChange={(e)=>setSyncAmb(e.target.checked)} className="h-4 w-4 accent-[var(--accent)]"/>
        <span className="font-medium text-fg-2"><Ic d={I.refresh} size={13} cls="inline"/> Sync ประวัติการเล่นจาก AMB</span>
      </label>
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">เพิ่มเทิร์นเพิ่มเติม (บาท)</span><input type="number" inputMode="decimal" min="0" step="1" className={inp+" tnum"} value={addAmount} onChange={(e)=>setAddAmount(e.target.value)} placeholder="0 = ไม่เพิ่ม"/></label>
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">หมายเหตุ</span><input className={inp} value={note} onChange={(e)=>setNote(e.target.value)} placeholder="เหตุผลการจำลองเทิร์น"/></label>
      <div className="mt-4 flex items-center justify-end gap-2">
        <button onClick={onClose} className="h-9 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken">ยกเลิก</button>
        <button disabled={busy} onClick={submit} className="h-9 rounded-lg bg-accent px-4 text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{busy?"กำลังทำ…":"ดำเนินการ"}</button>
      </div>
    </div>
  </div>);
}
const BANK_OPTIONS = [["kbank","กสิกรไทย"],["scb","ไทยพาณิชย์"],["ktb","กรุงไทย"],["bbl","กรุงเทพ"],["bay","กรุงศรี"],["gsb","ออมสิน"],["ttb","ทีทีบี"],["baac","ธ.ก.ส."],["kk","เกียรตินาคิน"],["cimb","ซีไอเอ็มบี"],["uob","ยูโอบี"],["tisco","ทิสโก้"],["ghb","อาคารสงเคราะห์"],["lhb","แลนด์ฯ"],["tcrb","ไทยเครดิต"],["truewallet","ทรูวอลเล็ท"]];
function fieldCls(){ return "mt-1 h-10 w-full rounded-lg border border-line bg-sunken px-3 text-sm text-fg placeholder:text-faint focus:border-accent focus:bg-surface focus:outline-none focus:ring-4 focus:ring-[var(--accent-ring)]"; }
function EditUserModal({ user, onClose, onDone, toast }) {
  /* ล็อกช่องเบอร์ตามสิทธิ์ที่ server บอก (phone_hidden) ไม่ใช่ดูจาก * — เบอร์ที่เสียจนมี * อยู่ใน DB จริง
     ต้องให้คนที่มีสิทธิ์กรอกเบอร์ใหม่ทับได้ ไม่ได้ค่าสิทธิ์มา = ล็อกไว้ก่อน */
  const phoneHidden=(user.phone_hidden ?? user.phoneHidden)!==false;
  const phoneBroken=!phoneHidden&&String(user.phone||"").includes("*");
  const [initial]=useState(()=>({ full_name:user.full_name||user.name||"", phone:phoneBroken?"":(user.phone||""), bankCode:bankSlug(user.bank_code||user.bankCode)||"", bankAccountNo:user.bank_account_no||user.acct||"", bankAccountName:user.bank_account_name||user.accountName||"" }));
  const [form,setForm]=useState(initial);
  const [busy,setBusy]=useState(false);
  const set=(k,v)=>setForm(f=>({...f,[k]:v})); const inp=fieldCls();
  const labels={ full_name:"ชื่อ-นามสกุล", phone:"เบอร์โทร", bankCode:"ธนาคาร", bankAccountNo:"เลขบัญชี", bankAccountName:"ชื่อบัญชี" };
  /* ส่งเฉพาะช่องที่แก้ — ช่องอื่น (โดยเฉพาะเบอร์ที่ถูกซ่อน) ไม่ถูกส่งและไม่ถูกแตะ */
  const changedKeys=Object.keys(labels).filter(k=>!(k==="phone"&&phoneHidden) && String(form[k]??"").trim()!==String(initial[k]??"").trim());
  const submit=async()=>{
    if(!changedKeys.length){toast("ไม่มีข้อมูลที่เปลี่ยนแปลง");return;}
    if(changedKeys.includes("phone")&&!form.phone.trim()){toast("กรุณากรอกเบอร์โทร");return;}
    if(changedKeys.includes("phone")&&!/^0\d{8,9}$/.test(form.phone.trim())){toast("เบอร์โทรไม่ถูกต้อง (ตัวเลข 9-10 หลัก ขึ้นต้นด้วย 0)");return;}
    if(changedKeys.includes("bankCode")&&!form.bankCode){toast("กรุณาเลือกธนาคาร");return;}
    setBusy(true);
    try{
      const body=Object.fromEntries(changedKeys.map(k=>[k,form[k]]));
      await api("/users/"+user.id+"/profile",{method:"PATCH",body});
      toast("บันทึกแล้ว: "+changedKeys.map(k=>labels[k]).join(", ")); onDone&&onDone(); onClose();
    }
    catch(e){toast(e.message||"บันทึกไม่สำเร็จ");}finally{setBusy(false);}
  };
  return (<div className="fixed inset-0 z-[80] flex items-center justify-center p-4 fade-in" onMouseDown={()=>!busy&&onClose()}>
    <div className="absolute inset-0 bg-black/55"/>
    <div className="pop relative w-full max-w-sm rounded-2xl border border-line bg-surface2 p-5 shadow-lg" onMouseDown={(e)=>e.stopPropagation()}>
      <div className="text-base font-semibold">แก้ไขข้อมูลสมาชิก</div>
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">ชื่อ–นามสกุล</span><input className={inp} value={form.full_name} onChange={(e)=>set("full_name",e.target.value)}/></label>
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">เบอร์โทรศัพท์</span><input className={inp+" mono"+(phoneHidden?" cursor-not-allowed opacity-60":"")} value={form.phone} readOnly={phoneHidden} placeholder={phoneBroken?String(user.phone):undefined} onChange={(e)=>set("phone",e.target.value)}/>{phoneHidden&&<span className="mt-1 block text-[11px] text-muted">ไม่มีสิทธิ์เห็นเบอร์ — ระบบจะไม่เปลี่ยนเบอร์ของลูกค้า</span>}{phoneBroken&&<span className="mt-1 block text-[11px] text-warn">เบอร์ในระบบเสีย ({String(user.phone)}) — ลูกค้าล็อกอินไม่ได้ กรอกเบอร์จริงเพื่อแก้</span>}</label>
      <div className="mt-3 grid grid-cols-2 gap-2">
        <label className="block"><span className="text-xs font-medium text-fg-2">ธนาคาร</span><select className={inp} value={form.bankCode} onChange={(e)=>set("bankCode",e.target.value)}><option value="">-</option>{BANK_OPTIONS.map(([k,l])=><option key={k} value={k}>{l}</option>)}</select></label>
        <label className="block"><span className="text-xs font-medium text-fg-2">เลขบัญชี</span><input className={inp+" mono"} value={form.bankAccountNo} onChange={(e)=>set("bankAccountNo",e.target.value)}/></label>
      </div>
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">ชื่อบัญชี</span><input className={inp} value={form.bankAccountName} onChange={(e)=>set("bankAccountName",e.target.value)}/></label>
      <div className="mt-3 text-[11px] text-muted">{changedKeys.length?<>จะบันทึกเฉพาะ: <span className="font-medium text-accent">{changedKeys.map(k=>labels[k]).join(", ")}</span></>:"ยังไม่ได้แก้ช่องไหน"}</div>
      <div className="mt-4 flex items-center justify-end gap-2">
        <button onClick={onClose} className="h-9 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken">ยกเลิก</button>
        <button disabled={busy} onClick={submit} className="h-9 rounded-lg bg-accent px-4 text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{busy?"กำลังบันทึก…":"บันทึก"}</button>
      </div>
    </div>
  </div>);
}
function AddUserModal({ onClose, onDone, toast }) {
  const [form,setForm]=useState({ phone:"", password:"", fullName:"", accountType:"bank", bankCode:"kbank", bankAccountNo:"", walletNo:"", bankAccountName:"" });
  const [busy,setBusy]=useState(false);
  const set=(k,v)=>setForm(f=>({...f,[k]:v})); const inp=fieldCls();
  const fillRandomData=()=>{
    const digit=()=>Math.floor(Math.random()*10);
    const firstNames=["สมชาย","สมหญิง","วิชัย","นภา","ปรียา","ธนกร","กมล","รัตนา","ชัยวัฒน์","มาลี"];
    const lastNames=["ใจดี","รักไทย","มีสุข","พรหมสุข","วงศ์ทอง","ศรีสะอาด","เจริญสุข","ทองคำ","แก้วมณี","บุญมา"];
    const phone="0"+[6,7,8,9][Math.floor(Math.random()*4)]+Array.from({length:8},digit).join("");
    const fullName=firstNames[Math.floor(Math.random()*firstNames.length)]+" "+lastNames[Math.floor(Math.random()*lastNames.length)];
    const accountType=Math.random()<0.25?"wallet":"bank";
    const bankCodes=BANK_OPTIONS.map(([code])=>code).filter(code=>code!=="truewallet");
    const bankCode=bankCodes[Math.floor(Math.random()*bankCodes.length)];
    const password=Math.random().toString(36).slice(2,10)+Math.floor(Math.random()*90+10);
    setForm({phone,password,fullName,accountType,bankCode,bankAccountNo:Array.from({length:10},digit).join(""),walletNo:phone,bankAccountName:fullName});
  };
  const submit=async()=>{
    if(!/^0\d{8,9}$/.test(form.phone)){toast("เบอร์โทรไม่ถูกต้อง (0xxxxxxxxx)");return;}
    if(!form.password||form.password.length<6){toast("รหัสผ่านอย่างน้อย 6 ตัว");return;}
    const isWallet=form.accountType==="wallet";
    const bankAccountNo=isWallet?(form.walletNo||form.phone):form.bankAccountNo;
    if(isWallet&&!/^0\d{8,9}$/.test(bankAccountNo)){toast("เบอร์วอลเล็ตไม่ถูกต้อง");return;}
    if(!isWallet&&!form.bankCode){toast("เลือกธนาคาร");return;}
    if(!isWallet&&(!bankAccountNo||bankAccountNo.replace(/\D/g,"").length<6)){toast("เลขบัญชีไม่ถูกต้อง");return;}
    setBusy(true);
    const body={phone:form.phone,password:form.password,fullName:form.fullName,bankCode:isWallet?"truewallet":form.bankCode,bankAccountNo,bankAccountName:form.bankAccountName};
    try{ const r=await api("/users/create",{method:"POST",body}); toast("เพิ่มสมาชิกแล้ว: "+(r&&r.username||"")); onDone&&onDone(); onClose(); }
    catch(e){ const m={invalid_phone:"เบอร์โทรไม่ถูกต้อง",password_too_short:"รหัสผ่านสั้นเกินไป",phone_already_registered:"เบอร์นี้มีอยู่แล้ว",bank_account_already_registered:"เลขบัญชีนี้มีอยู่แล้ว",bank_code_required:"เลือกธนาคาร",bank_account_required:"เลขบัญชีไม่ถูกต้อง"}[e.message]||e.message||"เพิ่มไม่สำเร็จ"; toast(m); }
    finally{setBusy(false);}
  };
  return (<div className="fixed inset-0 z-[80] flex items-center justify-center p-4 fade-in" onMouseDown={()=>!busy&&onClose()}>
    <div className="absolute inset-0 bg-black/55"/>
    <div className="pop relative w-full max-w-sm rounded-2xl border border-line bg-surface2 p-5 shadow-lg" onMouseDown={(e)=>e.stopPropagation()}>
      <div className="flex items-center justify-between gap-2">
        <div className="text-base font-semibold">เพิ่มสมาชิกใหม่</div>
        <button type="button" onClick={fillRandomData} disabled={busy} className="inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-line bg-sunken px-2.5 py-1.5 text-xs font-medium text-fg-2 hover:bg-surface disabled:opacity-50"><Ic d={I.wheel} size={13}/> Random ข้อมูลทดสอบ</button>
      </div>
      <p className="mt-0.5 text-xs text-muted">ยูสเซอร์เนมจะถูกสร้างอัตโนมัติตามที่ตั้งค่าไว้</p>
      <div className="mt-3 grid grid-cols-2 gap-2">
        <label className="block"><span className="text-xs font-medium text-fg-2">เบอร์โทรศัพท์ *</span><input className={inp+" mono"} value={form.phone} onChange={(e)=>set("phone",e.target.value)} placeholder="08xxxxxxxx"/></label>
        <label className="block"><span className="text-xs font-medium text-fg-2">รหัสผ่าน *</span><input className={inp} value={form.password} onChange={(e)=>set("password",e.target.value)} placeholder="อย่างน้อย 6 ตัว"/></label>
      </div>
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">ชื่อ–นามสกุล</span><input className={inp} value={form.fullName} onChange={(e)=>set("fullName",e.target.value)} placeholder="(เว้นได้ ใช้ชื่อบัญชีแทน)"/></label>
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">ประเภทบัญชี</span><select className={inp} value={form.accountType} onChange={(e)=>{const accountType=e.target.value;setForm(f=>({...f,accountType,walletNo:accountType==="wallet"?(f.walletNo||f.phone):f.walletNo}));}}><option value="bank">ธนาคาร</option><option value="wallet">วอลเล็ต (TrueMoney)</option></select></label>
      {form.accountType==="wallet" ? (
        <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">เบอร์วอลเล็ต *</span><input className={inp+" mono"} value={form.walletNo} onChange={(e)=>set("walletNo",e.target.value)} placeholder="08xxxxxxxx"/></label>
      ) : (
        <div className="mt-3 grid grid-cols-2 gap-2">
          <label className="block"><span className="text-xs font-medium text-fg-2">ธนาคาร *</span><select className={inp} value={form.bankCode} onChange={(e)=>set("bankCode",e.target.value)}>{BANK_OPTIONS.filter(([k])=>k!=="truewallet").map(([k,l])=><option key={k} value={k}>{l}</option>)}</select></label>
          <label className="block"><span className="text-xs font-medium text-fg-2">เลขบัญชี *</span><input className={inp+" mono"} value={form.bankAccountNo} onChange={(e)=>set("bankAccountNo",e.target.value)}/></label>
        </div>
      )}
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">ชื่อบัญชี</span><input className={inp} value={form.bankAccountName} onChange={(e)=>set("bankAccountName",e.target.value)}/></label>
      <div className="mt-4 flex items-center justify-end gap-2">
        <button onClick={onClose} className="h-9 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken">ยกเลิก</button>
        <button disabled={busy} onClick={submit} className="h-9 rounded-lg bg-accent px-4 text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{busy?"กำลังเพิ่ม…":"เพิ่มสมาชิก"}</button>
      </div>
    </div>
  </div>);
}
function PasswordModal({ user, onClose, toast }) {
  const [pw,setPw]=useState(""),[busy,setBusy]=useState(false); const inp=fieldCls();
  const submit=async()=>{ if(pw.length<4){toast("รหัสผ่านสั้นเกินไป");return;} setBusy(true);
    try{ await api("/users/"+user.id+"/password",{method:"PUT",body:{password:pw}}); toast("เปลี่ยนรหัสผ่านแล้ว"); onClose(); }
    catch(e){toast(e.message||"ไม่สำเร็จ");}finally{setBusy(false);} };
  return (<div className="fixed inset-0 z-[80] flex items-center justify-center p-4 fade-in" onMouseDown={()=>!busy&&onClose()}>
    <div className="absolute inset-0 bg-black/55"/>
    <div className="pop relative w-full max-w-sm rounded-2xl border border-line bg-surface2 p-5 shadow-lg" onMouseDown={(e)=>e.stopPropagation()}>
      <div className="text-base font-semibold">เปลี่ยนรหัสผ่าน</div>
      <p className="mt-0.5 truncate text-xs text-muted">{user.name||user.full_name} · {user.username}</p>
      <label className="mt-3 block"><span className="text-xs font-medium text-fg-2">รหัสผ่านใหม่</span><input className={inp} value={pw} onChange={(e)=>setPw(e.target.value)} placeholder="อย่างน้อย 4 ตัว"/></label>
      <div className="mt-4 flex items-center justify-end gap-2">
        <button onClick={onClose} className="h-9 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken">ยกเลิก</button>
        <button disabled={busy} onClick={submit} className="h-9 rounded-lg bg-accent px-4 text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{busy?"กำลังทำ…":"บันทึก"}</button>
      </div>
    </div>
  </div>);
}
/* ผลของปุ่ม "ดึงเครดิต" ฝั่ง UFA (transfer-out เข้ากระเป๋า) */
function ufaPullMessage(r) {
  if (!r || r.status === "no_ufa_user") return "ดึงเครดิตเกมล่าสุดแล้ว";
  const credit = N(r.credit);
  if (credit > 0) return "ดึงเครดิต UFA เข้ากระเป๋าแล้ว " + money2(credit) + (N(r.outstandingBaht) > 0 ? " (ยังมีบิลค้าง " + N(r.outstandingBaht).toLocaleString("th-TH") + " บาท ระบบจะดึงให้เมื่อบิลจบ)" : "");
  if (r.status === "pending_bets_keep_active") return "UFA มีบิลค้าง " + N(r.outstandingBaht).toLocaleString("th-TH") + " บาท — รอบิลจบแล้วกดดึงอีกครั้ง";
  if (r.status === "settled") return "ปิดรอบ UFA แล้ว ไม่มียอดเหลือในเกม";
  if (r.status === "session_started_skip") return "ลูกค้ากำลังเข้าเกม UFA — ลองกดใหม่อีกครั้ง";
  if (/failed/.test(String(r.status || ""))) return "ดึงเครดิต UFA ไม่สำเร็จ: " + (r.message || r.status);
  return "UFA ไม่มียอดค้าง — ดึงเครดิตเกมล่าสุดแล้ว";
}

/* ยอดฝาก/ถอน/กำไรของลูกค้า ตลอดชีพ + 6 เดือน (หน่วยสตางค์) — กำไร = ฝาก − ถอน (มุมเว็บ) */
function MemberMoneyStats({ stats }) {
  if (!stats) return null;
  const amt = (v) => (N(v)/100).toLocaleString("th-TH",{minimumFractionDigits:2,maximumFractionDigits:2});
  const chip = (label, value, bg, fg, title) => (<div title={title} className="min-w-0 rounded-md px-2 py-1" style={{background:bg}}>
    <div className="truncate text-[10px] font-medium leading-tight text-fg-2">{label}</div>
    <div className="truncate tnum text-[12px] font-semibold leading-tight" style={{color:fg}}>{amt(value)}</div>
  </div>);
  const row = (suffix, deposit, withdraw, depositBg, depositFg) => {
    const profit = N(deposit) - N(withdraw);
    const profitFg = profit > 0 ? "var(--success)" : profit < 0 ? "var(--danger)" : "var(--fg)";
    return (<div className="grid grid-cols-3 gap-1.5">
      {chip("ยอดฝาก"+suffix, deposit, depositBg, depositFg, "เงินฝากจริงที่สำเร็จ ไม่รวมคูปอง/โค้ด")}
      {chip("ยอดถอน"+suffix, withdraw, "var(--danger-soft)", "var(--danger)", "รายการถอนที่อนุมัติ/จ่ายแล้ว")}
      {chip("กำไร"+suffix, profit, "var(--sunken)", profitFg, "ยอดฝาก − ยอดถอน (มุมเว็บ)")}
    </div>);
  };
  return (<div className="mb-3 space-y-1.5">
    {row("ตลอดชีพ", stats.deposit_lifetime, stats.withdraw_lifetime, "var(--info-soft)", "var(--info)")}
    {row(" 6 เดือน", stats.deposit_6m, stats.withdraw_6m, "var(--success-soft)", "var(--success)")}
  </div>);
}

function MemberDrawer({ userId, onClose, onAdjust, onEdit, onPassword, onImpersonate, onChanged, toast }) {
  const [d,setD]=useState(null),[loading,setLoading]=useState(true),[err,setErr]=useState(null);
  const reload=useCallback(()=>{ setLoading(true); setErr(null); api("/users/"+userId+"/detail").then(x=>{setD(x);setLoading(false);}).catch(e=>{setErr(e.message||"โหลดไม่สำเร็จ");setLoading(false);}); },[userId]);
  useEffect(()=>{ reload(); },[reload]);
  useEffect(()=>{const h=(e)=>e.key==="Escape"&&onClose();document.addEventListener("keydown",h);return ()=>document.removeEventListener("keydown",h);},[onClose]);
  const u=(d&&d.user)||{}, w=(d&&d.wallet)||{};
  const nm=u.full_name||u.username||"-";
  const banned=u.is_blacklisted||u.status==="suspended";
  const stat=(label,val,c)=>(<div className="rounded-lg bg-sunken p-2.5"><div className="text-[10px] text-muted">{label}</div><div className="mt-0.5 text-sm font-semibold tnum" style={c?{color:c}:{}}>{val}</div></div>);
  const cnt=(k)=> (d&&Array.isArray(d[k]))?d[k].length:0;
  const [busyA,setBusyA]=useState(false);
  const [htab,setHtab]=useState("tx");
  const [betProv,setBetProv]=useState("");
  const [betDay,setBetDay]=useState("");
  const [betModal,setBetModal]=useState(false);
  const [simOpen,setSimOpen]=useState(false);
  const act=async(fn,msg)=>{ setBusyA(true); try{ await fn(); if(msg)toast(msg); reload(); onChanged&&onChanged(); }catch(e){toast(e.message||"ไม่สำเร็จ");}finally{setBusyA(false);} };
  const doStar=()=>act(()=>api("/users/"+userId+"/star",{method:"PATCH",body:{star:!(u.star)}}));
  const doStatus=()=>{ const suspend=!(u.status==="suspended"||u.is_blacklisted); act(()=>api("/users/"+userId+"/status",{method:"PATCH",body:{status:suspend?"suspended":"active"}}), suspend?"ระงับสมาชิกแล้ว":"ปลดระงับแล้ว"); };
  const doSync=()=>act(async()=>{ const r=await api("/users/"+userId+"/sync-game-credit",{method:"POST",body:{pullUfa:true}}); toast(ufaPullMessage(r&&r.ufa)); });
  const doBlacklist=()=>{ const bl=!u.is_blacklisted;
    if(bl){ const reason=window.prompt("เหตุผลการแบล็คลิสต์:",""); if(reason===null)return; act(()=>api("/users/"+userId+"/blacklist",{method:"PATCH",body:{blacklisted:true,reason}}),"แบล็คลิสต์แล้ว"); }
    else { if(!window.confirm("ปลดแบล็คลิสต์สมาชิกนี้?"))return; act(()=>api("/users/"+userId+"/blacklist",{method:"PATCH",body:{blacklisted:false}}),"ปลดแบล็คลิสต์แล้ว"); } };
  const doClearTurnover=()=>{ if(!window.confirm("ล้างเทิร์นสมาชิกนี้? (โบนัสอาจถูกตัดหรือแปลงเป็นเงินสดตามเงื่อนไข)"))return; const note=window.prompt("เหตุผลการล้างเทิร์น:","admin force clear"); if(note===null)return; act(()=>api("/turnover/clear/"+userId,{method:"POST",body:{note:note||"admin force clear"}}).catch(e=>{ throw new Error(turnoverClearErrorText(e)); }),"ล้างเทิร์นแล้ว"); };
  const doDelete=async()=>{ const t=window.prompt('พิมพ์ DELETE เพื่อยืนยันลบสมาชิก '+(u.username||"")+" (ลบถาวร กู้คืนไม่ได้):"); if(t!=="DELETE")return; setBusyA(true); try{ await api("/users/"+userId,{method:"DELETE"}); toast("ลบสมาชิกแล้ว"); onChanged&&onChanged(); onClose(); }catch(e){toast(e.message||"ลบไม่สำเร็จ");}finally{setBusyA(false);} };
  const canU=can("users"), canCredit=canAny(["tool_manage_credit","users"]), canImp=can("perm_impersonate_member");
  const txList = (((d&&d.deposits)||[]).map(x=>({...x,_t:"deposit"}))
    .concat(((d&&d.withdrawals)||[]).map(x=>({...x,_t:"withdraw"})))
    .concat(((d&&d.promotions)||[]).map(x=>({...x,_t:"promo"}))))
    .sort((a,b)=> new Date(b.created_at)-new Date(a.created_at));
  const betProviders = Array.from(new Set(((d&&d.providerBets)||[]).map(x=>x.provider_name||x.provider).filter(Boolean)));
  const bkDay=(iso)=>{ try{ return new Intl.DateTimeFormat("en-CA",{timeZone:"Asia/Bangkok"}).format(new Date(iso)); }catch{return "";} };
  const isCoupon=(x)=>["code","coupon","deposit_code"].includes(String(x.channel||"").toLowerCase());
  const histData=(k)=> k==="tx" ? txList
    : k==="providerBets" ? ((d&&d.providerBets)||[]).filter(x=> (!betProv || (x.provider_name||x.provider)===betProv) && (!betDay || bkDay(x.created_at)===betDay))
    : k==="coupon" ? ((d&&d.deposits)||[]).filter(isCoupon)
    : k==="referral" ? ((d&&d.referral)||[])
    : ((d&&d[k])||[]);
  const HIST=[["tx","ธุรกรรม"],["coupon","คูปอง"],["providerBets","การเล่น"],["wheelSpins","วงล้อ"],["checkins","เช็คอิน"],["referral","พันธมิตร"]];
  const DEP_TX={success:{t:"สำเร็จ",c:"success"},pending:{t:"รอตรวจ",c:"warn"},rejected:{t:"ปฏิเสธ",c:"danger"},failed:{t:"ไม่สำเร็จ",c:"danger"}};
  const WDR_TX={paid:{t:"โอนแล้ว",c:"success"},approved:{t:"อนุมัติ",c:"success"},pending:{t:"รออนุมัติ",c:"warn"},rejected:{t:"ปฏิเสธ",c:"danger"},failed:{t:"ไม่สำเร็จ",c:"danger"}};
  const txRow=(label,color,amt,badge,time)=>(<div className="flex items-center gap-2 px-3 py-2 text-xs">
    <div className="min-w-0 flex-1"><div className="truncate text-fg-2">{label}</div><div className="text-[10px] text-faint">{fmtDT(time)}</div></div>
    {badge && <Badge tone={badge.c}>{badge.t}</Badge>}
    <span className="w-24 shrink-0 text-right tnum font-semibold" style={{color}}>{amt}</span></div>);
  const histRow=(k,x,i)=>{
    if(k==="tx"){
      if(x._t==="deposit"){ const st=DEP_TX[String(x.status||"").toLowerCase()]||{t:x.status,c:"info"}; return <div key={i}>{txRow("ฝากเงิน · "+(CHANNEL_LABEL[x.channel]||x.channel||"-"),"var(--success)","+"+money2(x.gross_amount!=null?x.gross_amount:x.amount),st,x.created_at)}</div>; }
      if(x._t==="withdraw"){ const st=WDR_TX[String(x.status||"").toLowerCase()]||{t:x.status,c:"info"}; return <div key={i}>{txRow("ถอนเงิน","var(--danger)","-"+money2(Math.abs(Number(x.amount||0))),st,x.created_at)}</div>; }
      return <div key={i}>{txRow("โปรโมชั่น"+(x.code?" · "+x.code:(x.name?" · "+x.name:"")),"var(--warn)","+"+money2(x.bonus_amount!=null?x.bonus_amount:(x.amount||0)),null,x.created_at)}</div>;
    }
    if(k==="providerBets") return <div key={i} className="flex items-center gap-2 px-3 py-2 text-xs">
      <div className="min-w-0 flex-1"><div className="truncate text-fg-2">{x.game_name||x.provider_name||x.provider||"-"}</div><div className="text-[10px] text-faint">{fmtDT(x.created_at)} · แทง {money2(x.bet_amount)}</div></div>
      <span className="w-24 shrink-0 text-right tnum font-semibold" style={{color:Number(x.net)>=0?"var(--success)":"var(--danger)"}}>{Number(x.net)>=0?"+":""}{money2(x.net)}</span></div>;
    if(k==="wheelSpins") return <div key={i}>{txRow(x.reward_label||"หมุนวงล้อ","var(--accent)","+"+money2(x.reward_amount),{t:x.wallet_credited?"เติมแล้ว":"ไม่ได้เติม",c:x.wallet_credited?"success":"info"},x.created_at)}</div>;
    if(k==="checkins") return <div key={i}>{txRow("เช็คอิน วันที่ "+Number(x.checkin_day||0)+" (streak "+Number(x.streak||0)+")","var(--success)","+"+money2(x.reward_amount),null,x.created_at)}</div>;
    if(k==="coupon"){ const st=DEP_TX[String(x.status||"").toLowerCase()]||{t:x.status,c:"info"}; return <div key={i}>{txRow("คูปอง"+(x.ref_id?" · "+x.ref_id:""),"var(--warn)","+"+money2(x.gross_amount!=null?x.gross_amount:x.amount),st,x.created_at)}</div>; }
    if(k==="referral") return <div key={i}>{txRow("ชวนเพื่อน"+(x.referred_username?" · "+x.referred_username:""),"var(--success)","+"+money2(x.amount),null,x.created_at)}</div>;
    return null;
  };
  return (<div className="fixed inset-0 z-50 flex justify-end fade-in">
    <div className="absolute inset-0 bg-black/45" onClick={onClose}/>
    <div className="drawer-in relative flex h-full w-full max-w-md flex-col border-l border-line bg-surface shadow-lg">
      <div className="flex min-h-16 items-center gap-3 border-b border-line px-5 py-2">
        <div className="grid h-9 w-9 shrink-0 place-items-center rounded-full text-sm font-semibold text-white" style={{background:avc(nm)}}>{initials(nm)}</div>
        <div className="min-w-0"><div className="truncate text-[15px] font-semibold">{nm}</div><div className="truncate mono text-xs text-muted">{u.username||"-"} · {u.phone||"-"}</div>
          {u.amb_username && <div className="truncate mono text-[11px] text-faint" title="ยูสเซอร์ฝั่ง Agent">Agent: {u.amb_username}</div>}</div>
        {canU && <button disabled={busyA} onClick={doStar} title={u.star?"เอาดาวออก":"ติดดาว"} className="ml-auto grid h-8 w-8 place-items-center rounded-lg hover:bg-sunken" style={{color:u.star?"#F59E0B":"var(--faint)"}}><svg width="18" height="18" viewBox="0 0 24 24" fill={u.star?"#F59E0B":"none"} stroke="currentColor" strokeWidth="1.7"><path d={I.star[0]}/></svg></button>}
        <span className={canU?"":"ml-auto"}>{banned?<Badge tone="danger">ระงับ</Badge>:<Badge tone="success">ปกติ</Badge>}</span>
        <button onClick={onClose} className="grid h-9 w-9 place-items-center rounded-lg text-muted hover:bg-sunken hover:text-fg"><Ic d={I.x}/></button>
      </div>
      {err ? (<div className="flex-1 p-6 text-center"><div className="text-sm text-fg-2">{err}</div><button onClick={reload} className="mt-3 rounded-lg border border-line px-3 py-1.5 text-xs">ลองใหม่</button></div>)
      : loading ? (<div className="flex-1 space-y-3 p-5"><div className="skel h-20 w-full"/><div className="skel h-24 w-full"/><div className="skel h-32 w-full"/></div>)
      : (<div className="flex-1 overflow-y-auto p-5">
        <MemberMoneyStats stats={d&&d.stats}/>
        <div className="grid grid-cols-2 gap-2">
          {stat("ยอดกระเป๋า",money2(w.local_balance!=null?w.local_balance:w.balance),"var(--accent)")}
          {stat("ยอดในเกม",money2(w.active_amb_balance),"var(--info)")}
          {stat("โบนัส",money2(w.bonus_balance),"var(--warn)")}
          {stat("ถอนได้สูงสุด", w.max_withdraw>0?money2(w.max_withdraw):"ไม่จำกัด")}
        </div>
        <div className="mt-2 rounded-lg bg-sunken p-2.5">
          <div className="flex items-center justify-between text-[10px] text-muted"><span>เทิร์นโอเวอร์</span><span className="tnum font-semibold text-fg-2">{money2(w.turnover_progress)} / {money2(w.turnover_required)}</span></div>
          {(() => { const req=Number(w.turnover_required||0), prog=Number(w.turnover_progress||0), pct=req>0?Math.min(100,prog/req*100):100;
            return <div className="mt-1 h-2 overflow-hidden rounded-full bg-line"><div className="h-full rounded-full" style={{width:pct+"%",background:pct>=100?"var(--success)":"var(--accent)"}}/></div>; })()}
        </div>
        {(() => {
          const days=DAYS7; const dm={}, wm={}; days.forEach(dd=>{dm[dd]=0; wm[dd]=0;});
          const dkey=(iso)=>{ try{ return new Intl.DateTimeFormat("en-CA",{timeZone:"Asia/Bangkok"}).format(new Date(iso)); }catch{return "";} };
          ((d&&d.deposits)||[]).forEach(x=>{ if(String(x.status||"")==="success"){ const k=dkey(x.created_at); if(k in dm) dm[k]+=Number(x.gross_amount!=null?x.gross_amount:x.amount||0); } });
          ((d&&d.withdrawals)||[]).forEach(x=>{ if(["paid","approved"].includes(String(x.status||""))){ const k=dkey(x.created_at); if(k in wm) wm[k]+=Number(x.amount||0); } });
          const dep=days.map(dd=>({date:dd,amount:String(dm[dd])})), wdr=days.map(dd=>({date:dd,amount:String(wm[dd])}));
          const hasData=days.some(dd=>dm[dd]>0||wm[dd]>0);
          return hasData ? (<div className="mt-3 rounded-lg border border-line p-3"><div className="mb-1 flex items-center justify-between"><span className="text-xs font-semibold text-fg-2">ฝาก–ถอน 7 วัน</span><span className="flex items-center gap-2 text-[10px]"><span className="flex items-center gap-1"><span className="h-2 w-2 rounded-sm" style={{background:"var(--success)"}}/>ฝาก</span><span className="flex items-center gap-1"><span className="h-2 w-2 rounded-sm" style={{background:"var(--danger)"}}/>ถอน</span></span></div><Bars7d dep={dep} wdr={wdr}/></div>) : null;
        })()}
        <div className="mt-4 grid grid-cols-4 gap-2 text-center">
          {[["ฝาก",cnt("deposits")],["ถอน",cnt("withdrawals")],["เล่นเกม",cnt("providerBets")],["วงล้อ",cnt("wheelSpins")]].map(([l,c],i)=>(
            <div key={i} className="rounded-lg border border-line p-2"><div className="text-base font-semibold tnum">{c}</div><div className="text-[10px] text-muted">{l}</div></div>))}
        </div>
        <div className="mt-4 divide-y divide-line-soft rounded-lg border border-line">
          {[["ธนาคาร",[bankName(u.bank_code),u.bank_account_no,u.bank_account_name].filter(Boolean).join(" · ")],["สายการตลาด",u.source_channel_name||"-"],["รหัสแนะนำ",u.referral_code||"-"]].map(([k,v],i)=>(
            <div key={i} className="flex gap-3 px-3 py-2 text-sm"><span className="w-24 shrink-0 text-muted">{k}</span><span className="min-w-0 flex-1 break-words font-medium text-fg-2">{v}</span></div>))}
        </div>
        <div className="mt-4">
          <div className="mb-2 flex flex-wrap gap-1 rounded-lg border border-line bg-sunken p-0.5">
            {HIST.map(([k,l])=>(<button key={k} onClick={()=>setHtab(k)} className={"rounded-md px-2.5 py-1 text-xs font-medium transition-colors "+(htab===k?"bg-accent text-accent-fg shadow-sm":"text-fg-2 hover:text-fg")}>{l}<span className={"ml-1 text-[10px] "+(htab===k?"opacity-80":"text-faint")}>{histData(k).length}</span></button>))}
          </div>
          {htab==="providerBets" && <div className="mb-2 flex flex-wrap items-center gap-1.5">
            <select value={betProv} onChange={(e)=>setBetProv(e.target.value)} className="h-8 rounded-lg border border-line bg-surface px-2 text-xs text-fg-2"><option value="">ทุกค่าย</option>{betProviders.map(p=><option key={p} value={p}>{p}</option>)}</select>
            <input type="date" value={betDay} onChange={(e)=>setBetDay(e.target.value)} className="h-8 rounded-lg border border-line bg-surface px-2 text-xs"/>
            {(betProv||betDay) && <button onClick={()=>{setBetProv("");setBetDay("");}} className="h-8 rounded-lg border border-line px-2 text-xs text-muted hover:bg-sunken">ล้าง</button>}
            <button onClick={()=>setBetModal(true)} className="ml-auto flex h-8 items-center gap-1 rounded-lg border border-accent/40 bg-accent-soft px-2.5 text-xs font-medium text-accent hover:bg-accent-soft"><Ic d={I.search} size={13}/> ค้นหาย้อนหลังทั้งหมด</button>
          </div>}
          <div className="max-h-72 divide-y divide-line-soft overflow-y-auto rounded-lg border border-line">
            {histData(htab).length===0 ? <div className="px-3 py-6 text-center text-xs text-muted">ไม่มีรายการ</div>
              : histData(htab).slice(0,100).map((x,i)=>histRow(htab,x,i))}
          </div>
        </div>
      </div>)}
      <div className="border-t border-line p-3">
        <div className="grid grid-cols-3 gap-2">
          {canCredit && <button onClick={()=>onAdjust({id:userId,name:nm,username:u.username})} className="flex h-10 items-center justify-center gap-1.5 rounded-lg bg-accent text-xs font-semibold text-accent-fg hover:bg-accent-h"><Ic d={I.coins} size={15}/> ปรับเครดิต</button>}
          {canU && <button onClick={()=>onEdit({id:userId,name:nm,username:u.username,full_name:u.full_name,phone:u.phone,phone_hidden:u.phone_hidden,bank_code:u.bank_code,bank_account_no:u.bank_account_no,bank_account_name:u.bank_account_name})} className="flex h-10 items-center justify-center gap-1.5 rounded-lg border border-line text-xs font-medium text-fg-2 hover:bg-sunken"><Ic d={I.edit} size={15}/> แก้ไข</button>}
          {canU && <button onClick={()=>onPassword({id:userId,name:nm,username:u.username})} className="flex h-10 items-center justify-center gap-1.5 rounded-lg border border-line text-xs font-medium text-fg-2 hover:bg-sunken"><Ic d={I.key} size={15}/> รหัสผ่าน</button>}
          {canU && <button disabled={busyA} onClick={doStatus} className="flex h-10 items-center justify-center gap-1.5 rounded-lg border text-xs font-medium disabled:opacity-50" style={(u.status==="suspended")?{borderColor:"var(--success)",color:"var(--success)"}:{borderColor:"var(--warn)",color:"var(--warn)"}}><Ic d={I.ban} size={15}/> {u.status==="suspended"?"ปลดระงับ":"ระงับ"}</button>}
          {can("tool_clear_turnover") && <button disabled={busyA} onClick={doClearTurnover} className="flex h-10 items-center justify-center gap-1.5 rounded-lg border border-line text-xs font-medium text-fg-2 hover:bg-sunken disabled:opacity-50"><Ic d={I.broom} size={15}/> ล้างเทิร์น</button>}
          {canAny(["tool_clear_turnover","tool_manage_credit","users"]) && Number(w.turnover_required||0)>0 && <button disabled={busyA} onClick={()=>setSimOpen(true)} className="flex h-10 items-center justify-center gap-1.5 rounded-lg border text-xs font-medium disabled:opacity-50" style={{borderColor:"var(--accent)",color:"var(--accent)"}}><Ic d={I.refresh} size={15}/> จำลองเทิร์น</button>}
          {canCredit && <button disabled={busyA} onClick={doSync} className="flex h-10 items-center justify-center gap-1.5 rounded-lg border border-line text-xs font-medium text-fg-2 hover:bg-sunken disabled:opacity-50"><Ic d={I.refresh} size={15} cls={busyA?"animate-spin":""}/> ดึงเครดิต</button>}
          {canU && <button disabled={busyA} onClick={doBlacklist} className="flex h-10 items-center justify-center gap-1.5 rounded-lg border text-xs font-medium disabled:opacity-50" style={u.is_blacklisted?{borderColor:"var(--info)",color:"var(--info)"}:{borderColor:"var(--danger)",color:"var(--danger)"}}><Ic d={I.ban} size={15}/> {u.is_blacklisted?"ปลดแบล็คลิสต์":"แบล็คลิสต์"}</button>}
          {canImp && <button onClick={()=>onImpersonate({id:userId,username:u.username})} className="flex h-10 items-center justify-center gap-1.5 rounded-lg border text-xs font-medium" style={{borderColor:"var(--info)",color:"var(--info)"}}><Ic d={I.login2} size={15}/> เข้าใช้แทน</button>}
          {can("perm_delete_member") && <button disabled={busyA} onClick={doDelete} className="flex h-10 items-center justify-center gap-1.5 rounded-lg border text-xs font-medium disabled:opacity-50" style={{borderColor:"var(--danger)",color:"var(--danger)",background:"var(--danger-soft)"}}><Ic d={I.trash} size={15}/> ลบสมาชิก</button>}
        </div>
      </div>
    </div>
    {betModal && <BetHistoryModal userId={userId} username={u.username} onClose={()=>setBetModal(false)}/>}
    {simOpen && <SimulateTurnoverModal userId={userId} username={nm} required={Number(w.turnover_required||0)} progress={Number(w.turnover_progress||0)} onClose={()=>setSimOpen(false)} onDone={()=>{ reload(); onChanged&&onChanged(); }} toast={toast}/>}
  </div>);
}
/* Popup ประวัติลูกค้าแบบ reusable — เรียกจากหน้าอื่น (เช่นรายการฝาก/ถอน) ด้วย userId
   รวม MemberDrawer + โมดัลปรับเครดิต/แก้ไข/รหัสผ่าน/เข้าใช้แทน ไว้ในที่เดียว จัดการ state เอง */
function UserHistoryPopup({ userId, onClose, toast }) {
  const [adjustRow,setAdjustRow]=useState(null);
  const [editRow,setEditRow]=useState(null);
  const [passwordRow,setPasswordRow]=useState(null);
  const doImpersonate = async (u)=>{ try{ const d=await api("/users/"+u.id+"/impersonate",{method:"POST",body:{}}); const front=String(d.appUrl||location.origin).replace(/\/+$/,""); window.open(front+"/?_impersonate_token="+encodeURIComponent(d.token),"_blank"); toast&&toast("เปิดหน้าเข้าใช้แทนแล้ว"); }catch(e){ toast&&toast(e.message||"ไม่สำเร็จ"); } };
  return (<>
    <MemberDrawer userId={userId} onClose={onClose} onAdjust={(u)=>setAdjustRow(u)} onEdit={(u)=>setEditRow(u)} onPassword={(u)=>setPasswordRow(u)} onImpersonate={doImpersonate} onChanged={()=>{}} toast={toast}/>
    {adjustRow && <CreditAdjustModal user={adjustRow} onClose={()=>setAdjustRow(null)} onDone={()=>{}} toast={toast}/>}
    {editRow && <EditUserModal user={editRow} onClose={()=>setEditRow(null)} onDone={()=>{}} toast={toast}/>}
    {passwordRow && <PasswordModal user={passwordRow} onClose={()=>setPasswordRow(null)} toast={toast}/>}
  </>);
}

function MembersPage({ toast }) {
  const [rows, setRows] = useState([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState(null);
  const [q, setQ] = useState("");
  const [selected, setSelected] = useState(new Set());
  const [detailId, setDetailId] = useState(null);
  const [adjustRow, setAdjustRow] = useState(null);
  const [editRow, setEditRow] = useState(null);
  const [passwordRow, setPasswordRow] = useState(null);
  const [status, setStatus] = useState("");
  const [dep, setDep] = useState("");
  const [page, setPage] = useState(1);
  const [size, setSize] = useState(50);
  const [addOpen, setAddOpen] = useState(false);
  const debRef = useRef(null);
  const canU = can("users"), canCredit = canAny(["tool_manage_credit","users"]), canImp = can("perm_impersonate_member"), canCreate = canAny(["tool_create_member","users"]);
  const reloadRows = ()=>load(q.trim(), status);
  const doStarRow = async (r)=>{ try{ await api("/users/"+r.id+"/star",{method:"PATCH",body:{star:!r.star}}); reloadRows(); }catch(e){toast(e.message||"ไม่สำเร็จ");} };
  const doImpersonate = async (u)=>{ try{ const d=await api("/users/"+u.id+"/impersonate",{method:"POST",body:{}}); const front=String(d.appUrl||location.origin).replace(/\/+$/,""); window.open(front+"/?_impersonate_token="+encodeURIComponent(d.token),"_blank"); toast("เปิดหน้าเข้าใช้แทนแล้ว"); }catch(e){toast(e.message||"ไม่สำเร็จ");} };

  const load = useCallback(async (query, statusVal) => {
    setLoading(true); setErr(null);
    try {
      const qs = "/users?limit=500" + (query ? "&q=" + encodeURIComponent(query) : "") + (statusVal ? "&status=" + encodeURIComponent(statusVal) : "");
      const data = await api(qs);
      const list = Array.isArray(data) ? data : (data && data.rows) || [];
      setRows(list.map(mapUser));
      setSelected(new Set());
    } catch (e) {
      if (e.status === 401 || e.status === 403) { window.__nxAuthFail && window.__nxAuthFail(); }
      setErr(e.message || "โหลดข้อมูลไม่สำเร็จ");
    } finally { setLoading(false); }
  }, []);

  useEffect(() => { load("", status); }, [load, status]);
  useEffect(() => {
    if (debRef.current) clearTimeout(debRef.current);
    debRef.current = setTimeout(() => load(q.trim(), status), 350);
    return () => clearTimeout(debRef.current);
  }, [q, load]);
  useEffect(() => { setPage(1); }, [q, status, dep, size]);

  const filtered = useMemo(() => rows.filter(r => dep==="" ? true : (dep==="yes" ? r.depCount>0 : r.depCount<=0)), [rows, dep]);
  const paged = filtered.slice((page-1)*size, page*size);
  const allOn = paged.length>0 && paged.every(r=>selected.has(r.id));
  const someOn = paged.some(r=>selected.has(r.id)) && !allOn;
  const toggleAll = () => setSelected(allOn ? new Set() : new Set(paged.map(r=>r.id)));
  const doExport = () => { const H=["สมัคร","ชื่อบัญชี","ยูสเซอร์","สายการตลาด","ธนาคาร","เลขบัญชี","ชื่อบัญชีธนาคาร","เบอร์","สถานะ","ฝากเงิน"];
    const body = filtered.map(r=>[r.createdAt?fmtDT(r.createdAt):"", r.name, r.username, r.marketing, r.bankName, r.acct, r.accountName, r.phone, r.banned?"ระงับ":"ปกติ", r.depCount>0?"ฝากแล้ว":"ยังไม่ฝาก"]);
    csvDownload("members-"+bkkDate(0)+".csv",[H,...body]); toast("ส่งออก "+filtered.length+" รายการแล้ว"); };
  const toggleOne = (id) => setSelected(s=>{const n=new Set(s); n.has(id)?n.delete(id):n.add(id); return n;});
  const H = ({children,extra=""}) => (<th className={"whitespace-nowrap px-3 py-2.5 text-left text-[13px] font-bold text-fg-2 "+extra}>{children}</th>);

  return (<div className="space-y-3">
    <div className="flex flex-wrap items-center gap-2">
      <div className="relative flex-1 sm:max-w-xs">
        <span className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted"><Ic d={I.search} size={16}/></span>
        <input value={q} onChange={(e)=>setQ(e.target.value)} placeholder="ค้นหา ชื่อ / เบอร์ / ยูสเซอร์…" className="h-9 w-full rounded-lg border border-line bg-surface pl-9 pr-3 text-sm text-fg placeholder:text-faint focus:border-accent focus:outline-none focus:ring-4 focus:ring-[var(--accent-ring)]"/>
      </div>
      <select value={status} onChange={(e)=>setStatus(e.target.value)} className="h-9 rounded-lg border border-line bg-surface px-2 text-sm text-fg-2">
        <option value="">ทุกสถานะ</option><option value="suspended">ระงับ</option><option value="blacklisted">แบล็คลิสต์</option></select>
      <select value={dep} onChange={(e)=>setDep(e.target.value)} className="h-9 rounded-lg border border-line bg-surface px-2 text-sm text-fg-2">
        <option value="">ฝาก: ทั้งหมด</option><option value="yes">ฝากแล้ว</option><option value="no">ยังไม่ฝาก</option></select>
      <button onClick={reloadRows} className="flex h-9 items-center gap-1.5 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken"><Ic d={I.refresh} size={16}/> รีเฟรช</button>
      <ExportBtn onClick={doExport}/>
      {canCreate && <button onClick={()=>setAddOpen(true)} className="flex h-9 items-center gap-1.5 rounded-lg bg-accent px-3.5 text-sm font-semibold text-accent-fg shadow-sm transition-colors hover:bg-accent-h"><Ic d={I.plus} size={17} sw={2}/> เพิ่มสมาชิก</button>}
      <div className="ml-auto hidden text-xs text-muted sm:block tnum">แสดง <span className="font-medium text-fg-2">{filtered.length}</span> รายการ</div>
    </div>

    <div className="overflow-hidden rounded-xl border border-line bg-surface shadow-sm">
      <div className="relative h-14">
        {selected.size>0 ? (
          <div className="fade-in absolute inset-0 flex items-center gap-3 border-b border-line bg-accent-soft px-4">
            <span className="text-sm font-medium text-accent tnum">เลือก {selected.size} รายการ</span>
            <div className="ml-auto flex items-center gap-2">
              <button onClick={()=>toast("ฟังก์ชันนี้จะต่อในเฟสถัดไป")} className="flex h-8 items-center gap-1.5 rounded-lg border border-line bg-surface px-2.5 text-xs font-medium text-fg-2 hover:bg-sunken"><Ic d={I.coins} size={15}/> เติมเครดิต</button>
              <button onClick={()=>setSelected(new Set())} className="grid h-8 w-8 place-items-center rounded-lg text-accent hover:bg-surface"><Ic d={I.x} size={16}/></button>
            </div>
          </div>
        ) : (
          <div className="absolute inset-0 flex items-center gap-2 border-b border-line px-4">
            <div><div className="text-sm font-semibold">สมาชิกทั้งหมด</div><div className="text-xs text-muted tnum">{loading?"กำลังโหลด…":filtered.length+" รายการ"}</div></div>
          </div>
        )}
      </div>

      {err ? (
        <div className="px-4 py-16 text-center">
          <div className="mx-auto mb-2 grid h-10 w-10 place-items-center rounded-full text-danger" style={{background:"var(--danger-soft)"}}><Ic d={I.warn2} size={20}/></div>
          <div className="text-sm font-medium text-fg-2">{err}</div>
          <button onClick={reloadRows} className="mt-3 rounded-lg border border-line px-3 py-1.5 text-xs font-medium text-fg-2 hover:bg-sunken">ลองใหม่</button>
        </div>
      ) : (
        <div className="overflow-x-auto"><table className="w-full min-w-[1100px] border-collapse text-sm">
          <thead className="border-b-2 border-accent/25 bg-accent-soft"><tr>
            <th className="w-10 pl-4"><Checkbox checked={allOn} indeterminate={someOn} onChange={toggleAll}/></th>
            <H>สมัคร</H><H>ชื่อบัญชี</H><H>สายการตลาด</H><H>เลขบัญชี</H><H>เบอร์โทรศัพท์</H><H>เข้าสู่ระบบ (7 วันล่าสุด)</H><H>สถานะ</H><H>สถานะฝากเงิน</H><H extra="text-right pr-4">ดูรายละเอียด</H>
          </tr></thead>
          <tbody>
            {loading ? Array.from({length:8}).map((_,i)=>(
              <tr key={i} className="border-b border-line-soft"><td className="px-3 py-3.5" colSpan="10"><div className="skel h-5 w-full"/></td></tr>
            )) : filtered.length===0 ? (
              <tr><td colSpan="10" className="px-4 py-16 text-center"><div className="text-sm font-medium text-fg-2">ไม่พบสมาชิก</div><div className="mt-1 text-xs text-muted">ลองแก้คำค้นหา</div></td></tr>
            ) : paged.map(r=>{
              const on=selected.has(r.id);
              const acts=[
                ["eye","ดูรายละเอียด","#0EA5E9",()=>setDetailId(r.id),true],
                ["copy","คัดลอกเลขบัญชี","#8B5CF6",()=>{try{navigator.clipboard.writeText(r.acct);}catch{} toast("คัดลอกเลขบัญชีแล้ว");},true],
                ["coins","ปรับเครดิต","#10B981",()=>setAdjustRow(r),canCredit],
                ["edit","แก้ไข","#3B82F6",()=>setEditRow(r),canU],
                ["key","รหัสผ่าน","#F59E0B",()=>setPasswordRow(r),canU],
                ["login2","เข้าใช้แทน","#EC4899",()=>doImpersonate(r),canImp],
              ].filter(a=>a[4]);
              return (<tr key={r.id} className={"group border-b border-line-soft transition-colors last:border-0 "+(on?"bg-accent-soft/50":"hover:bg-sunken/50")}>
                <td className="pl-4"><Checkbox checked={on} onChange={()=>toggleOne(r.id)}/></td>
                <td className="whitespace-nowrap px-3 text-[11px] text-muted">{r.createdAt?fmtDT(r.createdAt):"-"}</td>
                <td className="px-3 py-2.5"><div className="flex items-center gap-2.5">
                  <div className="grid h-8 w-8 shrink-0 place-items-center rounded-full text-sm font-semibold text-white" style={{background:avc(r.username||r.name)}}>{initials(r.username||r.name)}</div>
                  <div className="min-w-0"><div className="flex items-center gap-1"><span className="truncate font-semibold text-accent mono">{r.username||("#"+r.id)}</span>
                    {canU && <button onClick={()=>doStarRow(r)} title={r.star?"เอาดาวออก":"ติดดาว"} className="shrink-0"><svg width="13" height="13" viewBox="0 0 24 24" fill={r.star?"#F59E0B":"none"} stroke={r.star?"#F59E0B":"var(--faint)"} strokeWidth="1.8"><path d={I.star[0]}/></svg></button>}
                  </div><div className="truncate text-[11px] text-muted">{r.name!==r.username?r.name:""}</div>
                  {r.ambUsername && <div className="truncate mono text-[10px] text-faint" title="ยูสเซอร์ฝั่ง Agent">{r.ambUsername}</div>}</div>
                </div></td>
                <td className="px-3 text-xs text-fg-2"><span className="truncate">{r.marketing||"-"}</span></td>
                <td className="px-3"><div className="flex items-center gap-2"><BankLogo code={r.bankCode} size={26}/><div className="min-w-0"><div className="mono text-xs text-fg-2">{r.acct}</div><div className="truncate text-[11px] text-faint">{r.accountName||r.bankName}</div></div></div></td>
                <td className="px-3 mono text-xs text-fg-2">{r.phone}</td>
                <td className="px-3"><div className="flex gap-0.5">{DAYS7.map((d,i)=>{const on2=r.loginset.has(d); const dd=d.slice(8,10);
                  return <span key={i} title={d} className="grid h-5 w-5 place-items-center rounded-full text-[10px] font-semibold tnum" style={{background: on2?"var(--success-soft)":"var(--danger-soft)", color: on2?"var(--success)":"var(--danger)"}}>{dd}</span>;})}</div></td>
                <td className="px-3">{r.banned?<Badge tone="danger">ระงับ</Badge>:<Badge tone="success">ยืนยันแล้ว</Badge>}</td>
                <td className="px-3">{r.depCount>0?<Badge tone="success">ฝากแล้ว</Badge>:<Badge tone="warn">ยังไม่ฝาก</Badge>}</td>
                <td className="pr-4"><div className="flex items-center justify-end gap-1">
                  {acts.map(([ic,tt,c,fn])=>(<button key={ic} title={tt} onClick={fn}
                    className="grid h-7 w-7 place-items-center rounded-lg border border-line bg-surface transition-colors hover:bg-sunken" style={{color:c}}><Ic d={I[ic]} size={14}/></button>))}
                </div></td>
              </tr>);
            })}
          </tbody>
        </table></div>
      )}
      {!err && !loading && <Pager page={page} setPage={setPage} total={filtered.length} size={size} setSize={setSize}/>}
    </div>

    {detailId && <MemberDrawer userId={detailId} onClose={()=>setDetailId(null)} onAdjust={(u)=>setAdjustRow(u)} onEdit={(u)=>setEditRow(u)} onPassword={(u)=>setPasswordRow(u)} onImpersonate={doImpersonate} onChanged={reloadRows} toast={toast}/>}
    {adjustRow && <CreditAdjustModal user={adjustRow} onClose={()=>setAdjustRow(null)} onDone={reloadRows} toast={toast}/>}
    {editRow && <EditUserModal user={editRow} onClose={()=>setEditRow(null)} onDone={reloadRows} toast={toast}/>}
    {passwordRow && <PasswordModal user={passwordRow} onClose={()=>setPasswordRow(null)} toast={toast}/>}
    {addOpen && <AddUserModal onClose={()=>setAddOpen(false)} onDone={reloadRows} toast={toast}/>}
  </div>);
}

/* ROUND ID / TXID ของรายการเล่น — กดเพื่อคัดลอกไปเทียบกับหลังบ้านค่ายได้ทันที */
function BetIdCell({ value }) {
  const [copied, setCopied] = React.useState(false);
  const v = value === null || value === undefined ? "" : String(value);
  if (!v) return <span className="text-faint">-</span>;
  const copy = () => {
    try { navigator.clipboard.writeText(v); } catch (_) {}
    setCopied(true); setTimeout(() => setCopied(false), 1200);
  };
  return (<button type="button" onClick={copy} title={copied ? "คัดลอกแล้ว" : "กดเพื่อคัดลอก"}
    className="block max-w-[230px] break-all text-left font-mono text-[11px] leading-snug text-fg-2 transition-colors hover:text-accent">
    {copied ? <span className="text-success">คัดลอกแล้ว</span> : v}
  </button>);
}

/* ---------- ประวัติการเล่นเต็ม — /users/:id/bet-history ---------- */
const b2 = (v) => Number(v||0).toLocaleString("th-TH",{minimumFractionDigits:2,maximumFractionDigits:2});
const BET_SYS = [["amb","Agent"],["pghard","PG Hard"],["ufa","UFA"],["rb7","RB7"],["onesport","OneSport"]];
function betStatus(r){ const s=String(r.status||"").toLowerCase(); if(s==="win"||Number(r.net)>0) return {t:"ได้",c:"success"}; if(s==="lose"||Number(r.net)<0) return {t:"เสีย",c:"danger"}; return {t:r.action||"เสมอ",c:"info"}; }

/* ปุ่มรูปตา "ดูรายละเอียดเกม" — เงื่อนไขเดียวกับหลังบ้านเก่า (users.js) ต้องมี ref พอให้ค่ายเปิดรายละเอียดได้ */
function betCanViewDetail(r, fallbackSource) {
  const src = String(r.source || fallbackSource || "").toLowerCase();
  if (src==="amb") return !!(r.ref1 || r.roundId || r.gameId);
  if (src==="pghard") return !!(r.gameId && (r.roundId || r.mainId));
  if (src==="onesport" || src==="rb7") return !!(r.transactionId || r.roundId);
  return false;
}
const BET_DETAIL_SLOT_WORDS = ["slot","fishing","fish","keno","poker","arcade"];

/* รายละเอียดเกมจากค่าย: POST /users/:id/game-detail -> { type:"iframe", url } | { type:"table", data } */
function BetGameDetailModal({ userId, bet, source, onClose }) {
  const [res,setRes]=useState(null), [err,setErr]=useState(null);
  const src = String(bet.source || source || "").toLowerCase();
  const slowData = BET_DETAIL_SLOT_WORDS.some(w=>String(bet.gameName||bet.provider||"").toLowerCase().includes(w));
  useEffect(()=>{ let alive=true;
    api("/users/"+userId+"/game-detail",{method:"POST",body:{
      source:src, gameId:bet.gameId||null, roundId:bet.roundId||null, ref1:bet.ref1||bet.roundId||null,
      ref2:bet.ref2||null, ref3:bet.ref3||null, betTime:bet.betTime||null,
      mainId:bet.mainId||bet.roundId||null, transactionId:bet.transactionId||null,
    }}).then(d=>{ if(alive) setRes(d||{}); }).catch(e=>{ if(alive) setErr(e); });
    return ()=>{ alive=false; }; },[userId,bet,src]);
  useEffect(()=>{const h=(e)=>e.key==="Escape"&&onClose();document.addEventListener("keydown",h);return ()=>document.removeEventListener("keydown",h);},[onClose]);
  // 429 = ค่ายให้รอ, 404 = ค่ายไม่มีรายละเอียดของรายการนี้ (เช่น PG เกิน 3-4 วัน) — เป็นข้อมูลแจ้ง ไม่ใช่ระบบพัง
  const rateLimited = err && (err.status===429 || err.status===404 || (err.data&&err.data.errorCode===100017));
  const tableRows = res && res.type==="table" && res.data ? Object.entries(res.data).filter(([,v])=>v!==null&&v!==undefined&&typeof v!=="object") : [];
  return ReactDOM.createPortal(<div className="fixed inset-0 z-[90] flex items-center justify-center p-3 fade-in" onMouseDown={onClose}>
    <div className="absolute inset-0 bg-black/60"/>
    <div className="pop relative flex h-[88vh] w-full max-w-6xl flex-col overflow-hidden rounded-2xl border border-line bg-surface2 shadow-lg" onMouseDown={(e)=>e.stopPropagation()}>
      <div className="flex h-12 items-center gap-2 border-b border-line px-4">
        <Ic d={I.eye} size={16} cls="text-accent"/><div className="min-w-0 truncate text-sm font-semibold">รายละเอียดเกม · {bet.gameName||bet.provider||"-"}</div>
        {res&&res.type==="iframe"&&res.url&&<a href={res.url} target="_blank" rel="noopener noreferrer" className="ml-auto rounded-lg border border-line px-2.5 py-1 text-xs font-medium text-fg-2 transition-colors hover:bg-sunken">เปิดในแท็บใหม่</a>}
        <button onClick={onClose} className={(res&&res.type==="iframe"&&res.url?"":"ml-auto ")+"grid h-8 w-8 place-items-center rounded-lg text-muted hover:bg-sunken"}><Ic d={I.x}/></button>
      </div>
      {slowData&&<div className="border-b border-line bg-warn-soft px-4 py-1.5 text-xs text-warn">ข้อมูล Slot / Fishing / Keno / Poker อาจล่าช้า 150-300 วินาที</div>}
      <div className="min-h-0 flex-1 overflow-auto">
        {err ? <div className={"px-6 py-16 text-center text-sm "+(rateLimited?"text-warn":"text-danger")}>{err.message||(rateLimited?"กรุณารอ 1 นาที แล้วลองใหม่":"โหลดรายละเอียดไม่สำเร็จ")}</div>
          : !res ? <div className="flex h-full items-center justify-center gap-2 text-sm text-muted"><Ic d={I.refresh} size={16} cls="animate-spin"/> กำลังโหลดรายละเอียด…</div>
          : res.type==="iframe"&&res.url ? <iframe src={res.url} title="รายละเอียดเกม" className="h-full w-full border-0 bg-black" sandbox="allow-scripts allow-same-origin allow-popups" referrerPolicy="no-referrer"/>
          : tableRows.length ? <table className="w-full text-sm"><tbody>{tableRows.map(([k,v])=>(<tr key={k} className="border-b border-line-soft"><td className="whitespace-nowrap px-4 py-2 text-xs font-semibold text-fg-2">{k}</td><td className="px-4 py-2 text-xs mono">{String(v)}</td></tr>))}</tbody></table>
          : <div className="py-16 text-center text-sm text-muted">ไม่พบข้อมูลรายละเอียดของรายการนี้</div>}
      </div>
    </div>
  </div>, document.body);
}
function BetHistoryModal({ userId, username, onClose }) {
  const [source,setSource]=useState("amb");
  const [provider,setProvider]=useState("");
  const [from,setFrom]=useState(bkkDate(6));
  const [to,setTo]=useState(bkkDate(0));
  const [fromTime,setFromTime]=useState("00:00");
  const [toTime,setToTime]=useState("23:59");
  const [pageSize,setPageSize]=useState(100);
  const [page,setPage]=useState(1);
  const [data,setData]=useState(null);
  const [loading,setLoading]=useState(true);
  const [err,setErr]=useState(null);
  const [detailBet,setDetailBet]=useState(null);
  const load=useCallback(async(src,prov,f,t,pg,ft,tt,size)=>{
    setLoading(true); setErr(null);
    try{ const p=new URLSearchParams({page:String(pg||1),limit:String(size||100),source:src,startDate:f,endDate:t}); if(prov)p.set("provider",prov);
      if(ft&&ft!=="00:00")p.set("startTime",ft); if(tt&&tt!=="23:59")p.set("endTime",tt);
      const d=await api("/users/"+userId+"/bet-history?"+p.toString()); setData(d);
      if(d && d.enabledProviders && !d.enabledProviders[src]){ const first=BET_SYS.find(([k])=>d.enabledProviders[k]); if(first){ setSource(first[0]); } }
    }catch(e){ setErr(e.message||"โหลดไม่สำเร็จ"); }finally{ setLoading(false); }
  },[userId]);
  useEffect(()=>{ load(source,provider,from,to,page,fromTime,toTime,pageSize); },[source,page,pageSize]);
  useEffect(()=>{const h=(e)=>e.key==="Escape"&&onClose();document.addEventListener("keydown",h);return ()=>document.removeEventListener("keydown",h);},[onClose]);
  const search=()=>{ setPage(1); load(source,provider,from,to,1,fromTime,toTime,pageSize); };
  const enabled=(data&&data.enabledProviders)||{};
  const sysTabs=BET_SYS.filter(([k])=> enabled[k]);
  const cur=(data&&data[source])||{data:[],total:0,page:1,totalPages:0};
  const providers=(data&&data.providers)||[];
  const inp="h-9 w-full rounded-lg border border-line bg-sunken px-2 text-sm text-fg focus:border-accent focus:bg-surface focus:outline-none";
  const Th=({children,extra=""})=>(<th className={"whitespace-nowrap px-3 py-2 text-left text-[13px] font-bold text-fg-2 "+extra}>{children}</th>);
  return (<div className="fixed inset-0 z-[85] flex items-center justify-center p-3 fade-in" onMouseDown={onClose}>
    <div className="absolute inset-0 bg-black/60"/>
    <div className="pop relative flex max-h-[92vh] w-full max-w-5xl flex-col overflow-hidden rounded-2xl border border-line bg-surface2 shadow-lg" onMouseDown={(e)=>e.stopPropagation()}>
      <div className="flex h-14 items-center gap-3 border-b border-line px-5">
        <Ic d={I.game} size={18} cls="text-accent"/><div className="text-[15px] font-semibold">ประวัติการเล่น · {username||""}</div>
        <button onClick={onClose} className="ml-auto grid h-8 w-8 place-items-center rounded-lg text-muted hover:bg-sunken"><Ic d={I.x}/></button>
      </div>
      <div className="border-b border-line p-4">
        <div className="grid grid-cols-2 gap-2 sm:grid-cols-6">
          <label className="block"><span className="text-[11px] font-medium text-muted">วันที่เริ่มต้น</span><input type="date" value={from} onChange={(e)=>setFrom(e.target.value)} className={inp}/></label>
          <label className="block"><span className="text-[11px] font-medium text-muted">เวลาเริ่ม</span><input type="time" value={fromTime} onChange={(e)=>setFromTime(e.target.value||"00:00")} className={inp}/></label>
          <label className="block"><span className="text-[11px] font-medium text-muted">วันที่สิ้นสุด</span><input type="date" value={to} onChange={(e)=>setTo(e.target.value)} className={inp}/></label>
          <label className="block"><span className="text-[11px] font-medium text-muted">เวลาสิ้นสุด</span><input type="time" value={toTime} onChange={(e)=>setToTime(e.target.value||"23:59")} className={inp}/></label>
          <label className="block"><span className="text-[11px] font-medium text-muted">ค่ายเกม</span><select value={provider} onChange={(e)=>setProvider(e.target.value)} className={inp}><option value="">ทุกค่าย{providers.length?" ("+providers.reduce((s,p)=>s+Number(p.count||0),0).toLocaleString()+")":""}</option>{providers.map(p=><option key={p.provider} value={p.provider}>{p.label||p.provider} ({Number(p.count||0).toLocaleString()})</option>)}</select></label>
          <div className="flex items-end"><button onClick={search} className="flex h-9 w-full items-center justify-center gap-1.5 rounded-lg bg-accent text-sm font-semibold text-accent-fg hover:bg-accent-h"><Ic d={I.search} size={16}/> ค้นหา</button></div>
        </div>
        {sysTabs.length>1 && <div className="mt-3 flex flex-wrap gap-1 rounded-lg border border-line bg-sunken p-0.5">
          {sysTabs.map(([k,l])=>(<button key={k} onClick={()=>{setSource(k); setPage(1);}} className={"rounded-md px-3 py-1 text-xs font-medium transition-colors "+(source===k?"bg-accent text-accent-fg shadow-sm":"text-fg-2 hover:text-fg")}>{l}<span className={"ml-1 text-[10px] "+(source===k?"opacity-80":"text-faint")}>{(data&&data[k]&&data[k].total)||0}</span></button>))}
        </div>}
      </div>
      <div className="flex-1 overflow-auto">
        {err ? <div className="py-16 text-center text-sm text-fg-2">{err}</div>
          : loading ? <div className="space-y-2 p-4">{Array.from({length:8}).map((_,i)=><div key={i} className="skel h-7 w-full"/>)}</div>
          : cur.data.length===0 ? <div className="py-16 text-center text-sm text-muted">ไม่มีรายการเล่นในช่วงนี้</div>
          : (<table className="w-full min-w-[1240px] border-collapse text-sm">
            <thead className="sticky top-0 border-b border-line bg-surface2"><tr>
              <Th>เวลา</Th><Th>ค่ายเกม</Th><Th>ชื่อเกม</Th><Th extra="text-right">ยอดเดิมพัน</Th><Th extra="text-right">ได้รับ</Th><Th extra="text-right">ก่อนแทง</Th><Th extra="text-right">หลังแทง</Th><Th>ROUND ID</Th><Th>TXID</Th><Th extra="w-10 text-center">ดู</Th><Th>สถานะ</Th>
            </tr></thead>
            <tbody>{cur.data.map((r,i)=>{ const st=betStatus(r); return (<tr key={i} className="border-b border-line-soft hover:bg-sunken/40">
              <td className="whitespace-nowrap px-3 py-1.5 text-xs text-fg-2">{fmtDT(r.createdAt)}</td>
              <td className="px-3"><span className="rounded-md border border-line bg-surface px-1.5 py-0.5 text-[11px] font-medium">{r.providerCode||r.provider||"-"}</span></td>
              <td className="px-3 text-xs text-fg-2"><span className="line-clamp-1">{r.gameName||"-"}</span></td>
              <td className="whitespace-nowrap px-3 text-right tnum">{b2(r.betAmount)}</td>
              <td className="whitespace-nowrap px-3 text-right tnum" style={{color:Number(r.payoff)>0?"var(--success)":undefined}}>{b2(r.payoff)}</td>
              <td className="whitespace-nowrap px-3 text-right tnum text-muted">{b2(r.beforeCredit)}</td>
              <td className="whitespace-nowrap px-3 text-right tnum text-muted">{b2(r.afterCredit)}</td>
              <td className="px-3"><BetIdCell value={r.roundId}/></td>
              <td className="px-3"><BetIdCell value={r.txId||r.transactionId}/></td>
              <td className="px-2 text-center">{betCanViewDetail(r,source)
                ? <button onClick={()=>setDetailBet(r)} title="ดูรายละเอียด" className="inline-grid h-7 w-7 place-items-center rounded-lg border border-line bg-surface text-info transition-colors hover:border-accent hover:bg-sunken"><Ic d={I.eye} size={14}/></button>
                : <span className="text-faint">-</span>}</td>
              <td className="px-3"><Badge tone={st.c}>{st.t}</Badge>{r.action && <span className="ml-1 text-[9px] text-faint">{r.action}</span>}</td>
            </tr>);})}</tbody>
          </table>)}
      </div>
      {detailBet && <BetGameDetailModal userId={userId} bet={detailBet} source={source} onClose={()=>setDetailBet(null)}/>}
      <div className="flex items-center gap-2 border-t border-line px-4 py-2.5 text-xs">
        <span className="text-muted tnum">ทั้งหมด {Number(cur.total||0).toLocaleString()} รายการ</span>
        <label className="ml-2 flex items-center gap-1 text-muted">แสดง
          <select value={pageSize} onChange={(e)=>{ setPage(1); setPageSize(Number(e.target.value)); }} className="h-7 rounded-lg border border-line bg-surface px-1.5 text-xs text-fg focus:border-accent focus:outline-none">
            {[20,100,200].map(n=><option key={n} value={n}>{n}</option>)}
          </select>รายการ/หน้า</label>
        <div className="ml-auto flex items-center gap-1">
          <label className="mr-1 flex items-center gap-1 text-muted">หน้า
            <select value={Math.min(page,cur.totalPages||1)} onChange={(e)=>setPage(Number(e.target.value))} className="h-7 rounded-lg border border-line bg-surface px-1.5 text-xs text-fg tnum focus:border-accent focus:outline-none">
              {Array.from({length:Math.max(cur.totalPages||1,1)},(_,i)=><option key={i+1} value={i+1}>{i+1}</option>)}
            </select>/ {cur.totalPages||1}</label>
          <button disabled={page<=1} onClick={()=>setPage(1)} className="grid h-7 w-7 place-items-center rounded-lg border border-line bg-surface disabled:opacity-40"><Ic d={["M11 6l-6 6 6 6","M18 6l-6 6 6 6"]} size={13}/></button>
          <button disabled={page<=1} onClick={()=>setPage(page-1)} className="grid h-7 w-7 place-items-center rounded-lg border border-line bg-surface disabled:opacity-40"><Ic d={I.back} size={13}/></button>
          <button disabled={page>=(cur.totalPages||1)} onClick={()=>setPage(page+1)} className="grid h-7 w-7 place-items-center rounded-lg border border-line bg-surface disabled:opacity-40"><Ic d={I.chevR} size={13}/></button>
          <button disabled={page>=(cur.totalPages||1)} onClick={()=>setPage(cur.totalPages||1)} className="grid h-7 w-7 place-items-center rounded-lg border border-line bg-surface disabled:opacity-40"><Ic d={["M13 6l6 6-6 6","M6 6l6 6-6 6"]} size={13}/></button>
        </div>
      </div>
    </div>
  </div>);
}
