/* ============================================================
   app.jsx — ระบบจัดการสอนแทน (login + มุมมองต่างๆ + Web Push Notification)
   ============================================================ */
const {useState, useEffect, useRef} = React;
const LS_KEY = 'ntk_substitute_v2';
const CLOUD = (typeof window!=='undefined' && window.db && window.db.isEnabled && window.db.isEnabled());

const nowTime = ()=>{ const t=new Date(); return String(t.getHours()).padStart(2,'0')+':'+String(t.getMinutes()).padStart(2,'0'); };
const timeOf = iso => iso && iso.length>=16 ? iso.slice(11,16) : nowTime();
let _nseq = 0;
const nId = ()=> 'n'+Date.now()+'_'+(++_nseq);

function seedNotifications(seed){
  const out=[];
  seed.forEach(a=>{
    out.push({id:nId(), toTeacherId:a.substituteId, type:'request', text:buildRequestMsg(a), time:timeOf(a.createdAt), assignmentId:a.id});
  });
  // คำขออนุญาตลาตัวอย่าง: กลุ่ม gA รอรองฯ วิชาการพิจารณา
  const gA = seed.filter(a=>a.groupId==='gA');
  if(gA.length) out.push({id:nId(), toTeacherId:DEFAULT_APPROVER_ROLES['deputy_academic'], type:'approval', text:buildApprovalMsg(gA,'deputy_academic'), time:'09:20', groupId:'gA'});
  return out;
}
function seedApprovals(seed){
  const ap = {};
  if(seed.some(a=>a.groupId==='gA')){
    ap['gA'] = { requestedAt:'2026-06-01T09:20', current:0,
      chain:APPROVAL_CHAIN.map(r=>({role:r,status:'pending',at:null,note:''})), final:'pending' };
  }
  return ap;
}
const defaultSchedules = ()=> Object.fromEntries(TEACHERS.map(t=>[t.id, t.schedule]));
const defaultDuties = ()=> Object.fromEntries(TEACHERS.map(t=>[t.id, (t.duties||[]).slice()]));

function loadState(){
  try{ const raw=localStorage.getItem(LS_KEY); if(raw){ const s=JSON.parse(raw); if(s&&s.assignments) return s; } }catch(e){}
  return { session:null, view:'leave', assignments:SEED.slice(), notifications:seedNotifications(SEED), seen:[], schedules:defaultSchedules(), duties:defaultDuties(), credentials:defaultCredentials(), extraTeachers:[], teacherEdits:{}, pushSubs:{}, chairs:{...DEFAULT_CHAIRS}, approvals:seedApprovals(SEED), approverRoles:{...DEFAULT_APPROVER_ROLES} };
}

function App(){
  const init = loadState();
  (init.extraTeachers||[]).forEach(registerTeacher);
  Object.entries(init.teacherEdits||{}).forEach(([id,patch])=>applyTeacherEdit(teacherById(id), patch));
  const [session, setSession] = useState(init.session);
  const [view, setView] = useState(init.view);
  const [assignments, setAssignments] = useState(init.assignments);
  const [notifications, setNotifications] = useState(init.notifications);
  const [seen, setSeen] = useState(init.seen||[]);
  const [schedules, setSchedules] = useState(init.schedules || defaultSchedules());
  const [duties, setDuties] = useState(init.duties || defaultDuties());
  const [credentials, setCredentials] = useState(init.credentials || defaultCredentials());
  const [extraTeachers, setExtraTeachers] = useState(init.extraTeachers || []);
  const [teacherEdits, setTeacherEdits] = useState(init.teacherEdits || {});
  const [pushSubs, setPushSubs] = useState(init.pushSubs || {});
  const [chairs, setChairs] = useState(init.chairs || {});
  const [approvals, setApprovals] = useState(init.approvals || {});
  const [approverRoles, setApproverRoles] = useState(init.approverRoles || {...DEFAULT_APPROVER_ROLES});
  const [acct, setAcct] = useState(false);
  const [notifOpen, setNotifOpen] = useState(false);
  const [permission, setPermission] = useState(typeof Notification!=='undefined' ? Notification.permission : 'unsupported');
  const [toast, setToast] = useState(null);
  const [cloudReady, setCloudReady] = useState(!CLOUD);   // ถ้าไม่เปิด cloud พร้อมทันที
  const [cloudEmpty, setCloudEmpty] = useState(false);    // DB ว่าง รอ admin กดเริ่มต้นข้อมูล
  const [lineSettings, setLineSettings] = useState({token:'',secret:'',enabled:false});
  const [lineUserIds, setLineUserIds] = useState({});     // map teacherId -> LINE userId
  const prevSnapRef = useRef(null);

  // โหลด state ทั้งหมดจาก cloud ครั้งแรก
  useEffect(()=>{
    if(!CLOUD) return;
    let dead = false;
    (async()=>{
      try{
        const data = await window.db.loadAll();
        if(dead) return;
        if(!data){ setCloudEmpty(true); setCloudReady(true); return; }
        setAssignments(data.assignments);
        setNotifications(data.notifications);
        setSeen(data.seen);
        setSchedules(data.schedules);
        setDuties(data.duties);
        setCredentials(data.credentials);
        setExtraTeachers(data.extraTeachers);
        setTeacherEdits(data.teacherEdits);
        setPushSubs(data.pushSubs);
        setChairs(data.chairs);
        setApprovals(data.approvals);
        setApproverRoles(data.approverRoles);
        if(data.lineSettings) setLineSettings(data.lineSettings);
        if(data.lineUserIds) setLineUserIds(data.lineUserIds);
        prevSnapRef.current = data;
        setCloudReady(true);
      }catch(e){
        console.error('[cloud load failed]', e);
        setToast({msg:'โหลดข้อมูลจาก cloud ไม่สำเร็จ — ใช้ข้อมูลในเครื่องชั่วคราว', icon:'xCircle'});
        setCloudReady(true);
      }
    })();
    return ()=>{ dead = true; };
  // eslint-disable-next-line
  },[]);

  // persist: localStorage เสมอ + cloud (diff) เมื่อพร้อม
  useEffect(()=>{
    const state = {assignments,notifications,seen,schedules,duties,credentials,extraTeachers,teacherEdits,pushSubs,chairs,approvals,approverRoles};
    localStorage.setItem(LS_KEY, JSON.stringify({session,view,...state}));
    if(CLOUD && cloudReady){
      window.db.persistDiff(prevSnapRef.current, state).catch(e=>console.error('[cloud persist failed]', e));
      prevSnapRef.current = state;
    }
  },[session,view,assignments,notifications,seen,schedules,duties,credentials,extraTeachers,teacherEdits,pushSubs,chairs,approvals,approverRoles,cloudReady]);

  // realtime: เมื่อ client อื่นเปลี่ยน → reload (debounce 400ms)
  useEffect(()=>{
    if(!CLOUD || !cloudReady) return;
    let t = null;
    const reload = async()=>{
      try{
        const data = await window.db.loadAll();
        if(!data) return;
        setAssignments(data.assignments);
        setNotifications(data.notifications);
        setSeen(data.seen);
        setSchedules(data.schedules);
        setDuties(data.duties);
        setCredentials(data.credentials);
        setExtraTeachers(data.extraTeachers);
        setTeacherEdits(data.teacherEdits);
        setPushSubs(data.pushSubs);
        setChairs(data.chairs);
        setApprovals(data.approvals);
        setApproverRoles(data.approverRoles);
        if(data.lineSettings) setLineSettings(data.lineSettings);
        if(data.lineUserIds) setLineUserIds(data.lineUserIds);
        prevSnapRef.current = data;
      }catch(e){ console.error('[realtime reload]', e); }
    };
    const unsub = window.db.subscribeRealtime(()=>{ clearTimeout(t); t = setTimeout(reload, 400); });
    return ()=>{ clearTimeout(t); unsub(); };
  },[cloudReady]);
  useEffect(()=>{ if(!toast) return; const t=setTimeout(()=>setToast(null),3400); return ()=>clearTimeout(t); },[toast]);

  const fireToast = (msg, icon)=> setToast({msg, icon});
  // ---- Web Push helpers ----
  const fireBrowserNotif = (title, body)=>{
    try{ if(typeof Notification!=='undefined' && Notification.permission==='granted'){ new Notification(title, {body, icon:'logo.png', badge:'logo.png'}); } }catch(e){}
  };
  const pushNote = (note)=>{
    setNotifications(ns=>[...ns,{id:nId(),time:nowTime(),...note}]);
    // ถ้าผู้รับคือผู้ใช้ที่ล็อกอินอยู่และเปิดแจ้งเตือนไว้ → เด้ง Browser Notification จริง
    if(session && note.toTeacherId===session.teacherId && pushSubs[note.toTeacherId]){
      const meta = (typeof notifMeta==='function') ? notifMeta(note) : {title:'การแจ้งเตือนใหม่'};
      fireBrowserNotif(meta.title, note.text);
    }
    // LINE push: ยิงผ่าน Edge Function (fire-and-forget) — function จะข้ามเองถ้ายังปิดใช้/ไม่ผูกบัญชี
    if(CLOUD && note.toTeacherId && note.text){
      window.db.lineNotify([note.toTeacherId], note.text).catch(()=>{});
    }
  };
  const updateAssign = (id,patch)=> setAssignments(arr=>arr.map(a=>a.id===id?{...a,...patch}:a));
  // เมื่อล็อกอิน: ถ้าเปิดแจ้งเตือนไว้และมีรายการที่ยังไม่ได้อ่าน → เด้งสรุป Browser Notification
  useEffect(()=>{
    if(!session || session.role!=='teacher') return;
    const tid = session.teacherId;
    if(!pushSubs[tid] || typeof Notification==='undefined' || Notification.permission!=='granted') return;
    const unseen = notifications.filter(n=>n.toTeacherId===tid && !seen.includes(n.id));
    if(unseen.length){ fireBrowserNotif(`คุณมีการแจ้งเตือนใหม่ ${unseen.length} รายการ`, unseen[unseen.length-1].text.slice(0,100)); }
  // eslint-disable-next-line
  },[session && session.teacherId]);

  // ---- loading จาก cloud ครั้งแรก ----
  if(CLOUD && !cloudReady){
    return (
      <div style={{minHeight:'60vh',display:'flex',alignItems:'center',justifyContent:'center',gap:10}}>
        <span className="ds"></span>
        <span style={{color:'var(--ink-2)',fontFamily:'Prompt,sans-serif'}}>กำลังโหลดข้อมูลจากคลาวด์…</span>
      </div>
    );
  }

  // ---- เริ่มต้นข้อมูลคลาวด์ (admin login มาแล้วเจอ DB ว่าง) ----
  const handleCloudSeed = async ()=>{
    try{
      await window.db.seed();
      const data = await window.db.loadAll();
      if(data){
        setAssignments(data.assignments); setNotifications(data.notifications); setSeen(data.seen);
        setSchedules(data.schedules); setDuties(data.duties); setCredentials(data.credentials);
        setExtraTeachers(data.extraTeachers); setTeacherEdits(data.teacherEdits);
        setPushSubs(data.pushSubs); setChairs(data.chairs); setApprovals(data.approvals);
        setApproverRoles(data.approverRoles); prevSnapRef.current = data;
      }
      setCloudEmpty(false);
      fireToast('เริ่มต้นข้อมูลในคลาวด์เรียบร้อย ✓','checkCircle');
    }catch(e){
      console.error('[seed failed]', e);
      fireToast('เริ่มต้นข้อมูลไม่สำเร็จ — ตรวจสอบ schema ใน Supabase','xCircle');
    }
  };

  // ---- login ----
  if(!session){
    return <LoginView credentials={credentials} approverRoles={approverRoles} onLogin={(res)=>{ setSession(res); setView(res.role==='admin'?'admin':'leave'); }}/>;
  }
  const role = session.role;
  const isAdmin = role==='admin';
  const isTeacher = role==='teacher';
  const meT = isTeacher ? teacherById(session.teacherId) : null;
  const myApproverRole = meT ? roleOfTeacher(approverRoles, meT.id) : null;
  const isApprover = !!myApproverRole;
  const approver = isApprover ? {...APPROVER_BY_ROLE[myApproverRole], role:myApproverRole, teacher:meT} : null;
  const accountId = isTeacher ? (meT && meT.id) : 'admin';
  const myMsgs = isTeacher ? notifications.filter(n=>n.toTeacherId===accountId) : [];
  const unread = myMsgs.filter(n=>!seen.includes(n.id)).length;
  const pendingForMe = meT ? assignments.filter(a=>a.substituteId===meT.id && a.status==='pending').length : 0;
  // approver: จำนวนคำขอรอพิจารณา
  let pendingApprovals = 0;
  if(isApprover){
    const grp = groupAssignments(assignments);
    pendingApprovals = Object.keys(grp).filter(gid=>{ const ap=approvals[gid]; return ap && ap.final==='pending' && APPROVAL_CHAIN[ap.current]===myApproverRole; }).length;
  }

  // ---- actions ----
  const handleSubmit = (list)=>{
    setAssignments(a=>[...a,...list]);
    list.forEach(a=>{
      if(a.status==='awaiting_chair'){
        const chairId = chairs[a.chairGroup];
        if(chairId) pushNote({toTeacherId:chairId, type:'chair_request', text:buildChairDelegateMsg(a), assignmentId:a.id});
      } else {
        pushNote({toTeacherId:a.substituteId, type:'request', text:buildRequestMsg(a), assignmentId:a.id});
      }
    });

    // ---- เริ่ม approval chain อัตโนมัติ → แจ้งรองฯ วิชาการทันที ----
    const groupIds = [...new Set(list.map(a=>a.groupId).filter(Boolean))];
    const newGroupIds = groupIds.filter(gid=>!approvals[gid]);
    if(newGroupIds.length){
      setApprovals(prev=>{
        const next = {...prev};
        newGroupIds.forEach(gid=>{
          next[gid] = {
            requestedAt: new Date().toISOString().slice(0,16),
            current: 0,
            chain: APPROVAL_CHAIN.map(r=>({role:r,status:'pending',at:null,note:''})),
            final: 'pending',
          };
        });
        return next;
      });
      const tidAcademic = approverRoles['deputy_academic'];
      newGroupIds.forEach(gid=>{
        const group = list.filter(a=>a.groupId===gid);
        if(tidAcademic) pushNote({toTeacherId:tidAcademic, type:'approval', text:buildApprovalMsg(group,'deputy_academic'), groupId:gid});
      });
    }

    const selfNames=[...new Set(list.filter(a=>a.status!=='awaiting_chair').map(a=>fullTitle(teacherById(a.substituteId))))].join(', ');
    const nDeleg=list.filter(a=>a.status==='awaiting_chair').length;
    const apTail = newGroupIds.length ? ' และส่งคำขออนุญาตไปยังรองฯ วิชาการ' : '';
    if(selfNames && nDeleg) fireToast(`ส่งคำขอแล้ว · แจ้ง ${selfNames} และประธานกลุ่มสาระ${apTail}`, 'send');
    else if(nDeleg)         fireToast(`ส่งคำขอให้ประธานกลุ่มสาระจัดครูสอนแทน${apTail}`, 'send');
    else                    fireToast(`ส่งคำขอและแจ้งเตือนไปยัง ${selfNames}${apTail}`, 'send');
  };
  const handleAck = (a)=>{ const upd={...a,status:'acknowledged',decidedAt:new Date().toISOString().slice(0,16)};
    updateAssign(a.id,{status:'acknowledged',decidedAt:upd.decidedAt});
    fireToast(`รับทราบการสอนแทนเรียบร้อยแล้ว · สถานะอัปเดตในระบบแล้ว`,'checkCircle'); };
  const handleReject = (a,reason)=>{ const upd={...a,status:'rejected',rejectReason:reason,decidedAt:new Date().toISOString().slice(0,16)};
    updateAssign(a.id,{status:'rejected',rejectReason:reason,decidedAt:upd.decidedAt});
    fireToast(`บันทึกการปฏิเสธแล้ว · สถานะอัปเดตในระบบแล้ว`,'xCircle'); };
  // ---- ประธานกลุ่มสาระจัดครูสอนแทนให้คำขอที่รอมอบหมาย ----
  const handleChairAssign = (assignmentId, patch)=>{
    const a = assignments.find(x=>x.id===assignmentId);
    if(!a) return;
    const updated = {...a, ...patch, status:'pending', delegatedToChair:true, arrangedByChairId: meT.id};
    updateAssign(assignmentId, {substituteId:patch.substituteId, makeupDate:patch.makeupDate, makeupPeriod:patch.makeupPeriod, status:'pending', arrangedByChairId:meT.id});
    // แจ้งครูสอนแทนให้มากดรับทราบ
    pushNote({toTeacherId:patch.substituteId, type:'request', text:buildRequestMsg(updated), assignmentId});
    // แจ้งครูที่ลาว่าประธานจัดให้แล้ว
    pushNote({toTeacherId:a.leaveTeacherId, type:'chair_arranged', text:buildChairArrangedMsg(updated), assignmentId});
    fireToast(`จัดครูสอนแทนให้ ${fullTitle(teacherById(a.leaveTeacherId))} แล้ว · แจ้งเตือนถึงครูสอนแทนและครูที่ลา`,'checkCircle');
  };
  // ---- admin แก้ไข/ลบ ประวัติการลา ----
  const handleEditAssign = (id, patch)=>{
    updateAssign(id, patch);
    fireToast('แก้ไขประวัติการลาเรียบร้อยแล้ว','checkCircle');
  };
  const handleDeleteAssign = (id)=>{
    setAssignments(arr=>arr.filter(a=>a.id!==id));
    fireToast('ลบรายการการลาเรียบร้อยแล้ว','info');
  };

  // schedule editing — ของตัวเอง
  const setCell = (day,period,slot)=> setSchedules(s=>{ const me=session.teacherId; const sch=JSON.parse(JSON.stringify(s[me]||{})); if(!sch[day]) sch[day]=Array(8).fill(null); sch[day][period-1]=slot; return {...s,[me]:sch}; });
  const addDuty = (d)=> setDuties(D=>({...D,[session.teacherId]:[...(D[session.teacherId]||[]),d]}));
  const removeDuty = (i)=> setDuties(D=>({...D,[session.teacherId]:(D[session.teacherId]||[]).filter((_,j)=>j!==i)}));
  // schedule editing — admin แก้ของครูคนไหนก็ได้
  const setCellFor = (teacherId,day,period,slot)=> setSchedules(s=>{ const sch=JSON.parse(JSON.stringify(s[teacherId]||{})); if(!sch[day]) sch[day]=Array(8).fill(null); sch[day][period-1]=slot; return {...s,[teacherId]:sch}; });
  const addDutyFor = (teacherId,d)=> setDuties(D=>({...D,[teacherId]:[...(D[teacherId]||[]),d]}));
  const removeDutyFor = (teacherId,i)=> setDuties(D=>({...D,[teacherId]:(D[teacherId]||[]).filter((_,j)=>j!==i)}));

  const openNotif = ()=>{
    setNotifOpen(true);
    const ids = myMsgs.map(n=>n.id);
    setSeen(ids);
    if(CLOUD && accountId && accountId!=='admin') window.db.markSeen(accountId, ids).catch(()=>{});
  };
  const logout = ()=>{ setSession(null); setNotifOpen(false); };
  const saveAccount = (id, val)=>{ setCredentials(c=>({...c,[id]:val})); setAcct(false); fireToast('เปลี่ยนรหัสผ่านเรียบร้อย · ใช้รหัสใหม่ในการเข้าระบบครั้งต่อไป','checkCircle'); };
  const handleAddTeacher = ({pre,name,primary})=>{
    const username = nextUsername(credentials);
    const t = makeTeacher({pre,name,primary}, username);
    registerTeacher(t);
    setExtraTeachers(arr=>[...arr,t]);
    setCredentials(c=>({...c,[t.id]:{username, password:'1234'}}));
    setSchedules(s=>({...s,[t.id]:t.schedule}));
    setDuties(D=>({...D,[t.id]:[]}));
    fireToast(`เพิ่ม ${formalName(t)} แล้ว · ชื่อผู้ใช้ ${username} รหัสผ่านเริ่มต้น 1234`, 'checkCircle');
  };
  const handleEditTeacher = (id, patch)=>{
    applyTeacherEdit(teacherById(id), patch);
    setTeacherEdits(e=>({...e,[id]:{...(e[id]||{}),...patch}}));
    fireToast(`บันทึกข้อมูล ${formalName(teacherById(id))} เรียบร้อย`, 'checkCircle');
  };
  const handleResetPassword = (id, newPass)=>{
    setCredentials(c=>({...c,[id]:{...c[id], password:newPass}}));
  };
  // ---- Web Push: เปิด/ปิดการแจ้งเตือนผ่านเบราว์เซอร์ ----
  const enablePush = async (teacherId)=>{
    // ขอสิทธิ์แสดง OS pop-up ถ้าเป็นไปได้ — แต่ไม่ว่าจะได้หรือไม่ ก็เปิดการแจ้งเตือนในแอปเสมอ
    let perm = (typeof Notification!=='undefined') ? Notification.permission : 'unsupported';
    if(typeof Notification!=='undefined' && perm==='default'){
      try{ perm = await Notification.requestPermission(); }catch(e){ perm = Notification.permission; }
    }
    setPermission(perm);
    setPushSubs(s=>({...s,[teacherId]:{enabledAt:new Date().toISOString().slice(0,16), browserPush:perm==='granted', device:(navigator.userAgent||'').slice(0,80)}}));
    if(perm==='granted'){
      fireBrowserNotif('เปิดการแจ้งเตือนแล้ว ✓','คุณจะได้รับการแจ้งเตือนทันทีเมื่อมีครูขอให้สอนแทน');
      fireToast('เปิดการแจ้งเตือนผ่านเบราว์เซอร์เรียบร้อยแล้ว','checkCircle');
    } else {
      // เบราว์เซอร์บล็อก/ไม่รองรับ OS pop-up — ยังใช้ศูนย์แจ้งเตือนในแอปได้
      fireToast('เปิดการแจ้งเตือนในแอปแล้ว · (เปิด pop-up ของเบราว์เซอร์ได้ภายหลังในการตั้งค่า)','checkCircle');
    }
  };
  const disablePush = (teacherId)=>{ setPushSubs(s=>{ const c={...s}; delete c[teacherId]; return c; }); fireToast('ปิดการแจ้งเตือนบนอุปกรณ์นี้แล้ว','info'); };
  const testNotif = ()=>{
    // ถ้าเบราว์เซอร์อนุญาต — เด้ง OS pop-up; ไม่งั้นแสดง toast ในแอปแทน
    if(typeof Notification!=='undefined' && Notification.permission==='granted'){
      fireBrowserNotif('ทดสอบการแจ้งเตือน','นี่คือตัวอย่างการแจ้งเตือนจากระบบการลา โรงเรียนหนองตาคงพิทยาคาร');
      fireToast('ส่งการแจ้งเตือนทดสอบแล้ว — ดูที่มุมหน้าจอ','send');
    } else {
      fireToast('ตัวอย่างการแจ้งเตือน — แสดงในศูนย์แจ้งเตือนของแอป','send');
    }
  };
  const handleSetChair = (group, teacherId)=>{
    setChairs(c=>{ const n={...c}; if(teacherId) n[group]=teacherId; else delete n[group]; return n; });
    if(teacherId) fireToast(`แต่งตั้ง ${fullTitle(teacherById(teacherId))} เป็นประธานกลุ่มสาระ${group}`,'checkCircle');
    else fireToast(`ยกเลิกประธานกลุ่มสาระ${group}แล้ว`,'info');
  };

  // ---- ขออนุญาตลา (workflow 3 ขั้น) ----
  const approverTeacherId = r => approverRoles[r] || null;
  const requestApproval = (groupId)=>{
    const group = assignments.filter(a=>a.groupId===groupId);
    if(!group.length) return;
    setApprovals(ap=>({...ap,[groupId]:{
      requestedAt:new Date().toISOString().slice(0,16),
      current:0,
      chain:APPROVAL_CHAIN.map(r=>({role:r,status:'pending',at:null,note:''})),
      final:'pending',
    }}));
    const tid = approverTeacherId('deputy_academic');
    if(tid) pushNote({toTeacherId:tid, type:'approval', text:buildApprovalMsg(group,'deputy_academic'), groupId});
    fireToast('ส่งคำขออนุญาตลาไปยังรองฯ วิชาการแล้ว · ส่งการแจ้งเตือนแล้ว','send');
  };
  const decideApproval = (groupId, decision, note)=>{
    const group = assignments.filter(a=>a.groupId===groupId);
    const ap = approvals[groupId];
    if(!ap) return;
    const curIdx = ap.current, curRole = APPROVAL_CHAIN[curIdx];
    setApprovals(prev=>{
      const a = {...prev[groupId]};
      a.chain = a.chain.map((s,i)=> i===curIdx ? {...s,status:decision,at:new Date().toISOString().slice(0,16),note:note||''} : s);
      if(decision==='rejected') a.final='rejected';
      else if(curIdx >= APPROVAL_CHAIN.length-1) a.final='approved';
      else a.current = curIdx+1;
      return {...prev,[groupId]:a};
    });
    if(decision==='rejected'){
      pushNote({toTeacherId:group[0].leaveTeacherId, type:'approval_result', text:buildApprovalResultMsg(group,false,curRole,note), groupId});
      fireToast('บันทึกผล “ไม่อนุญาต” แล้ว · แจ้งครูผ่านการแจ้งเตือน','xCircle');
    } else if(curIdx >= APPROVAL_CHAIN.length-1){
      pushNote({toTeacherId:group[0].leaveTeacherId, type:'approval_result', text:buildApprovalResultMsg(group,true), groupId});
      fireToast('อนุญาตการลาเรียบร้อย · แจ้งผลให้ครูผ่านการแจ้งเตือน','checkCircle');
    } else {
      const nextRole = APPROVAL_CHAIN[curIdx+1];
      const tid = approverTeacherId(nextRole);
      if(tid) pushNote({toTeacherId:tid, type:'approval', text:buildApprovalMsg(group,nextRole), groupId});
      fireToast(`เห็นควรอนุญาตแล้ว · ส่งต่อ ${APPROVER_BY_ROLE[nextRole].short} แล้ว`,'checkCircle');
    }
  };
  const handleSetApprover = (r, teacherId)=>{
    setApproverRoles(prev=>{
      const n={...prev};
      if(teacherId) Object.keys(n).forEach(rr=>{ if(n[rr]===teacherId) delete n[rr]; });
      if(teacherId) n[r]=teacherId; else delete n[r];
      return n;
    });
    if(teacherId) fireToast(`แต่งตั้ง ${fullTitle(teacherById(teacherId))} เป็น ${APPROVER_BY_ROLE[r].title}`,'checkCircle');
    else fireToast(`ยกเลิกตำแหน่ง ${APPROVER_BY_ROLE[r].short} แล้ว`,'info');
  };

  const resetDemo = ()=>{
    if(CLOUD){
      if(!confirm('โหลดข้อมูลใหม่จากคลาวด์? (ข้อมูลบน Supabase จะไม่ถูกแตะต้อง)')) return;
      localStorage.removeItem(LS_KEY);
      location.reload();
      return;
    }
    if(!confirm('ล้างข้อมูลทั้งหมดและกลับสู่ค่าเริ่มต้น?')) return;
    localStorage.removeItem(LS_KEY);
    setAssignments(SEED.slice()); setNotifications(seedNotifications(SEED)); setSeen([]);
    setSchedules(defaultSchedules()); setDuties(defaultDuties()); setCredentials(defaultCredentials()); setExtraTeachers([]); setTeacherEdits({}); setPushSubs({}); setChairs({...DEFAULT_CHAIRS}); setApprovals(seedApprovals(SEED)); setApproverRoles({...DEFAULT_APPROVER_ROLES}); pruneExtraTeachers(); fireToast('รีเซ็ตข้อมูลแล้ว','info');
  };

  const myChairGroup = meT ? chairGroupOf(chairs, meT.id) : null;
  const chairPending = myChairGroup ? assignments.filter(a=>a.status==='awaiting_chair' && membersOfGroup(myChairGroup).some(m=>m.id===a.leaveTeacherId)).length : 0;

  const mePush = meT ? pushSubs[meT.id] : null;
  const pushEnabledForView = !!mePush;
  const notifAccount = meT ? {id:meT.id, name:fullTitle(meT), sub: myApproverRole?APPROVER_BY_ROLE[myApproverRole].short:meT.primary, username:meT.username} : null;
  const TABS = isAdmin
    ? [{id:'admin',label:'ภาพรวมการจัดสอนแทน',icon:'shield'},{id:'dashboard',label:'แดชบอร์ดการลา',icon:'layers'},{id:'schedule-admin',label:'จัดการตารางสอน',icon:'clipboard'},{id:'users',label:'จัดการผู้ใช้งาน',icon:'users'},...(CLOUD?[{id:'line',label:'ตั้งค่า LINE',icon:'bell'}]:[])]
    : [
        ...(myApproverRole ? [
          {id:'approve',label:'พิจารณาอนุญาตการลา',icon:'shield', badge:pendingApprovals},
          {id:'dashboard',label:'แดชบอร์ดการลา',icon:'layers'},
        ] : []),
        {id:'schedule',label:'ภาระงานสอนของฉัน',icon:'clipboard'},
        {id:'leave',   label:'แจ้งลา / ขอเปลี่ยนคาบ',icon:'calendar'},
        {id:'sub',     label:'รายการสอนแทนของฉัน',icon:'inbox', badge:pendingForMe},
        ...(myChairGroup ? [{id:'chair', label:'ประธานกลุ่มสาระ', icon:'shield', badge:chairPending}] : []),
      ];
  // ถ้า view ปัจจุบันไม่มีในแท็บของบทบาทนี้ ให้ไปแท็บแรก
  const activeView = TABS.some(t=>t.id===view) ? view : (TABS[0] && TABS[0].id);

  return (
    <div className="app">
      <header className="appbar">
        <div className="appbar-in">
          <div className="brand">
            <div className="crest"><img src="logo.png" alt="ตราโรงเรียน"/></div>
            <div className="brand-txt">
              <div className="t1">ระบบการลา <span className="t1-en">Leave Control System</span></div>
              <div className="t2">โรงเรียนหนองตาคงพิทยาคาร · สพม.จันทบุรี ตราด</div>
            </div>
          </div>
          <span className="spacer"/>
          {isTeacher && (
            <button className="notif-btn" onClick={openNotif}>
              <Icon name="bell" size={16}/> แจ้งเตือน {unread>0 && pushEnabledForView && <span className="dot num">{unread}</span>}
            </button>
          )}
          <div className="user-chip">
            {isAdmin ? <span className="av av-sm" style={{background:'var(--brand-500)'}}><Icon name="shield" size={15}/></span>
              : <Avatar t={meT} size="sm"/>}
            <div>
              <div className="ui">{isAdmin?'งานวิชาการ / ผู้บริหาร': formalName(meT)}</div>
              <div className="ur">{isAdmin?'ผู้ดูแลภาพรวม': myApproverRole?APPROVER_BY_ROLE[myApproverRole].title : `ครูผู้สอน · ${meT.primary}`}</div>
            </div>
            <button className="logout-btn" title="ตั้งค่าบัญชี / เปลี่ยนรหัสผ่าน" onClick={()=>setAcct(true)}><Icon name="edit" size={15}/></button>
            <button className="logout-btn" title="ออกจากระบบ" onClick={logout}><Icon name="arrowLeft" size={16}/></button>
          </div>
        </div>
      </header>

      <div className="tabs-wrap">
        <div className="tabs">
          {TABS.map(t=>(
            <button key={t.id} className={`tab ${activeView===t.id?'active':''}`} onClick={()=>setView(t.id)}>
              <Icon name={t.icon} size={17}/>{t.label}{t.badge>0 && <span className="pill num">{t.badge}</span>}
            </button>
          ))}
        </div>
      </div>

      <main className="main">
        <div className="container">
          {isAdmin && CLOUD && cloudEmpty && (
            <div className="push-banner">
              <span className="lb-ic"><Icon name="bell" size={19}/></span>
              <div className="lb-t">
                <b>ฐานข้อมูลคลาวด์ยังว่าง</b>
                <div>กดเพื่อส่งรายชื่อครู ผู้บริหาร และตารางสอนเริ่มต้นขึ้น Supabase (ทำครั้งเดียว)</div>
              </div>
              <Btn variant="green" icon="send" onClick={handleCloudSeed}>เริ่มต้นข้อมูลในคลาวด์</Btn>
            </div>
          )}
          {isTeacher && !mePush && (
            <div className="push-banner">
              <span className="lb-ic"><Icon name="bell" size={19}/></span>
              <div className="lb-t"><b>ยังไม่ได้เปิดการแจ้งเตือน</b><div>เปิดการแจ้งเตือนผ่านเบราว์เซอร์ (Web Push) เพื่อรับแจ้งทันทีเมื่อมีครูขอให้สอนแทน</div></div>
              <Btn variant="green" icon="bell" onClick={()=>enablePush(meT.id)}>เปิดการแจ้งเตือน</Btn>
            </div>
          )}
          {isTeacher && mePush && (
            <div className="flex ac gap10 wrap" style={{marginBottom:16}}>
              <span className="push-status"><span className="ds"></span>เปิดการแจ้งเตือนผ่านเบราว์เซอร์แล้ว</span>
              <button className="tiny" style={{background:'none',border:'none',color:'var(--ink-3)',textDecoration:'underline',cursor:'pointer'}} onClick={()=>disablePush(meT.id)}>ปิดการแจ้งเตือน</button>
            </div>
          )}
          {isTeacher && activeView==='schedule' && <ScheduleView me={meT} schedule={schedules[meT.id]||meT.schedule} duties={duties[meT.id]||[]} onUpdateCell={setCell} onDeleteCell={(d,p)=>setCell(d,p,null)} onAddDuty={addDuty} onRemoveDuty={removeDuty}/>}
          {isTeacher && activeView==='leave' && <LeaveView me={meT} assignments={assignments} schedules={schedules} approvals={approvals} chairs={chairs} approverRoles={approverRoles} onSubmit={handleSubmit} onRequestApproval={requestApproval}/>}
          {isTeacher && activeView==='sub' && <SubstituteView me={meT} assignments={assignments} onAcknowledge={handleAck} onReject={handleReject}/>}
          {isTeacher && activeView==='chair' && myChairGroup && <ChairView chair={meT} group={myChairGroup} members={membersOfGroup(myChairGroup)} assignments={assignments} schedules={schedules} pushSubs={pushSubs} onSubmit={handleSubmit} onChairAssign={handleChairAssign}/>}
          {isApprover && activeView==='approve' && <ApproverView approver={approver} assignments={assignments} approvals={approvals} onDecide={decideApproval}/>}
          {(isAdmin||isApprover) && activeView==='dashboard' && <DashboardView assignments={assignments} approvals={approvals}/>}
          {isAdmin && activeView==='admin' && <AdminView assignments={assignments} schedules={schedules} onEditAssign={handleEditAssign} onDeleteAssign={handleDeleteAssign}/>}
          {isAdmin && activeView==='schedule-admin' && <AdminSchedule schedules={schedules} duties={duties} onSetCell={setCellFor} onAddDuty={addDutyFor} onRemoveDuty={removeDutyFor}/>}
          {isAdmin && activeView==='users' && <AdminUsers credentials={credentials} chairs={chairs} pushSubs={pushSubs} approverRoles={approverRoles} onAddTeacher={handleAddTeacher} onEditTeacher={handleEditTeacher} onResetPassword={handleResetPassword} onSetChair={handleSetChair} onSetApprover={handleSetApprover}/>}
          {isAdmin && activeView==='line' && CLOUD && (
            <LineSettingsView
              settings={lineSettings}
              lineUserIds={lineUserIds}
              webhookUrl={`${(window.APP_CONFIG||{}).SUPABASE_URL||''}/functions/v1/line-webhook`}
              onSave={async (s)=>{
                try{
                  await window.db.saveLineSettings(s);
                  setLineSettings(s);
                  fireToast(s.enabled?'บันทึกการตั้งค่า LINE และเปิดใช้งานแล้ว ✓':'บันทึกการตั้งค่าแล้ว (ยังไม่เปิดใช้งาน)','checkCircle');
                }catch(e){ console.error(e); fireToast('บันทึกไม่สำเร็จ: '+(e.message||''),'xCircle'); }
              }}
              onUnbind={async (tid)=>{
                if(!confirm('ปลดการผูก LINE ของครูคนนี้?')) return;
                try{ await window.db.saveLineUserId(tid, null); setLineUserIds(m=>{const n={...m}; delete n[tid]; return n;}); fireToast('ปลดการผูก LINE แล้ว','info'); }
                catch(e){ fireToast('ปลดผูกไม่สำเร็จ','xCircle'); }
              }}
              onTest={async (tid)=>{
                const t = teacherById(tid);
                const txt = `🔔 ทดสอบการแจ้งเตือนจากระบบการลา\nสวัสดี${fullTitle(t)} หากท่านอ่านข้อความนี้แสดงว่าระบบเชื่อมต่อ LINE สำเร็จ ✓`;
                const r = await window.db.lineNotify([tid], txt);
                if(r && r.sent>0) fireToast(`ส่งข้อความทดสอบไปยัง ${fullTitle(t)} แล้ว`,'send');
                else fireToast(`ส่งไม่สำเร็จ — ${r&&r.skipped?r.skipped:(r&&r.error?r.error:'ตรวจ token/การผูกบัญชี')}`,'xCircle');
              }}
            />
          )}

          <div className="flex ac jc gap8" style={{marginTop:36,paddingTop:18,borderTop:'1px dashed var(--line)'}}>
            <span className="tiny">ต้นแบบระบบ — โรงเรียนหนองตาคงพิทยาคาร · ข้อมูลตารางสอน 1/2569</span>
            <span className="tiny">·</span>
            <button className="tiny" style={{background:'none',border:'none',color:'var(--brand-600)',textDecoration:'underline',cursor:'pointer'}} onClick={resetDemo}>รีเซ็ตข้อมูล</button>
          </div>
        </div>
      </main>

      {notifOpen && notifAccount && <NotifCenter account={notifAccount} notifications={notifications} enabled={pushEnabledForView} permission={permission} onEnable={()=>enablePush(meT.id)} onTest={testNotif} onClose={()=>setNotifOpen(false)} onOpenLink={(n)=>{setNotifOpen(false);setView(n.type==='approval'?'approve':n.type==='chair_request'?'chair':'sub');}}/>}
      {acct && <AccountModal accountId={isAdmin?'admin':session.teacherId} label={isAdmin?'งานวิชาการ / ผู้บริหาร':formalName(meT)} credentials={credentials} onSave={saveAccount} onClose={()=>setAcct(false)}/>}
      {toast && <Toast msg={toast.msg} icon={toast.icon}/>}
    </div>
  );
}

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