// Main RACI Matrix page — sticky left & right columns, density toggle, full-screen,
// column visibility, comment drawer, status workflow.

const RaciMatrixPage = ({
  tenant, period, setPeriod, currentRole, currentUser,
  tasks, setTasks, taskHistory, setTaskHistory, comments, setComments, auditLog, setAuditLog,
  subprocesses, positions, divisions, departments, users,
  mainProcesses, processes, tweaks,
  initialDate, onConsumedInitialDate,
  onFullscreen, onExitFullscreen, fullscreen,
}) => {
  const toast = useToast();
  // Filters
  const [q, setQ] = useState("");
  const [divFilter, setDivFilter] = useState("");
  const [deptFilter, setDeptFilter] = useState("");
  const [statusFilter, setStatusFilter] = useState("all");
  const [showMine, setShowMine] = useState(false);
  const [showPrepare, setShowPrepare] = useState(false);
  const [showApprove, setShowApprove] = useState(false);
  const [density, setDensity] = useState("comfortable");
  const [showLegend, setShowLegend] = useState(true);
  const [selectedDate, setSelectedDate] = useState("2026-05-22"); // "today" in demo
  const [timelineFor, setTimelineFor] = useState(null); // sub-process for timeline drawer
  const [viewMode, setViewMode] = useState("raci"); // raci | calendar
  const [showDailyStatus, setShowDailyStatus] = useState(true);

  // Consume initial date from caller (e.g., dashboard drill-down)
  useEffect(() => {
    if (initialDate) {
      setSelectedDate(initialDate);
      // Clear any filters so the user definitely sees the row they came from
      setQ("");
      setDivFilter("");
      setDeptFilter("");
      setStatusFilter("all");
      setShowMine(false);
      setShowPrepare(false);
      setShowApprove(false);
      setViewMode("raci");
      setShowDailyStatus(true);
      onConsumedInitialDate?.();
    }
  }, [initialDate]);

  // Column visibility
  const [colVis, setColVis] = useState({
    div: true, dept: true, due: true, action: true, comment: true,
  });
  const [colsOpen, setColsOpen] = useState(false);

  // Active modals
  const [approveFor, setApproveFor] = useState(null); // task
  const [commentFor, setCommentFor] = useState(null);   // for R correction response
  const [cancelFor, setCancelFor] = useState(null);     // S → C cancel reason
  const [drawerFor, setDrawerFor] = useState(null);     // comment history drawer

  // Determine current user position-ids (if any)
  const myPositionIds = currentUser?.positions || [];

  // Filtered rows = subprocesses filtered, sorted by Main process → Process → Sub-process display_order
  const rows = useMemo(() => {
    const sorted = subprocesses.slice().sort((a, b) => {
      const mpA = mainProcesses.find(m=>m.id===a.main_process_id);
      const mpB = mainProcesses.find(m=>m.id===b.main_process_id);
      const mpDiff = (mpA?.display_order ?? mpA?.order ?? 999) - (mpB?.display_order ?? mpB?.order ?? 999);
      if (mpDiff !== 0) return mpDiff;
      const prA = processes.find(p=>p.id===a.process_id);
      const prB = processes.find(p=>p.id===b.process_id);
      const prDiff = (prA?.display_order ?? prA?.order ?? 999) - (prB?.display_order ?? prB?.order ?? 999);
      if (prDiff !== 0) return prDiff;
      return (a.display_order ?? a.order ?? 999) - (b.display_order ?? b.order ?? 999);
    });
    return sorted.filter(sp => {
      // search
      if (q) {
        const mp = mainProcesses.find(m=>m.id===sp.main_process_id)?.name || "";
        const pr = processes.find(p=>p.id===sp.process_id)?.name || "";
        if (!(`${mp} ${pr} ${sp.name}`.toLowerCase().includes(q.toLowerCase()))) return false;
      }
      // status — must use the resolved instance for the SELECTED date, not the current snapshot
      if (statusFilter !== "all") {
        const t = resolveTaskForDate(sp, selectedDate, tasks, taskHistory);
        if (!t || t.status !== statusFilter) return false;
      }
      // mine / prepare / approve — "งานของฉัน" = เฉพาะที่เป็น R หรือ A เท่านั้น (ไม่รวม C/I ซึ่งไม่ต้องดำเนินการ)
      if (showMine || showPrepare || showApprove) {
        const isR = myPositionIds.includes(sp.R);
        const isA = myPositionIds.includes(sp.A);
        if (showPrepare && !isR) return false;
        if (showApprove && !isA) return false;
        if (showMine && !(isR || isA)) return false;
      }
      return true;
    });
  }, [subprocesses, q, statusFilter, showMine, showPrepare, showApprove, tasks, taskHistory, selectedDate, myPositionIds, mainProcesses, processes]);

  // Position columns grouped by div + dept
  const positionGroups = useMemo(() => {
    let pos = positions.slice();
    if (divFilter) pos = pos.filter(p => p.division_id === divFilter);
    if (deptFilter) pos = pos.filter(p => p.department_id === deptFilter);
    // group dept->positions, then group div->depts
    const byDept = {};
    pos.forEach(p => {
      if (!byDept[p.department_id]) byDept[p.department_id] = [];
      byDept[p.department_id].push(p);
    });
    const groupedDivs = {};
    Object.entries(byDept).forEach(([deptId, ps]) => {
      const d = departments.find(x=>x.id===deptId);
      if (!d) return;
      if (!groupedDivs[d.division_id]) groupedDivs[d.division_id] = [];
      groupedDivs[d.division_id].push({ dept: d, positions: ps });
    });
    return divisions
      .filter(div => groupedDivs[div.id])
      .map(div => ({ div, departments: groupedDivs[div.id] }));
  }, [positions, divFilter, deptFilter, departments, divisions]);

  const flatPositions = useMemo(() => {
    const arr = [];
    positionGroups.forEach(g => g.departments.forEach(d => d.positions.forEach(p => arr.push(p))));
    return arr;
  }, [positionGroups]);

  // Get RACI role for sub-process x position
  const getRaci = (sp, posId) => {
    if (sp.R === posId) return "R";
    if (sp.A === posId) return "A";
    if (sp.C.includes(posId)) return "C";
    if (sp.I.includes(posId)) return "I";
    return null;
  };

  // ------ status change handlers ------
  // Write to the correct task instance — historical for date-bound frequencies, current snapshot for ad-hoc.
  const updateTask = (subId, patch, audit) => {
    const sp = subprocesses.find(s => s.id === subId);
    const dateKey = selectedDate;
    const stamp = nowStamp();

    if (sp && (sp.frequency === "daily" || sp.frequency === "weekly" || sp.frequency === "monthly" || sp.frequency === "quarterly")) {
      setTaskHistory(prev => {
        const list = (prev[subId] || []).slice();
        let idx = -1;
        if (sp.frequency === "monthly" || sp.frequency === "quarterly") {
          idx = list.findIndex(h => h.date.slice(0,7) === dateKey.slice(0,7));
        } else if (sp.frequency === "weekly") {
          idx = list.findIndex(h => Math.abs((new Date(h.date) - new Date(dateKey)) / 86400000) <= 6);
        } else {
          idx = list.findIndex(h => h.date === dateKey);
        }
        if (idx >= 0) {
          list[idx] = { ...list[idx], ...patch, updated: stamp };
        } else {
          list.push({ date: dateKey, status:"blank", ...patch, updated: stamp });
          list.sort((a,b) => a.date.localeCompare(b.date));
        }
        return { ...prev, [subId]: list };
      });
    }
    // Also keep the current snapshot in sync when editing today
    if (dateKey === "2026-05-22") {
      setTasks(prev => prev.map(t => t.sub_process_id === subId ? { ...t, ...patch, updated: stamp } : t));
    }
    // ad-hoc — write to current snapshot only
    if (sp?.frequency === "ad-hoc") {
      setTasks(prev => prev.map(t => t.sub_process_id === subId ? { ...t, ...patch, updated: stamp } : t));
    }

    if (audit) {
      const dateLbl = dateKey === "2026-05-22" ? "วันนี้" : `งวด ${dateKey}`;
      setAuditLog(prev => [{
        id:"a"+Math.random().toString(36).slice(2,7), scope:"tenant", tenant_id: tenant.id,
        action: audit.action || "status_change", reason: `${audit.reason} · ${dateLbl}`, performed_by: audit.who, at: stamp,
      }, ...prev]);
    }
  };

  const addComment = (subId, comment) => {
    setComments(prev => ({ ...prev, [subId]: [...(prev[subId]||[]), comment] }));
  };

  const handleP = (sp) => {
    const t = tasks.find(x=>x.sub_process_id===sp.id);
    const isR = myPositionIds.includes(sp.R);
    if (!isR && currentRole !== "admin") {
      toast({ kind:"warn", msg:"คุณไม่มีสิทธิ์อัปเดตงานนี้ เนื่องจากไม่ได้เป็นผู้รับผิดชอบ R" });
      return;
    }
    updateTask(sp.id, { status:"A", prepared_at: nowStamp() }, {
      reason: `P → A · ${sp.name}`, who: currentUser?.full_name || roleLabel(currentRole)
    });
    addComment(sp.id, { id:cid(), type:"system_note", author_name:"System", text:`ส่งงานให้ผู้ตรวจสอบ`, at: nowStamp(), task_date: selectedDate });
    toast({ kind:"success", msg:"ส่งงานให้ผู้ตรวจสอบแล้ว" });
  };

  const openApprove = (sp) => {
    const isA = myPositionIds.includes(sp.A);
    if (!isA && currentRole !== "admin") {
      toast({ kind:"warn", msg:"คุณไม่มีสิทธิ์อนุมัติงานนี้ เนื่องจากไม่ได้เป็นผู้ตรวจสอบ A" });
      return;
    }
    setApproveFor(sp);
  };

  const approveSuccess = (sp) => {
    updateTask(sp.id, { status:"S", approved_at: nowStamp(), completed_at: nowStamp() }, {
      reason:`A → S · ${sp.name}`, who: currentUser?.full_name || roleLabel(currentRole)
    });
    addComment(sp.id, { id:cid(), type:"system_note", author_name:"System", text:`อนุมัติงานสำเร็จ`, at: nowStamp(), task_date: selectedDate });
    setApproveFor(null);
    toast({ kind:"success", msg:"งานสำเร็จเรียบร้อยแล้ว" });
  };

  const approveCorrect = (sp, reason) => {
    updateTask(sp.id, { status:"C" }, { reason:`A → C · ${sp.name} (มี comment)`, who: currentUser?.full_name || roleLabel(currentRole) });
    addComment(sp.id, { id:cid(), type:"correction", author_name: currentUser?.full_name || "ผู้ตรวจสอบ", text:reason, at: nowStamp(), task_date: selectedDate });
    setApproveFor(null);
    toast({ kind:"success", msg:"ส่ง Comment ให้แก้ไขแล้ว" });
  };

  const handleC = (sp) => {
    const isR = myPositionIds.includes(sp.R);
    if (!isR && currentRole !== "admin") {
      toast({ kind:"warn", msg:"เฉพาะผู้รับผิดชอบ R เท่านั้นที่ตอบกลับ Comment ได้" });
      return;
    }
    setCommentFor(sp);
  };

  const submitCorrectionResponse = (sp, response) => {
    updateTask(sp.id, { status:"A" }, { reason:`C → A · ${sp.name} (ตอบกลับ comment)`, who: currentUser?.full_name || roleLabel(currentRole) });
    addComment(sp.id, { id:cid(), type:"response", author_name: currentUser?.full_name || "ผู้รับผิดชอบ", text:response, at: nowStamp(), task_date: selectedDate });
    setCommentFor(null);
    toast({ kind:"success", msg:"ส่งงานกลับให้ผู้ตรวจสอบแล้ว" });
  };

  const handleCancelS = (sp) => {
    const isA = myPositionIds.includes(sp.A);
    if (!isA && currentRole !== "admin") {
      toast({ kind:"warn", msg:"เฉพาะผู้ตรวจสอบ A หรือ Admin เท่านั้นที่ยกเลิกสถานะสำเร็จได้" });
      return;
    }
    setCancelFor(sp);
  };

  const submitCancelS = (sp, reason) => {
    updateTask(sp.id, { status:"C" }, { reason:`S → C · ${sp.name} (ยกเลิกสถานะสำเร็จ)`, who: currentUser?.full_name || roleLabel(currentRole), action:"status_change" });
    addComment(sp.id, { id:cid(), type:"cancel_success", author_name: currentUser?.full_name || "ผู้ตรวจสอบ", text:`ยกเลิกสถานะสำเร็จ: ${reason}`, at: nowStamp(), task_date: selectedDate });
    setCancelFor(null);
    toast({ kind:"success", msg:"ยกเลิกสถานะสำเร็จแล้ว และส่งกลับไปแก้ไข" });
  };

  // ---- Layout vars ----
  const COL_W = { mp:170, pr:170, sp:240, pos:124, due:108, action:130, comment:78 };
  const RIGHT_W = (colVis.due?COL_W.due:0)+(colVis.action?COL_W.action:0)+(colVis.comment?COL_W.comment:0);

  return (
    <div className={`${fullscreen ? "fixed inset-0 z-[60] bg-ink-50" : "p-6"}`}>
      {!fullscreen && (
        <PageHeader
          eyebrow={`${tenant.company_short} · พ.ค. 2026`}
          title="RACI Matrix"
          subtitle="ตารางหลักของการมอบหมายความรับผิดชอบและสถานะรายวัน · เลื่อนตารางในแนวนอนเพื่อดูตำแหน่งทั้งหมด"
          right={<>
            <Button icon="download" variant="secondary" onClick={()=>{
              downloadCsv(`${tenant.tenant_code}_raci_matrix_${selectedDate}.csv`,
                ["Main Process","Process","Sub-process","Frequency","Due rule","R Position","A Position","C Positions","I Positions","Status (date)","Last Update"],
                rows.map(sp => {
                  const mp = mainProcesses.find(m=>m.id===sp.main_process_id);
                  const pr = processes.find(p=>p.id===sp.process_id);
                  const t = resolveTaskForDate(sp, selectedDate, tasks, taskHistory);
                  return [mp?.name||"", pr?.name||"", sp.name, sp.frequency, sp.due_day,
                    positions.find(p=>p.id===sp.R)?.name||"",
                    positions.find(p=>p.id===sp.A)?.name||"",
                    sp.C.map(id=>positions.find(p=>p.id===id)?.name).filter(Boolean).join(" / "),
                    sp.I.map(id=>positions.find(p=>p.id===id)?.name).filter(Boolean).join(" / "),
                    STATUS_META[t?.status||"blank"]?.label, t?.updated||""];
                }));
            }}>Export Excel</Button>
            <Button icon="expand" onClick={onFullscreen}>โหมดเต็มจอ</Button>
          </>}
        />
      )}

      {fullscreen && (
        <div className="px-5 pt-3 pb-2 flex items-center gap-3 border-b border-ink-200 bg-white">
          <div className="flex items-center gap-2.5">
            <div className="w-8 h-8 rounded-md bg-gradient-to-br from-indigo-500 to-indigo-700 text-white flex items-center justify-center"><Icon name="table" size={14}/></div>
            <div>
              <div className="text-[14px] font-bold text-ink-900">RACI Matrix — Fullscreen</div>
              <div className="text-[11px] text-ink-500">{tenant.company_short} · พ.ค. 2026</div>
            </div>
          </div>
          <div className="ml-auto"><Button icon="compress" variant="secondary" onClick={onExitFullscreen}>ออกจากเต็มจอ</Button></div>
        </div>
      )}

      {/* Filters bar */}
      <div className={`${fullscreen ? "px-5 py-3 bg-white border-b border-ink-200" : "bg-white rounded-2xl border border-ink-100 px-4 py-3 mb-4"}`}>
        <div className="flex items-center gap-2 flex-wrap">
          <div className="relative flex-1 min-w-[220px] max-w-md">
            <Icon name="search" size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-ink-400"/>
            <input value={q} onChange={e=>setQ(e.target.value)} placeholder="ค้นหา Main / Process / Sub-process…"
              className="w-full h-9 pl-9 pr-3 text-[13px] border border-ink-200 rounded-lg bg-white focus:border-brand-500 ring-focus"/>
          </div>
          <Select value={divFilter} onChange={e=>{ setDivFilter(e.target.value); setDeptFilter(""); }} className="!w-[170px]">
            <option value="">ทุก Division</option>
            {divisions.map(d => <option key={d.id} value={d.id}>{d.name}</option>)}
          </Select>
          <Select value={deptFilter} onChange={e=>setDeptFilter(e.target.value)} className="!w-[170px]">
            <option value="">ทุก Department</option>
            {departments.filter(d=>!divFilter || d.division_id===divFilter).map(d => <option key={d.id} value={d.id}>{d.name}</option>)}
          </Select>
          <Select value={statusFilter} onChange={e=>setStatusFilter(e.target.value)} className="!w-[170px]">
            <option value="all">ทุกสถานะ</option>
            <option value="blank">ยังไม่ถึงกำหนด</option>
            <option value="P">P · รอจัดทำ</option>
            <option value="A">A · รอตรวจสอบ</option>
            <option value="C">C · มี Comment</option>
            <option value="S">S · สำเร็จ</option>
          </Select>
          {/* Date selector for daily-status lookup */}
          <DateNavigator value={selectedDate} onChange={setSelectedDate}/>

          <div className="ml-auto flex items-center gap-2 flex-wrap">
            <Segmented value={viewMode} onChange={setViewMode} options={[
              { value:"raci", label:"⌀ RACI" },
              { value:"calendar", label:"📅 Calendar" },
            ]}/>
            <Tip tip={showDailyStatus ? "ซ่อนแถบ Daily Status ด้านขวา" : "แสดงแถบ Daily Status"}>
              <button onClick={()=>setShowDailyStatus(v=>!v)} className={`inline-flex items-center gap-1.5 h-8 px-2.5 rounded-lg border text-[12px] transition ${showDailyStatus?"bg-white border-ink-200 text-ink-700 hover:bg-ink-50":"bg-amber-50 border-amber-200 text-amber-700"}`}>
                <Icon name={showDailyStatus?"eyeOff":"eye"} size={13}/>
                {showDailyStatus ? "ซ่อน Status" : "แสดง Status"}
              </button>
            </Tip>
            <div className="w-px h-5 bg-ink-200 mx-0.5"/>
            <Toggle checked={showMine} onChange={()=>setShowMine(v=>!v)} label="งานของฉัน"/>
            <Toggle checked={showPrepare} onChange={()=>setShowPrepare(v=>!v)} label="ที่ฉันต้องจัดทำ"/>
            <Toggle checked={showApprove} onChange={()=>setShowApprove(v=>!v)} label="ที่ฉันต้องตรวจสอบ"/>
            <div className="w-px h-5 bg-ink-200 mx-1"/>
            <Segmented value={density} onChange={setDensity} options={[{value:"comfortable",label:"Comfortable"},{value:"compact",label:"Compact"}]}/>
            <div className="relative">
              <button onClick={()=>setColsOpen(v=>!v)} className="inline-flex items-center gap-1.5 h-8 px-2.5 rounded-lg border border-ink-200 bg-white hover:bg-ink-50 text-[12px]"><Icon name="columns" size={13}/>คอลัมน์</button>
              {colsOpen && <ColumnVisibilityPopover colVis={colVis} setColVis={setColVis} onClose={()=>setColsOpen(false)}/>}
            </div>
          </div>
        </div>

        {showLegend && (
          <div className="mt-3 flex items-center gap-3 flex-wrap pt-2 border-t border-ink-100">
            <div className="text-[10.5px] uppercase tracking-wider text-ink-400 font-semibold">RACI Legend</div>
            {["R","A","C","I"].map(k => (
              <div key={k} className="flex items-center gap-1.5">
                <RaciBadge kind={k} size="sm"/>
                <span className="text-[11.5px] text-ink-600">{RACI_META[k].label}</span>
              </div>
            ))}
            <div className="w-px h-4 bg-ink-200"/>
            <div className="text-[10.5px] uppercase tracking-wider text-ink-400 font-semibold">Status</div>
            {["blank","P","A","C","S"].map(s => (
              <div key={s} className="flex items-center gap-1.5">
                <span className="w-2 h-2 rounded-full" style={{background:STATUS_META[s].dot}}/>
                <span className="text-[11.5px] text-ink-600">{STATUS_META[s].short !== "-" ? `${STATUS_META[s].short} · ` : ""}{STATUS_META[s].label}</span>
              </div>
            ))}
            <button onClick={()=>setShowLegend(false)} className="ml-auto text-[11px] text-ink-400 hover:text-ink-700">ซ่อน Legend</button>
          </div>
        )}
        {!showLegend && (
          <button onClick={()=>setShowLegend(true)} className="mt-2 text-[11px] text-brand-600 hover:underline">แสดง Legend</button>
        )}
      </div>

      {/* Matrix table container — RACI org view */}
      {viewMode === "raci" && (
      <div className={`${fullscreen ? "mx-5 mb-5" : ""} bg-white rounded-2xl border border-ink-100 overflow-hidden shadow-sm density-${density}`}
           style={{ height: fullscreen ? "calc(100vh - 200px)" : "calc(100vh - 280px)", minHeight: 540 }}>
        <div className="w-full h-full overflow-auto thin-scroll relative" id="raci-scroll">
          <table className="border-separate" style={{ borderSpacing:0 }}>
            <thead>
              {/* Row 1 - Division group + Section labels */}
              <tr>
                <th colSpan={3} rowSpan={colVis.dept ? 3 : (colVis.div ? 2 : 1)}
                    className="sticky top-0 left-0 z-50 bg-ink-50 matrix-head-cell text-left px-3 py-2"
                    style={{ minWidth: COL_W.mp+COL_W.pr+COL_W.sp, height:38 }}>
                  <div className="text-[10.5px] uppercase tracking-[.15em] text-ink-500 font-semibold">Process Hierarchy</div>
                  <div className="text-[10.5px] text-ink-400">Main · Process · Sub-process</div>
                </th>
                {colVis.div && positionGroups.map((g,i) => (
                  <th key={g.div.id} colSpan={g.departments.reduce((s,d)=>s+d.positions.length,0)}
                      className="sticky top-0 z-40 matrix-head-cell text-left px-2 py-1 bg-gradient-to-r from-indigo-50 to-indigo-50/60"
                      style={{height:30}}>
                    <div className="flex items-center gap-1.5 text-[11px] font-semibold text-indigo-700">
                      <span className="w-1.5 h-1.5 rounded-full bg-indigo-500"/>
                      <span className="truncate">{g.div.name}</span>
                    </div>
                  </th>
                ))}
                {showDailyStatus && (
                <th colSpan={Object.values({ due:colVis.due, action:colVis.action, comment:colVis.comment }).filter(Boolean).length}
                    rowSpan={colVis.dept ? 3 : (colVis.div ? 2 : 1)}
                    className="sticky top-0 right-0 z-50 bg-ink-50 matrix-head-cell text-left px-3 py-2 sticky-shadow-l"
                    style={{ minWidth: RIGHT_W }}>
                  <div className="text-[10.5px] uppercase tracking-[.15em] text-ink-500 font-semibold">Daily Status <span className="text-brand-600">· {formatDateThai(selectedDate)}</span>{selectedDate !== "2026-05-22" && <span className="ml-1.5 normal-case tracking-normal inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-amber-100 text-amber-700 text-[10px] font-semibold"><Icon name="history" size={9}/>โหมดย้อนหลัง</span>}</div>
                  <div className="text-[10.5px] text-ink-400">{selectedDate !== "2026-05-22" ? "แก้ไขย้อนหลังได้ · ทุกการอัปเดตจะถูกบันทึก Audit Log" : "เปิด Timeline เพื่อดูประวัติทุกวัน"}</div>
                </th>
                )}
              </tr>
              {/* Row 2 - Department */}
              {colVis.dept && (
                <tr>
                  {positionGroups.map(g => g.departments.map(d => (
                    <th key={d.dept.id} colSpan={d.positions.length}
                        className="sticky z-30 matrix-head-cell text-left px-2 bg-ink-50"
                        style={{ top: 30, height:28 }}>
                      <Tip tip={d.dept.name}>
                        <div className="text-[11px] text-ink-700 font-medium truncate">{d.dept.name}</div>
                      </Tip>
                    </th>
                  )))}
                </tr>
              )}
              {/* Row 3 - Position */}
              <tr>
                {flatPositions.map(p => (
                  <th key={p.id}
                      className="sticky z-30 matrix-head-cell text-center bg-ink-50"
                      style={{ width: COL_W.pos, minWidth: COL_W.pos, maxWidth: COL_W.pos, top: (colVis.div?30:0) + (colVis.dept?28:0), height:42 }}>
                    <Tip tip={`${p.name} · ${departments.find(d=>d.id===p.department_id)?.name||""}`}>
                      <div className="px-1 py-1">
                        <div className="text-[11px] font-semibold text-ink-800 truncate-1">{p.name}</div>
                        <div className="text-[10px] text-ink-500 truncate-1 font-mono mt-0.5">{p.level}</div>
                      </div>
                    </Tip>
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {rows.length === 0 && (
                <tr>
                  <td colSpan={3+flatPositions.length+5} className="text-center py-16">
                    <EmptyState icon="search" title="ไม่พบงานที่ตรงกับเงื่อนไข" subtitle="ลองล้างตัวกรองหรือเปลี่ยน Division / Department"/>
                  </td>
                </tr>
              )}
              {rows.map((sp, rIdx) => {
                const mp = mainProcesses.find(m=>m.id===sp.main_process_id);
                const pr = processes.find(p=>p.id===sp.process_id);
                // Resolve task instance for this sub-process on the selected date.
                // For daily/weekly tasks: look up historical instance by date.
                // For others: use the current task instance.
                const instance = resolveTaskForDate(sp, selectedDate, tasks, taskHistory);
                const task = instance;
                const status = task?.status || "blank";
                const isR = myPositionIds.includes(sp.R);
                const isA = myPositionIds.includes(sp.A);
                const cmts = comments[sp.id] || [];
                // Determine if this row starts a new main process — apply heavier top border
                const prev = rows[rIdx-1];
                const newMp = !prev || prev.main_process_id !== sp.main_process_id;
                const newPr = !prev || prev.process_id !== sp.process_id;
                return (
                  <tr key={sp.id} className={`row group ${newMp ? "border-t-2 border-ink-200" : ""}`}>
                    {/* Sticky Main Process */}
                    <td className="sticky left-0 z-30 bg-white group-hover:bg-ink-50/60 matrix-cell px-3 align-top"
                        style={{ width: COL_W.mp, minWidth: COL_W.mp, maxWidth: COL_W.mp }}>
                      {newMp && (
                        <Tip tip={mp?.name}>
                          <div className="flex items-center gap-1.5">
                            <span className="w-1 h-3.5 rounded-sm bg-indigo-500"/>
                            <span className="text-[12px] font-semibold text-ink-900 truncate-1">{mp?.name}</span>
                          </div>
                        </Tip>
                      )}
                    </td>
                    {/* Sticky Process */}
                    <td className="sticky z-30 bg-white group-hover:bg-ink-50/60 matrix-cell px-3 align-top"
                        style={{ left: COL_W.mp, width: COL_W.pr, minWidth: COL_W.pr, maxWidth: COL_W.pr }}>
                      {newPr && (
                        <Tip tip={pr?.name}>
                          <div className="text-[12px] text-ink-700 truncate-1">{pr?.name}</div>
                        </Tip>
                      )}
                    </td>
                    {/* Sticky Sub-process */}
                    <td className="sticky z-30 bg-white group-hover:bg-ink-50/60 matrix-cell px-3 sticky-shadow-r"
                        style={{ left: COL_W.mp + COL_W.pr, width: COL_W.sp, minWidth: COL_W.sp, maxWidth: COL_W.sp }}>
                      <Tip tip={sp.name}>
                        <div className="text-[12px] font-medium text-ink-900 truncate-1">{sp.name}</div>
                      </Tip>
                      <div className="flex items-center gap-1.5 mt-0.5 text-[10px] text-ink-500">
                        <Badge color="slate" className="!text-[10px]">{sp.frequency}</Badge>
                        <span className="truncate-1">{sp.due_day}</span>
                        {sp.track_status === false && <Tip tip="ไม่ติดตามสถานะใน RACI Matrix"><span className="inline-flex items-center text-slate-500"><Icon name="eye" size={11}/></span></Tip>}
                      </div>
                    </td>
                    {/* RACI cells */}
                    {flatPositions.map(p => {
                      const role = getRaci(sp, p.id);
                      const highlight = role==="R" || role==="A";
                      return (
                        <td key={p.id}
                            className={`matrix-cell text-center align-middle ${highlight?"bg-indigo-50/30":""} group-hover:bg-ink-50/60`}
                            style={{ width: COL_W.pos, minWidth: COL_W.pos, maxWidth: COL_W.pos }}>
                          {role ? <RaciBadge kind={role} size={density==="compact"?"sm":"md"} style={tweaks.badgeStyle}/> : <span className="text-ink-200">·</span>}
                        </td>
                      );
                    })}
                    {/* Due */}
                    {showDailyStatus && colVis.due && (
                      <td className="sticky bg-white group-hover:bg-ink-50/60 matrix-cell px-2 text-center" style={{ right: RIGHT_W-COL_W.due, width: COL_W.due, minWidth: COL_W.due }}>
                        <div className="text-[11.5px] font-mono text-ink-700">{task?.due_date || "—"}</div>
                        {task?.overdue && status !== "S" && <Badge color="rose" className="!text-[9.5px] mt-0.5">เลยกำหนด</Badge>}
                      </td>
                    )}
                    {/* Action button (also shows status colour + label) */}
                    {showDailyStatus && colVis.action && (
                      <td className="sticky bg-white group-hover:bg-ink-50/60 matrix-cell px-2" style={{ right: RIGHT_W-COL_W.due-COL_W.action, width: COL_W.action, minWidth: COL_W.action }}>
                        <ActionButton status={status} isR={isR} isA={isA} role={currentRole} sp={sp}
                          isPastDate={selectedDate !== "2026-05-22"}
                          onP={()=>handleP(sp)} onA={()=>openApprove(sp)} onC={()=>handleC(sp)} onCancelS={()=>handleCancelS(sp)}/>
                      </td>
                    )}
                    {/* Comment / Timeline icons */}
                    {showDailyStatus && colVis.comment && (
                      <td className="sticky bg-white group-hover:bg-ink-50/60 matrix-cell px-2 text-center" style={{ right: 0, width: COL_W.comment, minWidth: COL_W.comment }}>
                        <div className="flex items-center justify-center gap-1">
                          <Tip tip={`ดู Timeline ${sp.frequency} ทุกงวด`}>
                            <button onClick={()=>setTimelineFor(sp)} className="w-6 h-6 rounded-md hover:bg-indigo-50 text-indigo-500">
                              <Icon name="history" size={13}/>
                            </button>
                          </Tip>
                          <Tip tip="ดู Comment / ประวัติการตรวจ">
                            <button onClick={()=>setDrawerFor(sp)} className="relative w-6 h-6 rounded-md hover:bg-ink-100 text-ink-500">
                              <Icon name="message" size={13}/>
                              {cmts.length>0 && <span className="absolute -top-0.5 -right-0.5 min-w-[12px] h-3 px-0.5 bg-rose-500 text-white text-[8.5px] font-bold rounded-full flex items-center justify-center">{cmts.length}</span>}
                            </button>
                          </Tip>
                        </div>
                      </td>
                    )}
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
      )}

      {/* Calendar Status view — daily status across the month */}
      {viewMode === "calendar" && (
        <div className={`${fullscreen ? "mx-5 mb-5" : ""}`}>
          <CalendarStatusView
            subprocesses={subprocesses} mainProcesses={mainProcesses} processes={processes}
            positions={positions} departments={departments} divisions={divisions}
            tasks={tasks} taskHistory={taskHistory}
            setTasks={setTasks} setTaskHistory={setTaskHistory} setAuditLog={setAuditLog}
            period={period} tenant={tenant} currentRole={currentRole} currentUser={currentUser} users={users}
            filterFn={(sp) => rows.includes(sp)}
            comments={comments} setComments={setComments}
            setTimelineFor={setTimelineFor} setDrawerFor={setDrawerFor}
            density={density}
          />
        </div>
      )}

      {/* Footer summary */}
      <div className={`${fullscreen?"mx-5 mb-3":""} mt-3 flex items-center justify-between text-[11.5px] text-ink-500 flex-wrap gap-2`}>
        <div>
          แสดง <strong className="text-ink-800 font-semibold">{rows.length}</strong> Sub-processes
          {viewMode === "raci" && <> · {flatPositions.length} ตำแหน่ง · เลื่อนแนวนอนเพื่อดูตำแหน่งทั้งหมด</>}
          {viewMode === "calendar" && <> · มุมมองปฏิทินรายเดือน · คลิกที่เซลล์วันใดก็ได้เพื่อแก้สถานะ</>}
        </div>
        <div className="flex items-center gap-3">
          <Tip tip="คอลัมน์ทางซ้ายและขวาถูก freeze ไว้"><span className="flex items-center gap-1"><Icon name="columns" size={11}/>Sticky columns</span></Tip>
        </div>
      </div>

      {/* Approve modal */}
      <ApproveModal sp={approveFor} onClose={()=>setApproveFor(null)} onSuccess={()=>approveSuccess(approveFor)} onCorrect={(r)=>approveCorrect(approveFor, r)}/>

      {/* Correction response modal */}
      <CorrectionResponseModal sp={commentFor} comments={commentFor ? (comments[commentFor.id]||[]) : []} onClose={()=>setCommentFor(null)} onSubmit={(t)=>submitCorrectionResponse(commentFor, t)}/>

      {/* Cancel S reason */}
      <ReasonModal open={!!cancelFor} onClose={()=>setCancelFor(null)}
        onConfirm={(r)=>submitCancelS(cancelFor, r)}
        title="ยกเลิกสถานะสำเร็จ" subtitle={cancelFor?.name}
        confirmLabel="ยกเลิกสถานะสำเร็จ" placeholder="กรุณาระบุเหตุผลในการยกเลิกสถานะสำเร็จ" tone="warn"/>

      {/* Comment drawer */}
      <CommentHistoryDrawer sp={drawerFor} onClose={()=>setDrawerFor(null)} comments={drawerFor ? (comments[drawerFor.id]||[]) : []}
        task={drawerFor ? resolveTaskForDate(drawerFor, selectedDate, tasks, taskHistory) : null}
        positions={positions} users={users}/>

      {/* Timeline drawer */}
      <TaskTimelineDrawer sp={timelineFor} onClose={()=>setTimelineFor(null)}
        history={timelineFor ? (taskHistory[timelineFor.id]||[]) : []}
        selectedDate={selectedDate} onPickDate={(d)=>{ setSelectedDate(d); setTimelineFor(null); }}
        positions={positions} users={users}
        currentTask={timelineFor ? tasks.find(t=>t.sub_process_id===timelineFor.id) : null}
      />
    </div>
  );
};

// Action button per status
const ActionButton = ({ status, isR, isA, role, sp, onP, onA, onC, onCancelS, isPastDate }) => {
  const adminBypass = role === "admin";
  const isCorI = !isR && !isA; // C or I or no role on this sub-process
  const noPermTip = "คุณไม่มีสิทธิ์อัปเดตงานนี้ — ต้องเป็นผู้รับผิดชอบ R หรือ ผู้ตรวจสอบ A";

  // "ดูอย่างเดียว" — sub-process has track_status === false
  if (sp && sp.track_status === false) {
    return (
      <Tip tip="งานนี้ไม่ได้ติดตามสถานะ — ตั้งค่า 'Status Tracking' จากหน้า Process Master">
        <button disabled className="w-full h-7 rounded-md bg-slate-50 border border-slate-200 text-slate-500 text-[10.5px] font-medium cursor-not-allowed flex items-center justify-center gap-1">
          <Icon name="eye" size={10}/>ดูอย่างเดียว
        </button>
      </Tip>
    );
  }

  if (status === "blank") {
    if (isPastDate) {
      const can = isR || isA || adminBypass;
      if (!can) {
        return <Tip tip={noPermTip}><button disabled className="w-full h-7 rounded-md border border-dashed border-ink-200 text-ink-300 text-[10.5px] font-medium cursor-not-allowed flex items-center justify-center gap-1"><Icon name="ban" size={10}/>ไม่มีสิทธิ์</button></Tip>;
      }
      return (
        <Tip tip="บันทึกสถานะย้อนหลัง — เริ่มที่ขั้นรอจัดทำ (P)">
          <button onClick={onP} className="w-full h-7 rounded-md border border-dashed border-ink-300 text-ink-600 hover:bg-blue-50 hover:text-blue-700 hover:border-blue-400 text-[11px] font-medium flex items-center justify-center gap-1">
            <Icon name="plus" size={11}/>บันทึกย้อนหลัง
          </button>
        </Tip>
      );
    }
    return <button disabled className="w-full h-7 rounded-md bg-ink-100 text-ink-400 text-[10.5px] font-medium cursor-not-allowed" data-tip="ยังไม่ถึงกำหนด">รอถึงกำหนด</button>;
  }
  if (status === "P") {
    const can = isR || adminBypass;
    return (
      <Tip tip={can?"กดเพื่อส่งให้ผู้ตรวจสอบ":noPermTip}>
        <button onClick={can?onP:undefined} className={`w-full h-7 rounded-md text-[11px] font-semibold flex items-center justify-center gap-1 ${can?"bg-blue-600 hover:bg-blue-700 text-white":"bg-blue-50 text-blue-400 cursor-not-allowed"}`}>
          <Icon name="arrowUp" size={11}/>P · ส่งตรวจ
        </button>
      </Tip>
    );
  }
  if (status === "A") {
    const can = isA || adminBypass;
    return (
      <Tip tip={can?"กดเพื่อเปิดหน้าต่างอนุมัติ":noPermTip}>
        <button onClick={can?onA:undefined} className={`w-full h-7 rounded-md text-[11px] font-semibold flex items-center justify-center gap-1 ${can?"bg-violet-600 hover:bg-violet-700 text-white":"bg-violet-50 text-violet-400 cursor-not-allowed"}`}>
          <Icon name="checkCircle" size={11}/>A · ตรวจสอบ
        </button>
      </Tip>
    );
  }
  if (status === "C") {
    const can = isR || adminBypass;
    return (
      <Tip tip={can?"ดู Comment และตอบกลับ":noPermTip}>
        <button onClick={can?onC:undefined} className={`w-full h-7 rounded-md text-[11px] font-semibold flex items-center justify-center gap-1 ${can?"bg-amber-500 hover:bg-amber-600 text-white":"bg-amber-50 text-amber-400 cursor-not-allowed"}`}>
          <Icon name="message" size={11}/>C · แก้ไข
        </button>
      </Tip>
    );
  }
  if (status === "S") {
    const can = isA || adminBypass;
    return (
      <Tip tip={can?"คลิกเพื่อยกเลิกสถานะสำเร็จ":"สำเร็จแล้ว — เฉพาะ A หรือ Admin เท่านั้นที่ยกเลิกได้"}>
        <button onClick={can?onCancelS:undefined} className={`w-full h-7 rounded-md text-[11px] font-semibold flex items-center justify-center gap-1 ${can?"bg-emerald-100 hover:bg-emerald-200 text-emerald-800":"bg-emerald-50 text-emerald-700 cursor-not-allowed"}`}>
          <Icon name="check" size={11}/>S · สำเร็จ
        </button>
      </Tip>
    );
  }
  return null;
};

const ApproveModal = ({ sp, onClose, onSuccess, onCorrect }) => {
  const [mode, setMode] = useState(null); // "correct" | "success" | null
  const [comment, setComment] = useState("");
  useEffect(() => { if (sp) { setMode(null); setComment(""); } }, [sp]);
  return (
    <Modal open={!!sp} onClose={onClose} title="อนุมัติงาน" subtitle={sp?.name} width={520}
      footer={mode === "correct" ? (
        <>
          <Button variant="secondary" onClick={()=>setMode(null)}>ย้อนกลับ</Button>
          <Button variant="warn" disabled={!comment.trim()} onClick={()=>onCorrect(comment.trim())}>ส่ง Comment ให้แก้ไข</Button>
        </>
      ) : (
        <>
          <Button variant="warn" icon="message" onClick={()=>setMode("correct")}>แก้ไข</Button>
          <Button variant="success" icon="checkCircle" onClick={onSuccess}>สำเร็จ</Button>
        </>
      )}
    >
      {mode === "correct" ? (
        <Field label="วิธีการแก้ไข / สิ่งที่ต้องแก้" required>
          <Textarea rows={5} value={comment} onChange={e=>setComment(e.target.value)} placeholder="ระบุรายละเอียดที่ต้องการให้ผู้รับผิดชอบแก้ไข"/>
        </Field>
      ) : (
        <div>
          <div className="text-[13px] text-ink-700 mb-3">โปรดเลือกผลการตรวจสอบงานนี้</div>
          <div className="grid grid-cols-2 gap-3">
            <button onClick={()=>setMode("correct")} className="text-left p-4 rounded-xl border border-amber-200 bg-amber-50 hover:bg-amber-100">
              <Icon name="message" size={20} className="text-amber-600 mb-1.5"/>
              <div className="font-semibold text-amber-900">แก้ไข</div>
              <div className="text-[11.5px] text-amber-700 mt-0.5">ส่ง Comment กลับให้ผู้รับผิดชอบ (R) แก้ไข</div>
            </button>
            <button onClick={onSuccess} className="text-left p-4 rounded-xl border border-emerald-200 bg-emerald-50 hover:bg-emerald-100">
              <Icon name="checkCircle" size={20} className="text-emerald-600 mb-1.5"/>
              <div className="font-semibold text-emerald-900">สำเร็จ</div>
              <div className="text-[11.5px] text-emerald-700 mt-0.5">อนุมัติงาน ปิดสถานะเป็น S</div>
            </button>
          </div>
        </div>
      )}
    </Modal>
  );
};

const CorrectionResponseModal = ({ sp, comments, onClose, onSubmit }) => {
  const [text, setText] = useState("");
  useEffect(() => { if (sp) setText(""); }, [sp]);
  const lastCorrection = comments.filter(c => c.type === "correction").slice(-1)[0];
  return (
    <Modal open={!!sp} onClose={onClose} title="ตอบกลับ Comment" subtitle={sp?.name} width={560}
      footer={<>
        <Button variant="secondary" onClick={onClose}>ยกเลิก</Button>
        <Button variant="primary" disabled={!text.trim()} onClick={()=>onSubmit(text.trim())} iconRight="arrowRight">ส่งกลับให้ผู้ตรวจสอบ</Button>
      </>}>
      {lastCorrection && (
        <div className="rounded-xl border border-amber-200 bg-amber-50 p-3 mb-4">
          <div className="text-[10.5px] uppercase tracking-wider text-amber-700 font-semibold flex items-center gap-1"><Icon name="message" size={11}/>Comment จากผู้ตรวจสอบ · {lastCorrection.author_name}</div>
          <div className="text-[12.5px] text-amber-900 mt-1">{lastCorrection.text}</div>
          <div className="text-[10.5px] font-mono text-amber-700 mt-1">{lastCorrection.at}</div>
        </div>
      )}
      <Field label="วิธีการแก้ไข / คำตอบกลับ" required hint="จะถูกส่งให้ผู้ตรวจสอบ (A) ตรวจอีกครั้ง">
        <Textarea rows={5} value={text} onChange={e=>setText(e.target.value)} placeholder="อธิบายสิ่งที่ได้แก้ไขให้ผู้ตรวจสอบทราบ"/>
      </Field>
    </Modal>
  );
};

const CommentHistoryDrawer = ({ sp, onClose, comments, task, positions, users }) => {
  if (!sp) return null;
  const r = positions.find(p=>p.id===sp.R);
  const a = positions.find(p=>p.id===sp.A);
  const rU = users.find(u=>u.positions.includes(sp.R));
  const aU = users.find(u=>u.positions.includes(sp.A));
  return (
    <Drawer open={!!sp} onClose={onClose} title={sp.name} subtitle="ประวัติ Comment และการเปลี่ยนสถานะ" width={420}>
      <div className="p-5 space-y-4">
        <div className="rounded-xl border border-ink-100 bg-ink-50/60 p-3">
          <div className="text-[10.5px] uppercase tracking-wider text-ink-500 font-semibold mb-2">Task Info</div>
          <div className="grid grid-cols-2 gap-2 text-[12px]">
            <div><div className="text-[10px] text-ink-400">Due</div><div className="font-mono">{task?.due_date}</div></div>
            <div><div className="text-[10px] text-ink-400">Status</div><div><StatusPill status={task?.status || "blank"}/></div></div>
            <div className="col-span-2"><div className="text-[10px] text-ink-400">R · ผู้รับผิดชอบ</div><div className="flex items-center gap-1.5 mt-0.5"><RaciBadge kind="R" size="sm"/><span>{rU?.full_name || "—"} · {r?.name}</span></div></div>
            <div className="col-span-2"><div className="text-[10px] text-ink-400">A · ผู้ตรวจสอบ</div><div className="flex items-center gap-1.5 mt-0.5"><RaciBadge kind="A" size="sm"/><span>{aU?.full_name || "—"} · {a?.name}</span></div></div>
          </div>
        </div>

        <div>
          <div className="text-[10.5px] uppercase tracking-wider text-ink-500 font-semibold mb-2">ประวัติ Comment ({comments.length})</div>
          {comments.length === 0 ? (
            <EmptyState icon="message" title="ยังไม่มี Comment" subtitle="เมื่อมีการขอแก้ไข Comment จะปรากฏที่นี่"/>
          ) : (
            <div className="space-y-3">
              {/* Group by task_date desc, then render comments per group in chronological order */}
              {Object.entries(comments.reduce((acc, c) => {
                const k = c.task_date || c.at?.slice(0,10) || "—";
                (acc[k] = acc[k] || []).push(c);
                return acc;
              }, {})).sort((a,b) => b[0].localeCompare(a[0])).map(([date, cs]) => (
                <div key={date} className="rounded-xl border border-ink-100 overflow-hidden">
                  <div className="flex items-center gap-2 px-3 py-2 bg-ink-50/60 border-b border-ink-100">
                    <Icon name="calendar" size={11} className="text-brand-600"/>
                    <span className="text-[11.5px] font-semibold text-ink-900">งวด {date}</span>
                    <Badge color="slate" className="ml-auto !text-[10px]">{cs.length} comment</Badge>
                  </div>
                  <div className="p-2 space-y-1.5">
                    {cs.slice().reverse().map(c => {
                      const tone = c.type === "correction" ? { c:"amber", i:"message", l:"ขอให้แก้ไข" }
                                : c.type === "response" ? { c:"blue", i:"arrowUp", l:"คำตอบกลับจากผู้รับผิดชอบ" }
                                : c.type === "cancel_success" ? { c:"rose", i:"refresh", l:"ยกเลิกสถานะสำเร็จ" }
                                : { c:"slate", i:"info", l:"System" };
                      return (
                        <div key={c.id} className={`border rounded-lg p-2.5 ${
                          tone.c==="amber"?"bg-amber-50 border-amber-200":tone.c==="blue"?"bg-blue-50 border-blue-200":tone.c==="rose"?"bg-rose-50 border-rose-200":"bg-ink-50 border-ink-200"}`}>
                          <div className="flex items-center justify-between gap-2 mb-1">
                            <div className="flex items-center gap-1.5"><Icon name={tone.i} size={11}/>
                              <Badge color={tone.c}>{tone.l}</Badge>
                            </div>
                            <div className="font-mono text-[10px] text-ink-500">{c.at}</div>
                          </div>
                          <div className="text-[12px] text-ink-800">{c.text}</div>
                          <div className="text-[10.5px] text-ink-500 mt-1">โดย {c.author_name}</div>
                        </div>
                      );
                    })}
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </Drawer>
  );
};

const ColumnVisibilityPopover = ({ colVis, setColVis, onClose }) => {
  const ref = useRef(null);
  useEffect(() => {
    const cb = (e) => { if (!ref.current?.contains(e.target)) onClose(); };
    document.addEventListener("mousedown", cb);
    return () => document.removeEventListener("mousedown", cb);
  }, []);
  const cols = [
    { k:"div", l:"Division header" },
    { k:"dept", l:"Department header" },
    { k:"due", l:"Due date" },
    { k:"action", l:"Action button (พร้อมสถานะ)" },
    { k:"comment", l:"Timeline / Comment" },
  ];
  return (
    <div ref={ref} className="absolute right-0 top-full mt-2 w-[240px] bg-white border border-ink-200 rounded-xl shadow-xl z-[200] p-2 pop">
      <div className="text-[10px] uppercase tracking-wider text-ink-400 font-semibold px-2 py-1.5">Show / hide columns</div>
      {cols.map(c => (
        <label key={c.k} className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-ink-50 cursor-pointer">
          <Checkbox checked={colVis[c.k]} onChange={()=>setColVis(p=>({...p, [c.k]: !p[c.k]}))} label={<span className="text-[12px] text-ink-700">{c.l}</span>}/>
        </label>
      ))}
    </div>
  );
};

// helpers
const nowStamp = () => {
  const d = new Date();
  const pad = (n) => String(n).padStart(2,"0");
  return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
};
const cid = () => "c"+Math.random().toString(36).slice(2,8);
const roleLabel = (r) => ROLE_META[r]?.label || r;

const formatDateThai = (s) => {
  if (!s) return "";
  const m = ["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."];
  const [y, mo, d] = s.split("-").map(Number);
  return `${d} ${m[mo-1]} ${(y+543).toString().slice(-2)}`;
};

// Resolve the task instance to display for a given sub-process on a given calendar date.
// 1. Look in history for an exact match (most accurate for daily / weekly).
// 2. For monthly: match instance in the same month.
// 3. For weekly: instance in the same ISO-week window.
// 4. If nothing found: fall back to current snapshot (tasks state).
const resolveTaskForDate = (sp, dateStr, tasksState, history) => {
  const hist = history[sp.id] || [];
  if (sp.frequency === "daily") {
    const found = hist.find(h => h.date === dateStr);
    if (found) return { ...found, due_date: found.date, sub_process_id: sp.id };
    // weekend or no record → blank
    return { sub_process_id:sp.id, status:"blank", due_date: dateStr, updated:"" };
  }
  if (sp.frequency === "monthly" || sp.frequency === "quarterly") {
    const ymKey = dateStr.slice(0,7);
    const found = hist.find(h => h.date.slice(0,7) === ymKey);
    if (found) return { ...found, due_date: found.date, sub_process_id: sp.id };
  }
  if (sp.frequency === "weekly") {
    // find nearest historical entry within ±7 days
    const target = new Date(dateStr);
    const found = hist.find(h => Math.abs((new Date(h.date) - target)/(86400000)) <= 6);
    if (found) return { ...found, due_date: found.date, sub_process_id: sp.id };
  }
  // ad-hoc — current snapshot
  const cur = tasksState.find(t => t.sub_process_id === sp.id);
  return cur || { sub_process_id:sp.id, status:"blank", due_date: dateStr, updated:"" };
};

// Date navigator widget for the matrix filter bar
const DateNavigator = ({ value, onChange }) => {
  const shift = (n) => {
    const d = new Date(value); d.setDate(d.getDate() + n);
    onChange(d.toISOString().slice(0,10));
  };
  const isToday = value === "2026-05-22"; // demo "today"
  return (
    <div className="inline-flex items-stretch h-9 border border-ink-200 rounded-lg bg-white overflow-hidden">
      <button onClick={()=>shift(-1)} className="px-2 hover:bg-ink-50 text-ink-500 border-r border-ink-200"><Icon name="chevronLeft" size={13}/></button>
      <div className="flex items-center gap-2 px-2.5">
        <Icon name="calendar" size={12} className="text-brand-600"/>
        <input type="date" value={value} onChange={e=>onChange(e.target.value)} className="bg-transparent text-[12.5px] font-medium text-ink-800 focus:outline-none cursor-pointer"/>
      </div>
      <button onClick={()=>shift(1)} className="px-2 hover:bg-ink-50 text-ink-500 border-l border-ink-200"><Icon name="chevronRight" size={13}/></button>
      {!isToday && (
        <button onClick={()=>onChange("2026-05-22")} className="px-2.5 text-[11px] font-medium text-brand-600 hover:bg-brand-50 border-l border-ink-200">วันนี้</button>
      )}
    </div>
  );
};

// Timeline drawer — historical task instances for a sub-process
const TaskTimelineDrawer = ({ sp, onClose, history, selectedDate, onPickDate, positions, users, currentTask }) => {
  if (!sp) return null;
  const rPos = positions.find(p=>p.id===sp.R);
  const aPos = positions.find(p=>p.id===sp.A);
  const rUser = users.find(u=>u.positions.includes(sp.R));
  const aUser = users.find(u=>u.positions.includes(sp.A));

  // Summary stats from history
  const stats = history.reduce((acc, h) => {
    acc.total++;
    if (h.status === "S") acc.s++;
    else if (h.status === "C") acc.c++;
    else if (h.status === "A") acc.a++;
    else if (h.status === "P") acc.p++;
    else acc.b++;
    return acc;
  }, { total:0, s:0, a:0, c:0, p:0, b:0 });
  const completion = stats.total ? Math.round(stats.s / stats.total * 100) : 0;

  // For daily — build a calendar heatmap of the last 5 weeks
  const calendar = useMemo(() => {
    if (sp.frequency !== "daily") return null;
    const days = [];
    const start = new Date("2026-05-22");
    // walk back 34 days then align week start to Monday for grid
    const grid = [];
    const startBack = new Date(start); startBack.setDate(startBack.getDate() - 34);
    // align to Monday
    while (startBack.getDay() !== 1) startBack.setDate(startBack.getDate() - 1);
    for (let i = 0; i < 42; i++) {
      const d = new Date(startBack); d.setDate(d.getDate() + i);
      const ds = d.toISOString().slice(0,10);
      const h = history.find(x => x.date === ds);
      const isFuture = d > start;
      const isWeekend = d.getDay() === 0 || d.getDay() === 6;
      grid.push({ date: ds, dayNum: d.getDate(), inMonth: true, h, isFuture, isWeekend, isToday: ds === "2026-05-22" });
    }
    return grid;
  }, [sp.id, history]);

  return (
    <Drawer open={!!sp} onClose={onClose} title={sp.name} subtitle={`Timeline · ${sp.frequency} · ${sp.due_day}`} width={520}>
      <div className="p-5 space-y-5">
        {/* Header info */}
        <div className="rounded-xl border border-ink-100 bg-gradient-to-br from-ink-50 to-white p-4">
          <div className="grid grid-cols-3 gap-2">
            <div>
              <div className="text-[10px] uppercase tracking-wider text-ink-500">Frequency</div>
              <div className="text-[13px] font-semibold text-ink-900 capitalize">{sp.frequency}</div>
            </div>
            <div>
              <div className="text-[10px] uppercase tracking-wider text-ink-500">Due rule</div>
              <div className="text-[13px] font-semibold text-ink-900">{sp.due_day}</div>
            </div>
            <div>
              <div className="text-[10px] uppercase tracking-wider text-ink-500">งวดทั้งหมด</div>
              <div className="text-[13px] font-semibold text-ink-900 font-mono">{stats.total}</div>
            </div>
          </div>
          <div className="mt-3 grid grid-cols-2 gap-2 text-[11.5px]">
            <div className="flex items-center gap-1.5"><RaciBadge kind="R" size="sm"/><span className="text-ink-700">{rUser?.full_name || "—"}</span></div>
            <div className="flex items-center gap-1.5"><RaciBadge kind="A" size="sm"/><span className="text-ink-700">{aUser?.full_name || "—"}</span></div>
          </div>
        </div>

        {/* Completion bar */}
        <div className="rounded-xl border border-ink-100 p-4">
          <div className="flex items-center justify-between mb-2">
            <div className="text-[12px] font-semibold text-ink-800">Completion rate (ทั้งช่วง)</div>
            <div className="font-mono text-[16px] font-bold text-emerald-600">{completion}%</div>
          </div>
          <div className="h-2 bg-ink-100 rounded-full overflow-hidden flex">
            <div className="bg-emerald-500" style={{width:`${(stats.s/stats.total)*100||0}%`}}/>
            <div className="bg-violet-500" style={{width:`${(stats.a/stats.total)*100||0}%`}}/>
            <div className="bg-amber-500" style={{width:`${(stats.c/stats.total)*100||0}%`}}/>
            <div className="bg-blue-500" style={{width:`${(stats.p/stats.total)*100||0}%`}}/>
            <div className="bg-ink-300" style={{width:`${(stats.b/stats.total)*100||0}%`}}/>
          </div>
          <div className="mt-2 flex items-center gap-2 text-[10.5px] text-ink-600 flex-wrap">
            <Stat icon="checkCircle" color="text-emerald-600" v={stats.s} l="สำเร็จ"/>
            <Stat icon="checkCircle" color="text-violet-600" v={stats.a} l="รอตรวจ"/>
            <Stat icon="message" color="text-amber-600" v={stats.c} l="แก้ไข"/>
            <Stat icon="arrowUp" color="text-blue-600" v={stats.p} l="รอจัดทำ"/>
            <Stat icon="clock" color="text-ink-500" v={stats.b} l="ยังไม่ถึง"/>
          </div>
        </div>

        {/* Calendar heatmap for daily */}
        {sp.frequency === "daily" && calendar && (
          <div>
            <div className="text-[11.5px] uppercase tracking-wider text-ink-500 font-semibold mb-2 flex items-center justify-between">
              <span>ปฏิทินสถานะรายวัน · 35 วันย้อนหลัง</span>
              <span className="text-[10px] normal-case tracking-normal text-ink-400">คลิกวันที่เพื่อดูในตาราง Matrix</span>
            </div>
            <div className="rounded-xl border border-ink-100 p-3 bg-white">
              <div className="grid grid-cols-7 gap-1.5 mb-1.5">
                {["จ","อ","พ","พฤ","ศ","ส","อา"].map(d => (
                  <div key={d} className="text-[10px] text-center text-ink-400 font-medium">{d}</div>
                ))}
              </div>
              <div className="grid grid-cols-7 gap-1.5">
                {calendar.map((c,i) => {
                  const isSelected = c.date === selectedDate;
                  if (c.isFuture) return <div key={i} className="aspect-square rounded-md bg-ink-50 text-ink-300 flex items-center justify-center text-[10px]">{c.dayNum}</div>;
                  if (c.isWeekend) return <div key={i} className="aspect-square rounded-md bg-ink-50/40 text-ink-300 flex items-center justify-center text-[10px]" data-tip={`${c.date} (วันหยุด)`}>{c.dayNum}</div>;
                  const status = c.h?.status || "blank";
                  const m = STATUS_META[status];
                  return (
                    <button key={i} onClick={()=>onPickDate(c.date)}
                      className={`relative aspect-square rounded-md flex items-center justify-center text-[10.5px] font-semibold transition border ${isSelected?"ring-2 ring-brand-500 border-brand-500":"border-transparent hover:scale-105"}`}
                      style={{ background: status === "blank" ? "#f1f3f9" : status === "S" ? "#d1fae5" : status === "A" ? "#ede9fe" : status === "C" ? "#fef3c7" : status === "P" ? "#dbeafe" : "#f1f3f9",
                               color: status === "S" ? "#065f46" : status === "A" ? "#5b21b6" : status === "C" ? "#92400e" : status === "P" ? "#1e40af" : "#94a3b8" }}
                      data-tip={`${c.date} · ${m.label}`}>
                      <span className="absolute top-0.5 left-1 text-[8px] opacity-60">{c.dayNum}</span>
                      <span>{status === "blank" ? "·" : m.short}</span>
                      {c.isToday && <span className="absolute -bottom-0.5 left-1/2 -translate-x-1/2 w-1 h-1 rounded-full bg-rose-500"/>}
                    </button>
                  );
                })}
              </div>
              <div className="mt-3 flex items-center gap-2.5 flex-wrap text-[10px] text-ink-500">
                {["S","A","C","P","blank"].map(k => (
                  <div key={k} className="flex items-center gap-1">
                    <span className="w-3 h-3 rounded-sm border" style={{ background: k === "blank" ? "#f1f3f9" : k === "S" ? "#d1fae5" : k === "A" ? "#ede9fe" : k === "C" ? "#fef3c7" : "#dbeafe", borderColor: "rgba(0,0,0,.08)" }}/>
                    <span>{STATUS_META[k].label}</span>
                  </div>
                ))}
              </div>
            </div>
          </div>
        )}

        {/* List for monthly / weekly / quarterly / ad-hoc */}
        {sp.frequency !== "daily" && (
          <div>
            <div className="text-[11.5px] uppercase tracking-wider text-ink-500 font-semibold mb-2">ประวัติงวดที่ผ่านมา</div>
            <div className="space-y-1.5">
              {history.slice().reverse().map((h,i) => {
                const isSel = h.date === selectedDate;
                return (
                  <button key={i} onClick={()=>onPickDate(h.date)}
                    className={`w-full text-left flex items-center gap-3 px-3 py-2 rounded-lg border transition ${isSel?"border-brand-300 bg-brand-50":"border-ink-100 hover:bg-ink-50"}`}>
                    <div className="font-mono text-[11.5px] text-ink-700 w-24 shrink-0">{formatDateThai(h.date)}</div>
                    <StatusPill status={h.status}/>
                    <div className="ml-auto text-[10.5px] text-ink-500 font-mono">{h.updated || "—"}</div>
                    {h.has_comment && <Icon name="message" size={12} className="text-amber-500"/>}
                  </button>
                );
              })}
            </div>
          </div>
        )}
      </div>
    </Drawer>
  );
};

const Stat = ({ icon, color, v, l }) => (
  <div className="flex items-center gap-1 px-2 py-0.5 bg-ink-50 rounded">
    <Icon name={icon} size={10} className={color}/>
    <span className="font-mono font-semibold text-ink-800">{v}</span>
    <span className="text-ink-500">{l}</span>
  </div>
);

Object.assign(window, { RaciMatrixPage });
