// ============ Views ============

// ---- Task detail modal ----

// Build Outlook deep link - uses webLink directly (most reliable)
function outlookLink(webLink, graphId) {
  // Use webLink directly from Microsoft Graph - it's the OWA deep link
  if (webLink) return webLink;
  if (graphId) return 'https://outlook.office365.com/owa/?ItemID=' + encodeURIComponent(graphId) + '&exvsurl=1&viewmodel=ReadMessageItem';
  return null;
}

function openInOutlook(webLink, graphId) {
  const link = outlookLink(webLink, graphId);
  if (link) window.open(link, '_blank');
}



// Strip markdown formatting from AI-generated text
function stripMd(text) {
  if (!text) return text;
  return text.replace(/\*\*([^*]+)\*\*/g, '$1').replace(/\*([^*]+)\*/g, '$1').replace(/^#+\s*/gm, '').trim();
}


function ReplyCard({ reply, idx, copied, setCopied, sending, sent, onCopy, onSend, canSend }) {
  const [editing, setEditing] = useState(false);
  const [editText, setEditText] = useState(reply.text);

  const handleEdit = () => {
    setEditText(reply.text);
    setEditing(true);
  };

  const sendText = editing ? editText : reply.text;

  return (
    <div className="reply-card">
      <div className="rl-head">
        <span className="rl-label">Option {idx+1} · {reply.label}</span>
        <div style={{display:'flex', gap:6, flexWrap:'wrap'}}>
          <button className="btn tiny ghost" onClick={() => onCopy(sendText, 'r'+idx)}>
            <I.copy/> {copied === 'r'+idx ? 'Copied' : 'Copy'}
          </button>
          <button className="btn tiny ghost" onClick={handleEdit} title="Edit before sending" style={{color:'#6366f1'}}>
            ✏ Edit
          </button>
          {canSend && (
            <button className="btn tiny primary" onClick={() => onSend({...reply, text: sendText}, idx)} disabled={sending === idx}>
              <I.send/> {sending === idx ? 'Sending…' : sent === idx ? 'Sent ✓' : 'Send reply'}
            </button>
          )}
        </div>
      </div>
      {editing ? (
        <div>
          <textarea value={editText} onChange={e => setEditText(e.target.value)}
            style={{width:'100%', minHeight:160, padding:'10px 12px', border:'1px solid #e2e8f0', borderRadius:6,
              fontSize:13, lineHeight:1.6, fontFamily:'inherit', resize:'vertical', boxSizing:'border-box'}}
          />
          <div style={{display:'flex', gap:6, marginTop:6}}>
            <button className="btn tiny ghost" onClick={() => setEditing(false)}>Done editing</button>
            <button className="btn tiny ghost" onClick={() => setEditText(reply.text)} style={{color:'#9ca3af'}}>Reset</button>
          </div>
        </div>
      ) : (
        <pre style={{cursor:'pointer'}} onClick={handleEdit} title="Click to edit">{reply.text}</pre>
      )}
    </div>
  );
}


function TaskModal({ task, thread, onClose, onCheck, onSend }) {
  const [copied, setCopied] = useState(null);
  const [sending, setSending] = useState(null);
  const [sent, setSent]     = useState(null);
  const [body, setBody]     = useState(null);
  const [loadingBody, setLoadingBody] = useState(false);

  // On-demand replies (Task 4)
  const [showReplies, setShowReplies]     = useState(false);
  const [loadingReplies, setLoadingReplies] = useState(false);
  const [replies, setReplies]             = useState(task.replies?.length > 0 ? task.replies : null);

  // Fetch full email body on open
  useEffect(() => {
    if (!task.graphId) return;
    setLoadingBody(true);
    api.get('/api/email/body?graphId=' + encodeURIComponent(task.graphId))
      .then(d => setBody(d.body))
      .catch(() => setBody(null))
      .finally(() => setLoadingBody(false));
  }, [task.graphId]);

  const handleGenerateReplies = async () => {
    if (replies?.length > 0) { setShowReplies(true); return; }
    setLoadingReplies(true);
    try {
      const { replies: fetched } = await api.post('/api/ai/replies', {
        graphId: task.graphId,
        subject: task.subject,
        from: task.from,
        snippet: task.snippet,
      });
      setReplies(fetched);
      setShowReplies(true);
    } catch (err) {
      alert('Could not generate replies: ' + err.message);
    } finally {
      setLoadingReplies(false);
    }
  };

  const copy = (text, label) => {
    navigator.clipboard?.writeText(text);
    setCopied(label);
    setTimeout(() => setCopied(null), 1500);
  };

  const handleSend = async (reply, idx) => {
    if (!task.graphId && !task.internetMessageId && !task.messageId) { copy(reply.text, 'r'+idx); return; }
    setSending(idx);
    try {
      await api.post('/api/email/reply', { graphId: task.graphId, internetMessageId: task.internetMessageId || task.messageId, replyText: reply.text });
    } catch (err) {
      // Graph /reply returns 204 No Content which may cause parse errors - treat as success if no network error
      if (!err.message?.includes('HTTP') && !err.message?.includes('network') && !err.message?.includes('failed')) {
        // Likely a successful send with empty response - continue
      } else {
        setSending(null);
        alert('Send failed: ' + err.message);
        return;
      }
    }
    setSent(idx);
    setTimeout(() => setSent(null), 3000);
    onSend?.();
    setSending(null);
  };

  return (
    <Modal onClose={onClose}>
      <div className="modal-head">
        <div style={{display:'flex', alignItems:'center', gap:8, marginBottom:8}}>
          <Badge kind={thread.tag}>{thread.tag.toUpperCase()}</Badge>
          <Badge>{stripMd(thread.title)}</Badge>
          {task.hasAttachments && <Badge><I.clip/> Attachment</Badge>}
        </div>
        <h2>{task.subject}</h2>
        <div className="from-line">From {task.from} &lt;{task.fromEmail}&gt; · {formatDate(task.date)}</div>
      </div>

      <div className="modal-body">
        <div className="modal-section">
          <div className="label">Email body</div>
          {loadingBody
            ? <div style={{color:'#888',padding:'8px 0',fontSize:13}}>Loading…</div>
            : body
              ? <div style={{fontSize:13,lineHeight:1.6,maxHeight:320,overflowY:'auto',background:'#f9f8f6',border:'1px solid #e8e7e3',borderRadius:6,padding:'10px 14px',whiteSpace:'pre-wrap'}} dangerouslySetInnerHTML={{__html: body}} />
              : <div className="snippet">{task.snippet || '(no preview available)'}</div>
          }
        </div>

        <div className="modal-section">
          <div className="label">Suggested action <span style={{color:'var(--violet)', textTransform:'none', letterSpacing:0}}><I.ai/> AI generated</span></div>
          <div className="snippet" style={{background:'var(--violet-bg)', borderColor:'transparent'}}>{task.action}</div>
        </div>

        <div className="modal-section">
          <div className="label">On done → move to folder</div>
          <div className="folder-pill"><I.folder className="ic"/> {task.folder || thread.folder || '— stays in inbox —'}</div>
        </div>

        <div className="modal-section">
          {!showReplies ? (
            <button onClick={handleGenerateReplies} disabled={loadingReplies}
              style={{display:'inline-flex',alignItems:'center',justifyContent:'center',gap:8,width:'100%',padding:'12px 20px',fontSize:14,fontWeight:700,borderRadius:8,background:loadingReplies?'#94a3b8':'#1a1a18',color:'#fff',border:'none',cursor:loadingReplies?'default':'pointer',transition:'background .15s'}}>
              <I.sparkle/> {loadingReplies ? 'Generating reply options…' : '✨ Generate AI Reply Options'}
            </button>
          ) : (
            <>
              <div className="label"><I.sparkle/>&nbsp; AI reply suggestions</div>
              {(replies || []).map((r, i) => (
                <ReplyCard key={i} reply={r} idx={i}
                  copied={copied} setCopied={setCopied}
                  sending={sending} sent={sent}
                  onCopy={copy}
                  onSend={handleSend}
                  canSend={!!(task.graphId || task.internetMessageId || task.messageId)}
                />
              ))}
            </>
          )}
        </div>
      </div>

      <div className="modal-foot">
        <button className="btn ghost" onClick={onClose}>Close</button>
        <div style={{display:'flex', gap:8}}>
          {(task.webLink || task.graphId) && (
            <a className="btn" href={outlookLink(task.webLink, task.graphId)} onClick={e=>{e.preventDefault();openInOutlook(task.webLink, task.graphId);}} target="_blank" rel="noreferrer">
              📧 Open in Outlook
            </a>
          )}
          <button className="btn primary" onClick={() => { onCheck(); onClose(); }}>
            <I.check/> Mark done
          </button>
        </div>
      </div>
    </Modal>
  );
}

// ---- Follow-up task modal ----
function AddFollowUpModal({ threadId, threadTitle, onClose, onAdd }) {
  const [subject, setSubject] = useState('Follow-up required');
  const [note, setNote]       = useState('');
  const [busy, setBusy]       = useState(false);

  const submit = async () => {
    setBusy(true);
    try {
      await api.post('/api/task/followup', { threadId, subject, note });
      onAdd();
      onClose();
    } catch (err) {
      alert(err.message);
      setBusy(false);
    }
  };

  return (
    <Modal onClose={onClose}>
      <div className="modal-head">
        <h2>Add follow-up task</h2>
        <div className="from-line">Thread: {threadTitle}</div>
      </div>
      <div className="modal-body">
        <div className="modal-section">
          <div className="label">Task label</div>
          <input type="text" value={subject} onChange={e => setSubject(e.target.value)} />
        </div>
        <div className="modal-section">
          <div className="label">Notes</div>
          <textarea rows="3" value={note} onChange={e => setNote(e.target.value)} style={{width:'100%', resize:'vertical'}} />
        </div>
      </div>
      <div className="modal-foot">
        <button className="btn ghost" onClick={onClose}>Cancel</button>
        <button className="btn primary" onClick={submit} disabled={busy || !subject.trim()}>
          {busy ? 'Adding…' : 'Add task'}
        </button>
      </div>
    </Modal>
  );
}

// ---- TaskRow ----
function TaskRow({ task, thread, onCheck, onOpen, onRefresh }) {
  const [editing, setEditing] = useState(false);
  const [editSubject, setEditSubject] = useState(task.subject || '');
  const [editAction, setEditAction] = useState(task.action || '');
  const [saving, setSaving] = useState(false);
  const [confirmDelete, setConfirmDelete] = useState(false);

  // ---- Manual task card (distinct amber style) ----
  if (task.isManual) {
    const isStandalone = !!task.isStandalone;
    const updateRoute = isStandalone ? '/api/task/standalone/update' : '/api/task/manual/update';
    const deleteRoute = isStandalone ? '/api/task/standalone/delete' : '/api/task/manual/delete';
    const updatePayload = isStandalone
      ? { id: task.id, subject: editSubject, action: editAction }
      : { threadId: thread.id, taskId: task.id, subject: editSubject, action: editAction };
    const deletePayload = isStandalone
      ? { id: task.id }
      : { threadId: thread.id, taskId: task.id };

    const handleSaveEdit = async () => {
      setSaving(true);
      try {
        await api.post(updateRoute, updatePayload);
        onRefresh?.();
        setEditing(false);
      } catch (err) {
        alert('Save failed: ' + err.message);
      } finally {
        setSaving(false);
      }
    };

    const handleDelete = (e) => {
      e.stopPropagation();
      setConfirmDelete(true);
    };

    return (
      <>
        <div className={`task${task.doneAt ? ' done' : ''}`}
          style={{ borderLeft: '4px solid #f59e0b', background: '#fffbeb', gridTemplateColumns: '28px 1fr' }}>
          <div style={{paddingTop:2}}>
            <Check checked={!!task.doneAt} onClick={onCheck} />
          </div>
          <div className="task-body" style={{ flex: 1 }}>
            {editing ? (
              <div>
                <input type="text" value={editSubject}
                  onChange={e => setEditSubject(e.target.value)}
                  style={{ width: '100%', marginBottom: 6 }}
                  placeholder="Task label" autoFocus />
                <input type="text" value={editAction}
                  onChange={e => setEditAction(e.target.value)}
                  style={{ width: '100%', marginBottom: 8 }}
                  placeholder="Notes / action" />
                <div style={{ display: 'flex', gap: 6 }}>
                  <button className="btn tiny primary" onClick={handleSaveEdit}
                    disabled={saving || !editSubject.trim()}>
                    {saving ? 'Saving…' : 'Save'}
                  </button>
                  <button className="btn tiny ghost" onClick={() => {
                    setEditing(false);
                    setEditSubject(task.subject || '');
                    setEditAction(task.action || '');
                  }}>Cancel</button>
                </div>
              </div>
            ) : (
              <>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
                  <span className="task-title">{task.subject}</span>
                  <span style={{
                    display: 'inline-block', background: '#f59e0b', color: '#fff',
                    fontSize: 10, fontWeight: 700, padding: '1px 6px', borderRadius: 4,
                    letterSpacing: '0.06em', textTransform: 'uppercase',
                  }}>User Task</span>
                </div>
                {task.action && <div className="task-action">{task.action}</div>}
                <div className="task-row">
                  <span style={{ fontFamily: 'var(--mono)', fontSize: 11 }}>{formatDate(task.date || task.createdAt)}</span>
                  {task.rolledOver && <Badge kind="rolled">↻ rolled over</Badge>}
                </div>
              </>
            )}
          </div>
          {!editing && (
            <div style={{ display: 'flex', gap: 4, flexShrink: 0, paddingTop: 2 }}>
              <button className="btn tiny ghost" title="Edit"
                onClick={e => { e.stopPropagation(); setEditing(true); }}>✏️</button>
              <button className="btn tiny ghost" title="Delete"
                onClick={handleDelete}>🗑️</button>
            </div>
          )}
        </div>
        {confirmDelete && (
          <ConfirmDialog
            title="Are you sure?"
            body="This will remove this task from your list."
            confirmLabel="Yes, remove"
            danger
            onCancel={() => setConfirmDelete(false)}
            onConfirm={async () => {
              setConfirmDelete(false);
              try {
                await api.post(deleteRoute, deletePayload);
                onRefresh?.();
              } catch (err) {
                alert('Delete failed: ' + err.message);
              }
            }}
          />
        )}
      </>
    );
  }

  // ---- Standard email task card ----
  return (
    <div className={`task ${task.doneAt ? 'done' : ''}`} onClick={() => onOpen(task, thread)}>
      <div>
        <Check checked={!!task.doneAt} onClick={onCheck} />
      </div>
      <div className="task-body">
        <div className="task-title">{task.subject}</div>
        {task.action && <div className="task-action">{task.action}</div>}
        <div className="task-row">
          <span>{task.from}</span>
          <span className="sep">·</span>
          <span style={{fontFamily:'var(--mono)', fontSize:11}}>{formatDate(task.date)}</span>
          {task.hasAttachments && <><span className="sep">·</span><span style={{display:'inline-flex', alignItems:'center', gap:3}}><I.clip/> attachment</span></>}
          {task.tags?.map(t => <Badge key={t} kind={t.toLowerCase().replace(/[^a-z]/g,'')}>{t}</Badge>)}
          {task.isNew && <Badge kind="new">NEW</Badge>}
          {task.rolledOver && <Badge kind="rolled">↻ rolled over</Badge>}
          {(task.webLink || task.graphId) && <><span className="sep">·</span><a href={outlookLink(task.webLink, task.graphId)} onClick={e=>{e.preventDefault();openInOutlook(task.webLink, task.graphId);}} target="_blank" rel="noreferrer" onClick={e => e.stopPropagation()} style={{color:'#2563eb',fontWeight:600}}>Open in Outlook ↗</a></>}
        </div>
      </div>
    </div>
  );
}

// ---- Thread ----
function Thread({ thread, onCheck, onOpenTask, onToggle, onRefresh, compact, isExpanded }) {
  const [showAddFU, setShowAddFU] = useState(false);

  // Filter out soft-deleted manual tasks for display
  const visibleTasks = thread.tasks.filter(t => !t.deletedAt);
  const openCount = visibleTasks.filter(t => !t.doneAt).length;

  // Compact row — richer single-line with check-all for thread
  if (compact && !isExpanded) {
    const firstTask = visibleTasks.find(t => !t.doneAt);
    const allDone = visibleTasks.length > 0 && visibleTasks.every(t => t.doneAt);
    return (
      <div className={`thread ${thread.tag}`} data-open="false" style={{marginBottom:4}}>
        <div className="thread-head" style={{gap:8,padding:'8px 12px'}}>
          {/* Per-thread check button */}
          <button
            title={allDone ? 'All done' : 'Mark all tasks in this thread done'}
            onClick={e => { e.stopPropagation(); visibleTasks.filter(t=>!t.doneAt).forEach(t => onCheck(thread.id, t.id)); }}
            style={{width:20,height:20,borderRadius:'50%',border:`2px solid ${allDone?'#16a34a':'#cbd5e1'}`,background:allDone?'#16a34a':'transparent',cursor:'pointer',flexShrink:0,display:'flex',alignItems:'center',justifyContent:'center',color:'#fff',fontSize:11}}
          >{allDone ? '✓' : ''}</button>
          <Badge kind={thread.tag}>{thread.tag}</Badge>
          <div className="thread-title" style={{flex:1,cursor:'pointer'}} onClick={() => onToggle(thread.id)}>{stripMd(thread.title)}</div>
          {firstTask && <span style={{color:'var(--ink-3)',fontSize:12,maxWidth:220,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{firstTask.action}</span>}
          <span style={{color:'var(--ink-4)',fontFamily:'var(--mono)',fontSize:11,flexShrink:0}}>{openCount} open</span>
          <button className="btn tiny ghost" onClick={() => onToggle(thread.id)} style={{flexShrink:0}}>▾ Expand</button>
        </div>
      </div>
    );
  }

  return (
    <div className={`thread ${thread.tag}`} data-open={isExpanded}>
      <div className="thread-head" onClick={() => onToggle(thread.id)}>
        <I.chev className="thread-chev"/>
        <div className="thread-title">{stripMd(thread.title)}</div>
        <Badge kind={thread.tag}>{thread.tag}</Badge>
        <span className="thread-meta">{visibleTasks.length} {visibleTasks.length === 1 ? 'task' : 'tasks'}</span>
      </div>
      {isExpanded && (
        <div className="thread-body">
          {visibleTasks.map(t => (
            <TaskRow key={t.id} task={t} thread={thread}
              onCheck={() => onCheck(thread.id, t.id)}
              onOpen={onOpenTask}
              onRefresh={onRefresh} />
          ))}
          <div className="thread-foot">
            <button className="btn ghost sm" onClick={e => { e.stopPropagation(); setShowAddFU(true); }}>
              <I.plus/> Add follow-up task
            </button>
            <button className="btn ghost sm" onClick={() => onToggle(thread.id)}>Collapse</button>
          </div>
        </div>
      )}
      {showAddFU && (
        <AddFollowUpModal
          threadId={thread.id}
          threadTitle={stripMd(thread.title)}
          onClose={() => setShowAddFU(false)}
          onAdd={onRefresh}
        />
      )}
    </div>
  );
}

// ---- To-do list ----
function TodoView({ threads, onCheck, onOpenTask, banner, onBanner, onRefresh,
                    dismissedFollowUps, standaloneManualTasks, onCheckStandalone }) {
  const [search, setSearch] = useState('');
  const [compact, setCompact] = useState(false);
  const [confirmPending, setConfirmPending] = useState(null); // { kind, ...payload }

  // Inline add-task form state
  const [showAddTask, setShowAddTask] = useState(false);
  const [newTaskSubject, setNewTaskSubject] = useState('');
  const [newTaskNotes, setNewTaskNotes] = useState('');
  const [addingTask, setAddingTask] = useState(false);

  // Local expand state (not persisted) — urgent/dispute expanded by default; others collapsed if >5 threads
  const [expandedIds, setExpandedIds] = useState(() => {
    const ids = new Set();
    if (threads.length <= 5) {
      threads.forEach(t => ids.add(t.id));
    } else {
      threads.filter(t => ['urgent', 'dispute'].includes(t.tag)).forEach(t => ids.add(t.id));
    }
    return ids;
  });

  // When new threads arrive after a scan, auto-expand urgent/dispute ones
  const prevThreadIds = useRef(new Set(threads.map(t => t.id)));
  useEffect(() => {
    const newThreads = threads.filter(t => !prevThreadIds.current.has(t.id));
    if (newThreads.length > 0) {
      setExpandedIds(prev => {
        const next = new Set(prev);
        newThreads.forEach(t => {
          if (['urgent', 'dispute'].includes(t.tag)) next.add(t.id);
        });
        return next;
      });
    }
    prevThreadIds.current = new Set(threads.map(t => t.id));
  }, [threads]);

  const handleToggle = (threadId) => {
    setExpandedIds(prev => {
      const next = new Set(prev);
      if (next.has(threadId)) next.delete(threadId); else next.add(threadId);
      return next;
    });
  };

  const handleCollapseAll = () => setExpandedIds(new Set());
  const handleExpandAll  = () => setExpandedIds(new Set(threads.map(t => t.id)));

  const order = ['dispute','urgent','reply','action','tender','fyi'];
  const sorted = [...threads].sort((a,b) => order.indexOf(a.tag) - order.indexOf(b.tag));

  // Always filter out done + deleted tasks from main list
  const filtered = sorted.map(t => ({
    ...t,
    tasks: t.tasks.filter(x => {
      if (x.deletedAt) return false;
      if (x.doneAt) return false;
      if (search.trim()) {
        return x.subject?.toLowerCase().includes(search.toLowerCase()) ||
               x.from?.toLowerCase().includes(search.toLowerCase()) ||
               x.action?.toLowerCase().includes(search.toLowerCase());
      }
      return true;
    }),
  })).filter(t => t.tasks.length > 0);

  // Standalone tasks visible in MY TASKS (not deleted, not done)
  const visibleStandalone = (standaloneManualTasks || []).filter(t => !t.deletedAt && !t.doneAt);

  const sections = [
    { key:'urgent', label:'Urgent · disputes, claims & unread', tags:['dispute','urgent'] },
    { key:'reply',  label:'Action / Reply needed',              tags:['reply','action'] },
    { key:'tender', label:'Tenders',                            tags:['tender'] },
    { key:'fyi',    label:'FYI',                                tags:['fyi'] },
  ];

  const urgentCount = threads.reduce((n,t) =>
    n + (['urgent','dispute'].includes(t.tag) ? t.tasks.filter(x=>!x.doneAt && !x.deletedAt).length : 0), 0);
  const openCount = threads.reduce((n,t) => n + t.tasks.filter(x=>!x.doneAt && !x.deletedAt).length, 0);

  return (
    <div className="main-inner">
      <div className="page-head">
        <div>
          <h1 className="page-title">
            Today · {new Date().toLocaleDateString('en-AU', { weekday:'long', day:'numeric', month:'long' })}
          </h1>
          <p className="page-sub">
            {urgentCount} urgent · {openCount} open · unchecked items roll over at midnight AEDT
          </p>
        </div>
        <div style={{display:'flex', gap:6, alignItems:'center'}}>
          <div style={{position:'relative'}}>
            <I.search style={{position:'absolute', left:8, top:'50%', transform:'translateY(-50%)', color:'var(--ink-4)', pointerEvents:'none'}}/>
            <input type="text" placeholder="Search tasks…" value={search} onChange={e=>setSearch(e.target.value)}
              style={{paddingLeft:28, width:180}} />
          </div>
        </div>
      </div>

      {/* Toolbar: Collapse All / Expand All / Compact / Add task */}
      <div style={{display:'flex', gap:6, alignItems:'center', marginBottom:12, flexWrap:'wrap'}}>
        <button className="btn ghost sm" onClick={handleCollapseAll}>Collapse All</button>
        <button className="btn ghost sm" onClick={handleExpandAll}>Expand All</button>
        <button className={`btn sm ${compact ? 'primary' : 'ghost'}`}
          onClick={() => setCompact(c => !c)}>
          {compact ? '▣ Compact on' : '▤ Compact'}
        </button>
        {compact && (
          <span style={{color:'var(--ink-3)',fontSize:11,fontStyle:'italic'}}>Click ○ on a thread header to check all its tasks</span>
        )}
        <button className="btn ghost sm" onClick={() => setShowAddTask(s => !s)}
          style={{color:'#92400e', borderColor:'#f59e0b'}}>
          <I.plus/> Add task
        </button>
        <span style={{color:'var(--ink-4)', fontSize:11, fontFamily:'var(--mono)', marginLeft:4}}>
          {filtered.length} threads · {openCount} open
        </span>
      </div>

      {/* Inline add-task form */}
      {showAddTask && (
        <div style={{background:'#fffbeb', border:'1px solid #fde68a', borderRadius:8, padding:'14px 16px', marginBottom:14}}>
          <div style={{fontWeight:600, fontSize:13, marginBottom:10, color:'#92400e'}}>New task</div>
          <input type="text" value={newTaskSubject}
            onChange={e => setNewTaskSubject(e.target.value)}
            placeholder="Task title (required)"
            autoFocus
            style={{width:'100%', marginBottom:8}} />
          <textarea rows="2" value={newTaskNotes}
            onChange={e => setNewTaskNotes(e.target.value)}
            placeholder="Notes / description (optional)"
            style={{width:'100%', resize:'vertical', marginBottom:10}} />
          <div style={{display:'flex', gap:8}}>
            <button className="btn primary sm"
              disabled={addingTask || !newTaskSubject.trim()}
              onClick={async () => {
                setAddingTask(true);
                try {
                  await api.post('/api/task/standalone', { subject: newTaskSubject.trim(), action: newTaskNotes.trim() });
                  setShowAddTask(false);
                  setNewTaskSubject('');
                  setNewTaskNotes('');
                  onRefresh?.();
                } catch (err) {
                  alert('Failed: ' + err.message);
                } finally {
                  setAddingTask(false);
                }
              }}>
              {addingTask ? 'Adding…' : 'Add task'}
            </button>
            <button className="btn ghost sm" onClick={() => {
              setShowAddTask(false);
              setNewTaskSubject('');
              setNewTaskNotes('');
            }}>Cancel</button>
          </div>
        </div>
      )}

      {banner && (
        <div className="banner">
          <I.sparkle className="icon"/>
          <p>
            New email <strong>"{banner.subject}"</strong> matches existing thread <strong>{banner.threadTitle}</strong>. Add as a sub-task?
          </p>
          <button className="btn sm" onClick={() => onBanner('skip')}>No, skip</button>
          <button className="btn sm primary" onClick={() => onBanner('accept')}>Yes, add to thread</button>
        </div>
      )}

      {/* MY TASKS — standalone manual tasks */}
      {visibleStandalone.length > 0 && (
        <div style={{marginBottom:16}}>
          <div className="section-head">
            <span style={{color:'#92400e', fontWeight:700, letterSpacing:'0.04em'}}>MY TASKS</span>
            <span className="count">{visibleStandalone.length}</span>
            <span className="line"/>
          </div>
          <div style={{border:'1px solid #fde68a', borderRadius:8, overflow:'hidden', background:'#fffbeb', boxShadow:'0 1px 3px rgba(245,158,11,.1)'}}>
            {visibleStandalone.map(task => (
              <TaskRow key={task.id}
                task={task}
                thread={{ id: 'standalone', title: 'MY TASKS' }}
                onCheck={() => onCheckStandalone?.(task.id)}
                onOpen={() => {}}
                onRefresh={onRefresh}
              />
            ))}
          </div>
        </div>
      )}

      {filtered.length === 0 && visibleStandalone.length === 0 && (
        <div className="empty">
          {search ? `No tasks match "${search}"` : 'All caught up — no active tasks'}
        </div>
      )}

      {filtered.length === 0 && visibleStandalone.length > 0 && search && (
        <div className="empty">No email tasks match "{search}"</div>
      )}

      {sections.map(sec => {
        const items = filtered.filter(t => sec.tags.includes(t.tag));
        if (!items.length) return null;
        const open = items.reduce((n,t) => n + t.tasks.filter(x=>!x.doneAt && !x.deletedAt).length, 0);
        return (
          <div key={sec.key}>
            <div className="section-head">
              <span>{sec.label}</span>
              <span className="count">{open} open</span>
              <span className="line"/>
            </div>
            {items.map(t => (
              <Thread key={t.id} thread={t}
                onCheck={onCheck} onOpenTask={onOpenTask}
                onToggle={handleToggle} onRefresh={onRefresh}
                compact={compact} isExpanded={expandedIds.has(t.id)} />
            ))}
          </div>
        );
      })}

      

      {/* Confirm dialog for permanent deletions */}
      {confirmPending && (
        <ConfirmDialog
          title="Are you sure?"
          body={`This will permanently delete "${confirmPending.label}". This cannot be undone.`}
          confirmLabel="Yes, delete forever"
          danger
          onCancel={() => setConfirmPending(null)}
          onConfirm={async () => {
            try {
              if (confirmPending.kind === 'deleteManualTask') {
                await api.post('/api/task/manual/permanent-delete', { threadId: confirmPending.threadId, taskId: confirmPending.taskId });
              } else if (confirmPending.kind === 'deleteStandaloneTask') {
                await api.post('/api/task/standalone/permanent-delete', { id: confirmPending.id });
              } else if (confirmPending.kind === 'deleteFollowup') {
                await api.post('/api/followup/permanent-delete', { id: confirmPending.id });
              }
              onRefresh?.();
            } catch (err) {
              alert('Delete failed: ' + err.message);
            }
            setConfirmPending(null);
          }}
        />
      )}
    </div>
  );
}

// ---- Recently Deleted (standalone view) ----
function RecentlyDeletedView({ threads, standaloneManualTasks, dismissedFollowUps, onRefresh }) {
  const [confirmPending, setConfirmPending] = useState(null);

  const deletedItems = [];
  for (const th of (threads||[])) {
    for (const task of th.tasks) {
      if (task.isManual && task.deletedAt) deletedItems.push({ kind:'manualTask', task, thread: th });
    }
  }
  for (const task of (standaloneManualTasks||[])) {
    if (task.deletedAt) deletedItems.push({ kind:'standaloneTask', task });
  }
  for (const fu of (dismissedFollowUps||[])) {
    deletedItems.push({ kind:'followup', fu });
  }

  return (
    <div className="main-inner">
      <div className="page-head">
        <div>
          <h1 className="page-title">Recently Deleted</h1>
          <p className="page-sub">Deleted manual tasks and dismissed follow-ups. Restore or permanently delete.</p>
        </div>
      </div>
      {deletedItems.length === 0 && <div className="empty">Nothing here — recently deleted items will appear here.</div>}
      {deletedItems.length > 0 && (
        <div style={{background:'#fafaf9', border:'1px solid var(--line)', borderRadius:8, overflow:'hidden'}}>
          {deletedItems.map((item, idx) => {
            if (item.kind === 'manualTask') {
              const { task, thread } = item;
              return (
                <div key={task.id} style={{display:'flex',alignItems:'center',gap:10,padding:'12px 16px',borderBottom:'1px solid var(--line)'}}>
                  <div style={{flex:1}}>
                    <div style={{fontWeight:600,fontSize:13,color:'var(--ink-2)',textDecoration:'line-through'}}>{task.subject}</div>
                    <div style={{display:'flex',alignItems:'center',gap:6,marginTop:3}}><span style={{background:'#dbeafe',color:'#1d4ed8',fontSize:10,fontWeight:700,padding:'1px 6px',borderRadius:4,textTransform:'uppercase',letterSpacing:'.04em'}}>📋 To-do Task</span><span style={{fontSize:11,color:'var(--ink-4)'}}>Thread: {stripMd(thread.title)}</span></div>
                  </div>
                  <button className="btn sm ghost" onClick={async () => { try { await api.post('/api/task/manual/restore', { threadId: thread.id, taskId: task.id }); onRefresh?.(); } catch(e) { alert(e.message); } }}>Restore</button>
                  <button className="btn sm danger" onClick={() => setConfirmPending({ kind:'deleteManualTask', threadId: thread.id, taskId: task.id, label: task.subject })}><I.trash/></button>
                </div>
              );
            }
            if (item.kind === 'standaloneTask') {
              const { task } = item;
              return (
                <div key={task.id} style={{display:'flex',alignItems:'center',gap:10,padding:'12px 16px',borderBottom:'1px solid var(--line)'}}>
                  <div style={{flex:1}}>
                    <div style={{fontWeight:600,fontSize:13,color:'var(--ink-2)',textDecoration:'line-through'}}>{task.subject}</div>
                    <div style={{display:'flex',alignItems:'center',gap:6,marginTop:3}}><span style={{background:'#fef3c7',color:'#92400e',fontSize:10,fontWeight:700,padding:'1px 6px',borderRadius:4,textTransform:'uppercase',letterSpacing:'.04em'}}>✅ My Task</span></div>
                  </div>
                  <button className="btn sm ghost" onClick={async () => { try { await api.post('/api/task/standalone/restore', { id: task.id }); onRefresh?.(); } catch(e) { alert(e.message); } }}>Restore</button>
                  <button className="btn sm danger" onClick={() => setConfirmPending({ kind:'deleteStandaloneTask', id: task.id, label: task.subject })}><I.trash/></button>
                </div>
              );
            }
            if (item.kind === 'followup') {
              const { fu } = item;
              return (
                <div key={fu.id} style={{display:'flex',alignItems:'center',gap:10,padding:'12px 16px',borderBottom:'1px solid var(--line)'}}>
                  <div style={{flex:1}}>
                    <div style={{fontWeight:600,fontSize:13,color:'var(--ink-2)',textDecoration:'line-through'}}>{stripMd(fu.subject)}</div>
                    <div style={{display:'flex',alignItems:'center',gap:6,marginTop:3,flexWrap:'wrap'}}><span style={{background:'#fce7f3',color:'#9d174d',fontSize:10,fontWeight:700,padding:'1px 6px',borderRadius:4,textTransform:'uppercase',letterSpacing:'.04em'}}>⏰ Follow-up</span>{fu.autoDismissed && <span style={{background:'#dcfce7',color:'#166534',fontSize:10,fontWeight:700,padding:'1px 6px',borderRadius:4}}>✓ Auto-dismissed</span>}<span style={{fontSize:11,color:'var(--ink-4)'}}>{fu.autoDismissReason || ('to ' + (fu.to || 'unknown'))}</span></div>
                  </div>
                  <button className="btn sm ghost" onClick={async () => { try { await api.post('/api/followup/restore', { id: fu.id }); onRefresh?.(); } catch(e) { alert(e.message); } }}>Restore</button>
                  <button className="btn sm danger" onClick={() => setConfirmPending({ kind:'deleteFollowup', id: fu.id, label: fu.subject })}><I.trash/></button>
                </div>
              );
            }
            return null;
          })}
        </div>
      )}
      {confirmPending && (
        <ConfirmDialog title="Delete forever?" body={`“${confirmPending.label}” will be permanently removed.`}
          confirmLabel="Yes, delete forever" danger
          onCancel={() => setConfirmPending(null)}
          onConfirm={async () => {
            try {
              if (confirmPending.kind === 'deleteManualTask') await api.post('/api/task/manual/permanent-delete', { threadId: confirmPending.threadId, taskId: confirmPending.taskId });
              else if (confirmPending.kind === 'deleteStandaloneTask') await api.post('/api/task/standalone/permanent-delete', { id: confirmPending.id });
              else if (confirmPending.kind === 'deleteFollowup') await api.post('/api/followup/permanent-delete', { id: confirmPending.id });
              onRefresh?.();
            } catch(e) { alert(e.message); }
            setConfirmPending(null);
          }}
        />
      )}
    </div>
  );
}

// ---- Done list ----
function DoneView({ doneHistory, onUndo }) {
  const groups = doneHistory || {};
  const [active, setActive] = useState('all');
  const [undoing, setUndoing] = useState(null);
  const [removedIds, setRemovedIds] = useState(new Set());
  const [dateFrom, setDateFrom] = useState('');
  const [dateTo, setDateTo]   = useState('');
  const [search, setSearch]   = useState('');
  const allDayKeys = Object.keys(groups).sort().reverse();

  // Filter by date range + search
  const dayKeys = allDayKeys.filter(k => {
    if (dateFrom && k < dateFrom) return false;
    if (dateTo   && k > dateTo)   return false;
    if (search.trim()) {
      const q = search.toLowerCase();
      return (groups[k]||[]).some(it =>
        (it.subject||'').toLowerCase().includes(q) ||
        (it.from||'').toLowerCase().includes(q) ||
        (it.folder||'').toLowerCase().includes(q)
      );
    }
    return true;
  });

  const totalFiltered = dayKeys.reduce((n,k) => n + (groups[k]||[]).length, 0);

  const handleUndo = async (it) => {
    if (!it.taskId) return;
    setUndoing(it.id);
    // Optimistically hide from Done list immediately
    setRemovedIds(prev => new Set([...prev, it.id]));
    try {
      if (it.isStandalone) {
        await api.post('/api/task/standalone/undo', { id: it.taskId });
      } else {
        await fetch('/api/task/undo', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ threadId: it.threadId, taskId: it.taskId }) });
      }
      onUndo && onUndo();
    } catch(e) {
      console.error('Undo failed', e);
      // Rollback optimistic hide on error
      setRemovedIds(prev => { const s = new Set(prev); s.delete(it.id); return s; });
    }
    setUndoing(null);
  };

  return (
    <div className="main-inner">
      <div className="page-head">
        <div>
          <h1 className="page-title">Done</h1>
          <p className="page-sub">Completed tasks grouped by day. Each item shows the Outlook folder the email was moved to.</p>
        </div>
      </div>
      {/* Filters */}
      <div style={{display:'flex',gap:10,flexWrap:'wrap',alignItems:'flex-end',marginBottom:16,background:'var(--bg-2,#f5f4f0)',borderRadius:8,padding:'12px 14px'}}>
        <div>
          <div style={{fontSize:11,color:'var(--ink-4)',marginBottom:4,textTransform:'uppercase',letterSpacing:'.04em'}}>From</div>
          <input type="date" value={dateFrom} onChange={e => { setDateFrom(e.target.value); setActive('all'); }}
            style={{padding:'6px 10px',border:'1px solid var(--line)',borderRadius:6,fontSize:13,background:'#fff'}} />
        </div>
        <div>
          <div style={{fontSize:11,color:'var(--ink-4)',marginBottom:4,textTransform:'uppercase',letterSpacing:'.04em'}}>To</div>
          <input type="date" value={dateTo} onChange={e => { setDateTo(e.target.value); setActive('all'); }}
            style={{padding:'6px 10px',border:'1px solid var(--line)',borderRadius:6,fontSize:13,background:'#fff'}} />
        </div>
        <div style={{flex:1,minWidth:160}}>
          <div style={{fontSize:11,color:'var(--ink-4)',marginBottom:4,textTransform:'uppercase',letterSpacing:'.04em'}}>Search</div>
          <input type="text" placeholder="Subject, sender, folder…" value={search} onChange={e => { setSearch(e.target.value); setActive('all'); }}
            style={{width:'100%',padding:'6px 10px',border:'1px solid var(--line)',borderRadius:6,fontSize:13,background:'#fff'}} />
        </div>
        {(dateFrom||dateTo||search) && (
          <button className="btn ghost sm" onClick={() => { setDateFrom(''); setDateTo(''); setSearch(''); }} style={{alignSelf:'flex-end'}}>× Clear</button>
        )}
        <div style={{alignSelf:'flex-end',fontSize:12,color:'var(--ink-3)',fontFamily:'var(--mono)'}}>
          {totalFiltered} item{totalFiltered!==1?'s':''}
        </div>
      </div>

      {dayKeys.length === 0 && <div className="empty">{allDayKeys.length === 0 ? 'No completed tasks yet' : 'No results for this filter'}</div>}
      <div className="chips" style={{marginBottom:16,flexWrap:'wrap'}}>
        <button className="chip" aria-pressed={active==='all'} onClick={() => setActive('all')}>All</button>
        {dayKeys.map(k => (
          <button key={k} className="chip" aria-pressed={active===k} onClick={() => setActive(k)}>
            {k} · {(search ? (groups[k]||[]).filter(it => it.subject?.toLowerCase().includes(search.toLowerCase()) || it.from?.toLowerCase().includes(search.toLowerCase()) || it.folder?.toLowerCase().includes(search.toLowerCase())).length : (groups[k]||[]).length)}
          </button>
        ))}
      </div>
      {dayKeys.filter(k => active==='all' || active===k).map(day => {
        const dayItems = search.trim()
          ? (groups[day]||[]).filter(it => {
              if (removedIds.has(it.id)) return false;
              const q = search.toLowerCase();
              return (it.subject||'').toLowerCase().includes(q) || (it.from||'').toLowerCase().includes(q) || (it.folder||'').toLowerCase().includes(q);
            })
          : (groups[day]||[]).filter(it => !removedIds.has(it.id));
        if (!dayItems.length) return null;
        return (
        <div className="day-group" key={day}>
          <div className="day-head">
            <h3>{day}</h3>
            <span className="c">{dayItems.length} completed</span>
          </div>
          {dayItems.map(it => (
            <div className="done-item" key={it.id}>
              <Check checked={undoing !== it.id} onClick={() => handleUndo(it)}/>
              <div>
                <div className="t" style={{textDecoration: 'line-through', opacity:0.6}}>{it.subject}</div>
                <div className="m">from {it.from} · at {it.at}{it.disputeClosed ? ' · Dispute closed' : ''}</div>
              </div>
              <div style={{display:'flex', alignItems:'center', gap:8}}>
                <span className="folder-pill"><I.folder className="ic"/> {it.folder}</span>
                {(it.webLink || it.graphId) && <a href={outlookLink(it.webLink, it.graphId)} onClick={e=>{e.preventDefault();openInOutlook(it.webLink, it.graphId);}} target="_blank" rel="noreferrer" style={{color:'#2563eb',fontSize:12,fontWeight:600}}>Open ↗</a>}
              </div>
            </div>
          ))}
        </div>
        );
      })}
    </div>
  );
}


// ---- Follow-ups ----
function FollowupsView({ items, onRefresh }) {
  const [nudging, setNudging] = useState(null);
  const [nudgeText, setNudgeText] = useState({});
  const [search, setSearch] = useState('');
  const [bodyCache, setBodyCache] = useState({});
  const [expandedBody, setExpandedBody] = useState(null);
  const [confirmDismiss, setConfirmDismiss] = useState(null); // { fu }

  const toggleBody = async (fu) => {
    if (expandedBody === fu.id) { setExpandedBody(null); return; }
    setExpandedBody(fu.id);
    if (!bodyCache[fu.id] && fu.graphId) {
      try {
        const d = await api.get('/api/email/body?graphId=' + encodeURIComponent(fu.graphId));
        setBodyCache(c => ({...c, [fu.id]: d.body || '(no body)'}));
      } catch { setBodyCache(c => ({...c, [fu.id]: '(could not load body)'})); }
    }
  };

  const handleDeleteManual = async (fu) => {
    try {
      await api.post('/api/task/followup/delete', { threadId: fu.threadId, taskId: fu.taskId });
      onRefresh?.();
    } catch (err) {
      alert('Delete failed: ' + err.message);
    }
  };

  const handleDismiss = (fu) => {
    setConfirmDismiss({ fu });
  };

  const confirmDoDismiss = async (fu) => {
    try {
      await api.post('/api/followup/dismiss', { id: fu.id });
      onRefresh?.();
    } catch (err) {
      alert('Dismiss failed: ' + err.message);
    }
  };

  const getDraftNudge = async (item) => {
    setNudging(item.id);
    try {
      const { text } = await api.post('/api/ai/nudge', {
        subject: item.subject,
        recipientName: item.to,
        daysSince: item.sentDays,
      });
      setNudgeText(prev => ({ ...prev, [item.id]: text }));
    } catch (err) {
      alert('Could not generate nudge: ' + err.message);
    } finally {
      setNudging(null);
    }
  };

  return (
    <div className="main-inner">
      <div className="page-head">
        <div>
          <h1 className="page-title">Follow-ups</h1>
          <p className="page-sub">Emails you sent where no reply has landed within the threshold. Auto-clears when a reply arrives.</p>
        </div>
      </div>
      {/* Search bar */}
      <div style={{marginBottom:16,display:'flex',alignItems:'center',gap:10,background:'#fff',border:'1px solid #d1d5db',borderRadius:8,padding:'8px 14px',maxWidth:480,boxShadow:'0 1px 3px rgba(0,0,0,.06)'}}>
        <svg width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="#9ca3af" strokeWidth="1.5" strokeLinecap="round"><circle cx="6.5" cy="6.5" r="4.5"/><path d="M10.5 10.5l3 3"/></svg>
        <input type="text" placeholder="Search by subject, recipient, or keyword…" value={search} onChange={e => setSearch(e.target.value)}
          style={{flex:1,border:'none',outline:'none',fontSize:13.5,background:'transparent',color:'#1a1a18'}} />
        {search && <button onClick={() => setSearch('')} style={{border:'none',background:'none',cursor:'pointer',color:'#9ca3af',fontSize:16,lineHeight:1,padding:0}}>×</button>}
      </div>
      {confirmDismiss && (
        <ConfirmDialog
          title="Are you sure?"
          body="This will remove this follow-up from your list."
          confirmLabel="Yes, remove"
          danger
          onCancel={() => setConfirmDismiss(null)}
          onConfirm={async () => {
            await confirmDoDismiss(confirmDismiss.fu);
            setConfirmDismiss(null);
          }}
        />
      )}
      {items.length === 0 && <div className="empty">No pending follow-ups — inbox all clear</div>}
      {items.filter(fu => !search.trim() || fu.subject?.toLowerCase().includes(search.toLowerCase()) || fu.to?.toLowerCase().includes(search.toLowerCase())).map(fu => (
        <div className={`thread followup`} key={fu.id} data-open="true" style={{marginBottom:10}}>
          <div className="thread-head">
            <div style={{width:7, height:7, borderRadius:'50%', background: fu.urgency==='overdue' ? 'var(--urgent)' : 'oklch(55% 0.14 75)', marginLeft:6}}/>
            <div className="thread-title">{stripMd(fu.subject)}</div>
            <Badge kind={fu.urgency==='overdue' ? 'urgent' : 'reply'}>
              {fu.urgency==='overdue' ? `${fu.sentDays}d overdue` : 'Due today'}
            </Badge>
            <span className="thread-meta">sent {fu.sentDays}d ago</span>
            <button className="btn tiny ghost" title="Dismiss this follow-up" onClick={e => { e.stopPropagation(); handleDismiss(fu); }} style={{marginLeft:'auto',color:'var(--ink-3)'}}>Dismiss ×</button>
          </div>
          <div className="thread-body">
            <div className="task">
              <div/>
              <div className="task-body">
                <div className="task-title">Waiting on {fu.to}</div>
                <div className="task-action">{fu.action}</div>
                <div className="task-row">
                  <span>{fu.to} &lt;{fu.toEmail}&gt;</span>
                  <span className="sep">·</span>
                  {(fu.webLink || fu.graphId) && <><a href={outlookLink(fu.webLink, fu.graphId)} onClick={e=>{e.preventDefault();openInOutlook(fu.webLink, fu.graphId);}} target="_blank" rel="noreferrer" style={{color:'#2563eb',fontWeight:600,fontSize:12}} onClick={e => e.stopPropagation()}>📧 Open in Outlook</a><span className="sep">·</span></>}
                  <button className="btn tiny ghost" onClick={() => toggleBody(fu)}>
                    {expandedBody === fu.id ? '▼ Hide email' : '► View email'}
                  </button>
                  <button className="btn tiny ghost"
                    onClick={() => getDraftNudge(fu)}
                    disabled={nudging === fu.id}>
                    <I.sparkle/> {nudging === fu.id ? 'Drafting…' : 'Draft nudge'}
                  </button>
                  {fu.isManual && (
                    <button className="btn tiny ghost"
                      title="Remove this manual follow-up"
                      onClick={e => { e.stopPropagation(); handleDeleteManual(fu); }}
                      style={{color:'var(--urgent)'}}>
                      ×
                    </button>
                  )}
                </div>
                {nudgeText[fu.id] && (
                  <div className="reply-card" style={{marginTop:10}}>
                    <div className="rl-head">
                      <span className="rl-label">AI-drafted nudge</span>
                      <button className="btn tiny ghost" onClick={() => {
                        navigator.clipboard?.writeText(nudgeText[fu.id]);
                      }}>
                        <I.copy/> Copy
                      </button>
                    </div>
                    <pre>{nudgeText[fu.id]}</pre>
                  </div>
                )}
                {expandedBody === fu.id && (
                  <div style={{marginTop:10,background:'#f9f8f6',border:'1px solid #e8e7e3',borderRadius:6,padding:'10px 14px',fontSize:13,lineHeight:1.6,maxHeight:300,overflowY:'auto'}}>
                    {bodyCache[fu.id]
                      ? <div dangerouslySetInnerHTML={{__html: bodyCache[fu.id]}} />
                      : <span style={{color:'#888'}}>Loading…</span>
                    }
                  </div>
                )}
              </div>
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

// ---- Disputes ----
function DisputesView({ disputes, onAction, onRefresh }) {
  const sorted = [...disputes].sort((a,b) => {
    if (a.state==='resolved' && b.state!=='resolved') return 1;
    if (b.state==='resolved' && a.state!=='resolved') return -1;
    if (a.state==='escalated' && b.state!=='escalated') return -1;
    if (b.state==='escalated' && a.state!=='escalated') return 1;
    return a.priority - b.priority;
  });
  const active   = sorted.filter(d => d.state !== 'resolved');
  const resolved = sorted.filter(d => d.state === 'resolved');
  const [showAudit, setShowAudit]   = useState(null);
  const [confirm, setConfirm]       = useState(null);
  const [showNew, setShowNew]       = useState(false);
  const [newForm, setNewForm]       = useState({ title:'', party:'', amount:'', action:'' });
  const [busy, setBusy]             = useState(false);

  const stateLabel = { open:'Open', progress:'In progress', resolved:'Resolved', escalated:'Escalated' };
  const cycleState = async (d) => {
    const cycle = ['open','progress','escalated','resolved'];
    const next = cycle[(cycle.indexOf(d.state) + 1) % cycle.length];
    await onAction({ type:'state', id:d.id, next, title:d.title });
  };

  const createDispute = async () => {
    if (!newForm.title.trim()) return;
    setBusy(true);
    try {
      await api.post('/api/dispute/new', newForm);
      setShowNew(false);
      setNewForm({ title:'', party:'', amount:'', action:'' });
      onRefresh();
    } catch (err) { alert(err.message); }
    finally { setBusy(false); }
  };

  const Card = ({ d }) => (
    <div className={`dispute-card ${d.state}`}>
      <span className="drag" title="Drag to reorder"><I.drag/></span>
      <div className="dispute-body">
        <div className="d-title">{d.title}</div>
        <div className="d-meta">{d.party} · {d.amount} · opened {d.opened}</div>
        <div className="d-action">{d.action}</div>
      </div>
      <div className="dispute-actions">
        <button className={`state-pill ${d.state}`} onClick={() => cycleState(d)}>
          <span className="d"/>{stateLabel[d.state]}
        </button>
        <button className="btn tiny ghost" onClick={() => setShowAudit(d)}>Audit</button>
        {d.state !== 'resolved' && (
          <button className="btn tiny" onClick={() => setConfirm({ kind:'close', d })}>Close</button>
        )}
        <button className="btn tiny danger" onClick={() => setConfirm({ kind:'delete', d })}><I.trash/></button>
      </div>
    </div>
  );

  return (
    <div className="main-inner">
      <div className="page-head">
        <div>
          <h1 className="page-title">Disputes & Claims</h1>
          <p className="page-sub">Track formal disputes, SOPA claims, and payment disagreements. Emails classified as "dispute" or "claim" auto-appear here.</p>
        </div>
        <button className="btn primary" onClick={() => setShowNew(true)}><I.plus/> New dispute</button>
      </div>
      <div style={{
        background:'#fffbeb', border:'1px solid #fde68a', borderRadius:8,
        padding:'10px 14px', marginBottom:16, fontSize:12.5, color:'#92400e',
        display:'flex', alignItems:'center', gap:8,
      }}>
        <I.sparkle style={{color:'#f59e0b', flexShrink:0}}/>
        <span>Emails classified as <strong>dispute</strong> or <strong>claim</strong> by Claude AI are automatically added here. Use <strong>New dispute</strong> to manually track a disagreement.</span>
      </div>

      <div className="section-head"><span>Active</span><span className="count">{active.length}</span><span className="line"/></div>
      {active.length === 0 && <div className="empty" style={{padding:'20px 0'}}>No active disputes</div>}
      {active.map(d => <Card key={d.id} d={d}/>)}

      {resolved.length > 0 && (
        <>
          <div className="section-head"><span>Resolved</span><span className="count">{resolved.length}</span><span className="line"/></div>
          {resolved.map(d => <Card key={d.id} d={d}/>)}
        </>
      )}

      {/* New dispute modal */}
      {showNew && (
        <Modal onClose={() => setShowNew(false)}>
          <div className="modal-head"><h2>New dispute / claim</h2></div>
          <div className="modal-body">
            {[['Title','title'],['Party','party'],['Amount','amount'],['Notes','action']].map(([label, key]) => (
              <div className="modal-section" key={key}>
                <div className="label">{label}</div>
                <input type="text" value={newForm[key]} onChange={e => setNewForm(f=>({...f,[key]:e.target.value}))} />
              </div>
            ))}
          </div>
          <div className="modal-foot">
            <button className="btn ghost" onClick={() => setShowNew(false)}>Cancel</button>
            <button className="btn primary" onClick={createDispute} disabled={busy || !newForm.title.trim()}>
              {busy ? 'Creating…' : 'Create dispute'}
            </button>
          </div>
        </Modal>
      )}

      {/* Audit trail */}
      {showAudit && (
        <Modal onClose={() => setShowAudit(null)}>
          <div className="modal-head">
            <h2>Audit trail — {showAudit.title}</h2>
            <div className="from-line">Immutable once written · {showAudit.audit?.length || 0} events</div>
          </div>
          <div className="modal-body">
            {(showAudit.audit || []).map((a,i) => (
              <div key={i} style={{display:'grid', gridTemplateColumns:'150px 20px 1fr', gap:10, padding:'8px 0', borderBottom:'1px dashed var(--line)', fontSize:12.5}}>
                <span style={{fontFamily:'var(--mono)', color:'var(--ink-3)', fontSize:11}}>{new Date(a.t).toLocaleString('en-AU')}</span>
                <span style={{color: a.kind==='ai' ? 'var(--violet)' : a.kind==='user' ? 'var(--accent-ink)' : 'var(--ink-4)'}}>
                  {a.kind==='ai' ? '◆' : a.kind==='user' ? '●' : '○'}
                </span>
                <span>{a.text}</span>
              </div>
            ))}
          </div>
          <div className="modal-foot">
            <span style={{fontSize:11, color:'var(--ink-3)', fontFamily:'var(--mono)'}}>dispute.{showAudit.id}.audit</span>
            <button className="btn" onClick={() => setShowAudit(null)}>Close</button>
          </div>
        </Modal>
      )}

      {confirm?.kind === 'close' && (
        <ConfirmDialog
          title="Close this dispute?"
          body={`"${confirm.d.title}" will be marked Resolved and the email moved to "Disputes & Claims". An audit record will be kept.`}
          confirmLabel="Yes, close it"
          onCancel={() => setConfirm(null)}
          onConfirm={() => { onAction({type:'close', id:confirm.d.id, title:confirm.d.title}); setConfirm(null); }}
        />
      )}
      {confirm?.kind === 'delete' && (
        <ConfirmDialog
          title="Delete this dispute permanently?"
          body="This action cannot be undone. An audit record will be written before removal."
          confirmLabel="Yes, delete it" danger
          onCancel={() => setConfirm(null)}
          onConfirm={() => { onAction({type:'delete', id:confirm.d.id, title:confirm.d.title}); setConfirm(null); }}
        />
      )}
    </div>
  );
}

// ---- Folder rules ----
const FOLDER_OPTIONS = [
  '— stays in inbox —',
  'Disputes & Claims', 'Claims',
  'Tenders - VIC Gov',
  'Suppliers - Spray Seal',
  'Vendors - Estimating', 'Vendors - Kynection',
  'Internal - Payroll',
  'FYI',
  'Noise',
  'Read Later',
  'Archive',
];

function FolderRulesView({ folderRules, onRefresh }) {
  const rules = folderRules || [];
  const [local, setLocal] = useState(() => rules.map(r => ({...r})));
  const [saving, setSaving] = useState(false);
  const [saved, setSaved] = useState(false);

  const save = async () => {
    setSaving(true);
    try {
      await api.post('/api/settings/folders', { rules: local });
      setSaved(true);
      setTimeout(() => setSaved(false), 2000);
      onRefresh?.();
    } catch(e) { alert('Save failed: ' + e.message); }
    setSaving(false);
  };

  return (
    <div className="main-inner">
      <div className="page-head">
        <div>
          <h1 className="page-title">Folder rules</h1>
          <p className="page-sub">When a task is marked done, its email is moved in Outlook to the folder you set here. Missing folders are created automatically.</p>
        </div>
      </div>
      <div className="rules">
        <div className="row head">
          <div>Email category</div>
          <div/>
          <div>Outlook folder</div>
          <div>Moved</div>
        </div>
        {local.map((r, i) => (
          <div className="row" key={r.cat}>
            <div>{r.cat}</div>
            <div className="arrow">→</div>
            <div>
              <select value={r.folder} onChange={e => {
                const updated = [...local];
                updated[i] = {...r, folder: e.target.value};
                setLocal(updated);
              }} style={{padding:'5px 8px', border:'1px solid var(--line)', borderRadius:5, fontSize:13, background:'#fff', minWidth:200}}>
                {FOLDER_OPTIONS.map(opt => <option key={opt} value={opt}>{opt}</option>)}
              </select>
            </div>
            <div style={{fontFamily:'var(--mono)', fontSize:12, color:'var(--ink-3)'}}>{r.emails || '—'}</div>
          </div>
        ))}
      </div>
      <div style={{marginTop:16, display:'flex', gap:10, alignItems:'center'}}>
        <button className="btn primary" onClick={save} disabled={saving}>
          {saving ? 'Saving…' : saved ? '✓ Saved' : 'Save folder rules'}
        </button>
        <span style={{fontSize:12, color:'var(--ink-3)'}}>Changes apply to new emails marked done</span>
      </div>
    </div>
  );
}

// ---- Reply style ----
function ReplyStyleView({ employee, onSave }) {
  const rs = employee?.replyStyle || {};
  const [tone,   setTone]   = useState(rs.tone    || 'professional');
  const [sign,   setSign]   = useState(rs.signOff || 'Kind regards');
  const [opts,   setOpts]   = useState(rs.options || 2);
  const [thresh, setThresh] = useState(2);
  const [saved, setSaved]   = useState(false);

  const tones = [
    { v:'professional', l:'Professional & concise' },
    { v:'warm',         l:'Warm & friendly' },
    { v:'formal',       l:'Formal & detailed' },
    { v:'direct',       l:'Direct & brief' },
  ];

  const save = async () => {
    await onSave({ tone, signOff: sign, options: opts, followUpThresholdDays: thresh });
    setSaved(true);
    setTimeout(() => setSaved(false), 2000);
  };

  return (
    <div className="main-inner">
      <div className="page-head">
        <div>
          <h1 className="page-title">Reply style</h1>
          <p className="page-sub">Your preferences are stored locally and injected as a system prompt prefix on every Claude API call.</p>
        </div>
      </div>

      <div className="form-card">
        <div className="form-row">
          <div><div className="k">Tone</div><div className="k-sub">Voice Claude uses when drafting reply options.</div></div>
          <div className="chips">
            {tones.map(t => <button className="chip" key={t.v} aria-pressed={tone===t.v} onClick={() => setTone(t.v)}>{t.l}</button>)}
          </div>
        </div>
        <div className="form-row">
          <div><div className="k">Reply options</div><div className="k-sub">Number of alternatives per email.</div></div>
          <div className="chips">
            {[1,2,3].map(n => <button className="chip" key={n} aria-pressed={opts===n} onClick={() => setOpts(n)}>{n}</button>)}
          </div>
        </div>
        <div className="form-row">
          <div><div className="k">Sign-off</div><div className="k-sub">Appended to every drafted reply. Use multiple lines for name, title, company.</div></div>
          <textarea value={sign} onChange={e => setSign(e.target.value)} rows={4}
            style={{width:'100%', padding:'8px 12px', border:'1px solid var(--line)', borderRadius:6, fontSize:13, lineHeight:1.6, fontFamily:'inherit', resize:'vertical', background:'#fff'}} />
        </div>
        <div className="form-row">
          <div><div className="k">Follow-up threshold</div><div className="k-sub">Business days before an email appears under Follow-ups.</div></div>
          <div className="chips">
            {[1,2,3,5].map(n => <button className="chip" key={n} aria-pressed={thresh===n} onClick={() => setThresh(n)}>{n} day{n>1?'s':''}</button>)}
          </div>
        </div>
      </div>

      <div className="form-card">
        <div style={{fontSize:11, letterSpacing:'0.08em', textTransform:'uppercase', color:'var(--ink-3)', marginBottom:10, fontWeight:500}}>Claude system prompt preview</div>
        <pre style={{margin:0, fontFamily:'var(--mono)', fontSize:11.5, color:'var(--ink-2)', lineHeight:1.6, whiteSpace:'pre-wrap'}}>
{`Employee: ${employee?.name || 'User'} · Role: ${employee?.role || ''}
Reply tone: ${tones.find(t=>t.v===tone)?.l.toLowerCase()}
Reply options: ${opts}
Sign-off: "${sign}"
Follow-up threshold: ${thresh} business day${thresh>1?'s':''} (disputes: ${Math.max(1,thresh-1)})`}
        </pre>
      </div>

      <div style={{display:'flex', gap:8, justifyContent:'flex-end'}}>
        <button className="btn primary" onClick={save}>{saved ? '✓ Saved' : 'Save preferences'}</button>
      </div>
    </div>
  );
}

Object.assign(window, {
  TaskModal, TaskRow, Thread, TodoView, DoneView, RecentlyDeletedView,
  FollowupsView, DisputesView, FolderRulesView, ReplyStyleView,
  AddFollowUpModal,
});
