// Card detail — centered modal (Trello-style).

// Description textarea: auto-grows to fit its content as you type (no
// internal scrollbar), while still allowing manual drag-resize. Whichever
// height it ends up at — auto or manually dragged — is remembered per task
// so reopening the card doesn't reset it.
function DescArea({ task }) {
  const ref = React.useRef(null);
  const lastSetRef = React.useRef(null);
  const storageKey = 'descH:' + task.id;

  function autoGrow(el) {
    el.style.height = 'auto';
    const h = el.scrollHeight;
    el.style.height = h + 'px';
    lastSetRef.current = h;
    try { localStorage.setItem(storageKey, String(h)); } catch (e) {}
  }

  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let saved = 0;
    try { saved = parseFloat(localStorage.getItem(storageKey)) || 0; } catch (e) {}
    if (saved) {
      el.style.height = saved + 'px';
      lastSetRef.current = saved;
    }
    // Whether restored to a remembered size or the CSS default, grow further
    // if the content still overflows what's currently shown.
    if (el.scrollHeight > el.clientHeight) autoGrow(el);

    const ro = new ResizeObserver(() => {
      const h = Math.round(el.getBoundingClientRect().height);
      if (lastSetRef.current !== null && Math.abs(h - lastSetRef.current) < 1) return; // our own auto-grow, already saved
      lastSetRef.current = h;
      try { localStorage.setItem(storageKey, String(h)); } catch (e) {} // user dragged the resize handle
    });
    ro.observe(el);
    return () => ro.disconnect();
  }, [task.id]);

  return (
    <textarea ref={ref} className="desc-area" defaultValue={task.notes || ''}
      placeholder="Add a description…"
      onInput={(e) => autoGrow(e.target)}
      onBlur={(e) => actions.updateTask(task.id, { notes: e.target.value })} />
  );
}

function SidePanel() {
  const s = useStore();
  const task = s.tasks.find((t) => t.id === s.activeTaskId);
  const open = !!task;
  const project = task ? s.projects.find((p) => p.id === task.projectId) : null;
  const [menuAnchor, setMenuAnchor] = React.useState(null);
  const [addFieldAnchor, setAddFieldAnchor] = React.useState(null);
  const [editingFieldId, setEditingFieldId] = React.useState(null);
  const [depPickerDir, setDepPickerDir] = React.useState(null); // 'in' | 'out' | null
  const [depPickerAnchor, setDepPickerAnchor] = React.useState(null);
  const [depSearch, setDepSearch] = React.useState('');
  const [childPickerAnchor, setChildPickerAnchor] = React.useState(null);
  const [childSearch, setChildSearch] = React.useState('');

  function close() { actions.setActiveTask(null); }

  React.useEffect(() => {
    function key(e) {
      if (e.key === 'Escape' && open && !menuAnchor && !addFieldAnchor && !editingFieldId && !depPickerDir && !childPickerAnchor) close();
    }
    document.addEventListener('keydown', key);
    return () => document.removeEventListener('keydown', key);
  }, [open, menuAnchor, addFieldAnchor, editingFieldId, depPickerDir, childPickerAnchor]);

  if (!task) return null;

  const stage = project.stages.find((st) => st.id === task.stageId);
  const sidebarFields  = project.fields.filter(f => !f.showOnCard || f.builtin); // sidebar shows everything; the showOnCard flag is for board card display
  // For the modal we render ALL fields; the flag governs the *card*. Keep modal complete.
  const builtinFields = project.fields.filter(f => f.builtin);
  const customFields  = project.fields.filter(f => !f.builtin);

  const allTasks = s.tasks.filter(t => t.projectId === project.id && t.id !== task.id);
  const depsInTasks  = (task.depsIn  || []).map(id => s.tasks.find(t => t.id === id)).filter(Boolean);
  const depsOutTasks = (task.depsOut || []).map(id => s.tasks.find(t => t.id === id)).filter(Boolean);
  const epicChildren = task.isEpic ? allTasks.filter(t => t.epicId === task.id) : [];
  const epicProgress = task.isEpic ? getEpicProgress(project, task.id) : null;
  const epicPct = epicProgress && epicProgress.total > 0 ? Math.round(epicProgress.done / epicProgress.total * 100) : 0;
  const storyChildren = task.isStory ? allTasks.filter(t => !t.isEpic && !t.isStory && t.storyId === task.id) : [];
  const storyProgress = task.isStory ? getStoryProgress(task.id) : null;
  const storyPct = storyProgress && storyProgress.total > 0 ? Math.round(storyProgress.done / storyProgress.total * 100) : 0;
  const parentInfo = getParentInfo(task);
  const blockingDeps = depsInTasks.filter(d => !d.done);

  return (
    <div className="modal-backdrop card-modal-backdrop" onClick={close}>
      <div className="modal card-modal" onClick={(e) => e.stopPropagation()}>
        <div className="card-modal-head">
          <div className="cm-crumbs">
            <span className="sb-color" style={{ background: project.color, marginRight: 6 }}></span>
            <span>{project.name}</span>
            <span className="cm-sep">›</span>
            <span>{stage?.name}</span>
          </div>
          <div style={{ display: 'flex', gap: 4 }}>
            <button className="btn btn-ghost" onClick={(e) => setMenuAnchor(e.currentTarget)} title="More">⋯</button>
            <button className="btn btn-ghost" onClick={close} title="Close (Esc)">✕</button>
          </div>
        </div>

        <div className="card-modal-body">
          <div className="cm-main">
            <Editable
              tag="h2"
              value={task.title}
              onChange={(v) => actions.updateTask(task.id, { title: v.trim() || 'Untitled' })}
              placeholder="Task title"
            />

            {parentInfo && (
              <button className="parent-jump-btn cm-parent-jump" onClick={() => actions.setActiveTask(parentInfo.id)}
                title={`Jump to ${parentInfo.label}: ${parentInfo.title}`}>
                ↑ {parentInfo.label}: {parentInfo.title}
              </button>
            )}

            {/* Description — directly under title */}
            <DescArea task={task} key={task.id + '-desc'} />

            {/* Subtasks — epics organize work through linked tickets instead */}
            {!task.isEpic && (
              <div className="sp-section">
                <div className="sp-section-title">
                  <span>Subtasks ({task.subtasks.filter((s) => s.done).length}/{task.subtasks.length})</span>
                </div>
                {task.subtasks.map((st) =>
                  <div key={st.id} className={'subtask' + (st.done ? ' done' : '')}>
                    <input type="checkbox" checked={st.done} onChange={(e) => actions.updateSubtask(task.id, st.id, { done: e.target.checked })} />
                    <Editable tag="div" className="st-text" value={st.text}
                      onChange={(v) => actions.updateSubtask(task.id, st.id, { text: v.trim() || 'Subtask' })} />
                    <button className="st-del" onClick={() => actions.deleteSubtask(task.id, st.id)}>✕</button>
                  </div>
                )}
                <SubtaskAdder taskId={task.id} />
              </div>
            )}

            {/* Dependencies */}
            <div className="sp-section">
              <div className="sp-section-title">
                <span>In Dependency <span className="dep-count">{depsInTasks.length}</span></span>
                <button className="add-inline-tiny" onClick={(e) => { setDepSearch(''); setDepPickerAnchor(e.currentTarget); setDepPickerDir('in'); }}>+ Add</button>
              </div>
              {depsInTasks.length === 0 && (
                <div className="empty-fields" style={{ marginBottom: 6 }}>
                  Tasks that must be completed before this one can start.
                </div>
              )}
              {blockingDeps.length > 0 && (
                <div className="dep-warning">
                  ⚠ {blockingDeps.length} dependenc{blockingDeps.length === 1 ? 'y' : 'ies'} not done yet — you may not be able to start this.
                </div>
              )}
              {depsInTasks.map(dt => (
                <DepRow key={dt.id} task={dt} project={project}
                  onOpen={() => actions.setActiveTask(dt.id)}
                  onRemove={() => actions.removeDep(task.id, dt.id, 'in')}
                  warnIfNotDone />
              ))}

              <div className="sp-section-title" style={{ marginTop: 14 }}>
                <span>Out Dependency <span className="dep-count">{depsOutTasks.length}</span></span>
                <button className="add-inline-tiny" onClick={(e) => { setDepSearch(''); setDepPickerAnchor(e.currentTarget); setDepPickerDir('out'); }}>+ Add</button>
              </div>
              {depsOutTasks.length === 0 && (
                <div className="empty-fields" style={{ marginBottom: 6 }}>
                  Tasks that depend on this one.
                </div>
              )}
              {depsOutTasks.map(dt => (
                <DepRow key={dt.id} task={dt} project={project}
                  onOpen={() => actions.setActiveTask(dt.id)}
                  onRemove={() => actions.removeDep(task.id, dt.id, 'out')} />
              ))}
            </div>

            {/* Comments */}
            <div className="sp-section">
              <div className="sp-section-title">
                <span>Comments{(task.comments || []).length > 0 && <span className="dep-count" style={{ marginLeft: 5 }}>{(task.comments || []).length}</span>}</span>
              </div>
              {(task.comments || []).map(c => (
                <CommentRow key={c.id} comment={c}
                  onDelete={() => actions.deleteComment(task.id, c.id)} />
              ))}
              <CommentInput key={task.id + '-ci'} onAdd={(text) => actions.addComment(task.id, text)} />
            </div>
          </div>

          <div className="cm-side">
            <div className="cm-side-title">Properties</div>

            <div className="field-row">
              <div className="field-label">Done</div>
              <div className="field-value">
                <input type="checkbox" checked={!!task.done} onChange={(e) => actions.setTaskDone(task.id, e.target.checked)} />
              </div>
            </div>

            <div className="field-row">
              <div className="field-label">Stage</div>
              <div className="field-value">
                <StageEditor project={project} task={task} allTasks={s.tasks} />
              </div>
            </div>

            <div className="field-row">
              <div className="field-label">{task.isStory ? 'Parent epic' : 'Parent'}</div>
              <div className="field-value">
                {task.isEpic ? (
                  <span className="epic-self-note">This ticket is an Epic</span>
                ) : task.isStory ? (
                  <EpicFieldEditor project={project} task={task} allTasks={s.tasks} />
                ) : (
                  <ParentFieldEditor project={project} task={task} allTasks={s.tasks} />
                )}
              </div>
            </div>

            {builtinFields.map((f) =>
              <div className="field-row" key={f.id}>
                <div className="field-label">
                  <span className="field-color-dot" style={{ background: f.id === 'priority' ? priorityColor(project, task.values.priority) || f.color : f.color }} />
                  {f.name}
                </div>
                <div className="field-value">
                  <FieldValueEditor field={f} value={task.values[f.id]} project={project}
                    onChange={(v) => actions.setTaskValue(task.id, f.id, v)} />
                </div>
              </div>
            )}

            {task.isEpic && (
              <div className="sp-section" style={{ marginTop: 16 }}>
                <div className="cm-side-title">Epic color</div>
                <ColorSwatchPicker value={task.epicColor || '#9a948a'} onChange={(c) => actions.setEpicColor(task.id, c)} />

                <div className="sp-section-title" style={{ marginTop: 14 }}>
                  <span>Linked tickets <span className="dep-count">{epicChildren.length}</span></span>
                  <button className="add-inline-tiny" onClick={(e) => { setChildSearch(''); setChildPickerAnchor(e.currentTarget); }}>+ Add</button>
                </div>
                {epicProgress.total > 0 && (
                  <div className="epic-progress-bar" style={{ marginBottom: 8 }}>
                    <div className="epic-progress-fill" style={{ width: epicPct + '%', background: task.epicColor || '#9a948a' }} />
                  </div>
                )}
                {epicChildren.length === 0 &&
                  <div className="empty-fields">No tickets linked yet. Click + Add.</div>
                }
                {epicChildren.map(ct => (
                  <DepRow key={ct.id} task={ct} project={project}
                    onOpen={() => actions.setActiveTask(ct.id)}
                    onRemove={() => actions.setTaskEpic(ct.id, null)}
                    badge={ct.isStory ? 'Story' : undefined} />
                ))}
              </div>
            )}

            {task.isStory && (
              <div className="sp-section" style={{ marginTop: 16 }}>
                <div className="cm-side-title">Story color</div>
                <ColorSwatchPicker value={task.storyColor || '#7a9bc4'} onChange={(c) => actions.setStoryColor(task.id, c)} />

                <div className="sp-section-title" style={{ marginTop: 14 }}>
                  <span>Linked tasks <span className="dep-count">{storyChildren.length}</span></span>
                  <button className="add-inline-tiny" onClick={(e) => { setChildSearch(''); setChildPickerAnchor(e.currentTarget); }}>+ Add</button>
                </div>
                {storyProgress.total > 0 && (
                  <div className="epic-progress-bar" style={{ marginBottom: 8 }}>
                    <div className="epic-progress-fill" style={{ width: storyPct + '%', background: task.storyColor || '#7a9bc4' }} />
                  </div>
                )}
                {storyChildren.length === 0 &&
                  <div className="empty-fields">No tasks linked yet. Click + Add.</div>
                }
                {storyChildren.map(ct => (
                  <DepRow key={ct.id} task={ct} project={project}
                    onOpen={() => actions.setActiveTask(ct.id)}
                    onRemove={() => actions.setTaskStory(ct.id, null)} />
                ))}
              </div>
            )}

            <div className="cm-side-title" style={{ marginTop: 16, display: 'flex', justifyContent: 'space-between' }}>
              <span>Custom fields</span>
              <button className="add-inline-tiny" onClick={(e) => setAddFieldAnchor(e.currentTarget)}>+ Add</button>
            </div>
            {customFields.length === 0 &&
              <div className="empty-fields">
                No custom fields yet. Click + Add.
              </div>
            }
            {customFields.map((f) =>
              <div className="field-row" key={f.id}>
                <div className="field-label cf-label">
                  <span className="field-color-dot" style={{ background: f.color }} />
                  <span className="cf-name" onClick={() => setEditingFieldId(f.id)} title="Click to edit">{f.name}</span>
                </div>
                <div className="field-value">
                  <FieldValueEditor field={f} value={task.values[f.id]} project={project}
                    onChange={(v) => actions.setTaskValue(task.id, f.id, v)} />
                </div>
              </div>
            )}
          </div>
        </div>

        {menuAnchor &&
          <Popover anchor={menuAnchor} onClose={() => setMenuAnchor(null)}>
            {!task.isEpic && !task.isStory && (
              <>
                <div className="item" onClick={() => { actions.addSubtask(task.id, 'New subtask'); setMenuAnchor(null); }}>+ Add subtask</div>
                <div className="sep" />
              </>
            )}
            <div className="item" onClick={() => { actions.setEpicFlag(task.id, !task.isEpic); setMenuAnchor(null); }}>
              {task.isEpic ? '◇ Remove Epic status' : '◆ Convert to Epic — its subtasks become linked tickets'}
            </div>
            <div className="item" onClick={() => { actions.setStoryFlag(task.id, !task.isStory); setMenuAnchor(null); }}>
              {task.isStory ? '◇ Remove Story status' : '◇ Convert to Story — its subtasks become linked tasks'}
            </div>
            <div className="sep" />
            <div className="item danger" onClick={() => {
              if (confirm('Delete this task?')) { actions.deleteTask(task.id); setMenuAnchor(null); }
            }}>Delete task</div>
          </Popover>
        }

        {addFieldAnchor &&
          <AddFieldPopover project={project} anchor={addFieldAnchor} onClose={() => setAddFieldAnchor(null)} />
        }

        {editingFieldId && (() => {
          const f = project.fields.find((ff) => ff.id === editingFieldId);
          if (!f || f.builtin) { setTimeout(() => setEditingFieldId(null), 0); return null; }
          return <EditFieldDialog project={project} field={f} onClose={() => setEditingFieldId(null)} />;
        })()}

        {depPickerDir && depPickerAnchor && (
          <Popover anchor={depPickerAnchor} onClose={() => { setDepPickerDir(null); setDepPickerAnchor(null); setDepSearch(''); }} align="left">
            <div className="dep-picker">
              <div className="ftp-title">{depPickerDir === 'in' ? 'Add an In Dependency' : 'Add an Out Dependency'}</div>
              <input
                autoFocus
                className="dep-search-input"
                placeholder="Search tasks…"
                value={depSearch}
                onChange={e => setDepSearch(e.target.value)}
              />
              {(() => {
                const eligible = allTasks
                  .filter(t => !(depPickerDir === 'in' ? task.depsIn : task.depsOut).includes(t.id))
                  .filter(t => !depSearch.trim() || t.title.toLowerCase().includes(depSearch.toLowerCase()));
                if (eligible.length === 0) return (
                  <div className="empty-fields" style={{ margin: '4px 0' }}>
                    {depSearch.trim() ? 'No matching tasks.' : 'No other tasks in this project.'}
                  </div>
                );
                return eligible.slice(0, 40).map(t => (
                  <div key={t.id} className="ftp-row" onClick={() => {
                    actions.addDep(task.id, t.id, depPickerDir);
                    setDepPickerDir(null); setDepPickerAnchor(null); setDepSearch('');
                  }}>
                    <span className="prio-dot" style={{ background: priorityColor(project, t.values.priority) }}></span>
                    <span className="ftp-label" style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{t.title}</span>
                    <span className="ftp-hint">{project.stages.find(s => s.id === t.stageId)?.name}</span>
                  </div>
                ));
              })()}
            </div>
          </Popover>
        )}

        {childPickerAnchor && (
          <Popover anchor={childPickerAnchor} onClose={() => { setChildPickerAnchor(null); setChildSearch(''); }} align="left">
            <div className="dep-picker">
              <div className="ftp-title">{task.isStory ? 'Link a task to this Story' : 'Link a ticket to this Epic'}</div>
              <input
                autoFocus
                className="dep-search-input"
                placeholder="Search tasks…"
                value={childSearch}
                onChange={e => setChildSearch(e.target.value)}
              />
              {(() => {
                const eligible = allTasks
                  .filter(t => task.isStory
                    ? (!t.isEpic && !t.isStory && t.storyId !== task.id)
                    : (!t.isEpic && t.epicId !== task.id && !t.storyId))
                  .filter(t => !childSearch.trim() || t.title.toLowerCase().includes(childSearch.toLowerCase()));
                if (eligible.length === 0) return (
                  <div className="empty-fields" style={{ margin: '4px 0' }}>
                    {childSearch.trim() ? 'No matching tasks.' : 'No other tickets in this project.'}
                  </div>
                );
                return eligible.slice(0, 40).map(t => (
                  <div key={t.id} className="ftp-row" onClick={() => {
                    if (task.isStory) actions.setTaskStory(t.id, task.id);
                    else actions.setTaskEpic(t.id, task.id);
                    setChildPickerAnchor(null); setChildSearch('');
                  }}>
                    <span className="prio-dot" style={{ background: priorityColor(project, t.values.priority) }}></span>
                    <span className="ftp-label" style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{t.title}</span>
                    <span className="ftp-hint">{project.stages.find(s => s.id === t.stageId)?.name}</span>
                  </div>
                ));
              })()}
            </div>
          </Popover>
        )}
      </div>
    </div>
  );
}

function StageEditor({ project, task, allTasks }) {
  const [open, setOpen] = React.useState(false);
  const triggerRef = React.useRef(null);
  const stage = project.stages.find((st) => st.id === task.stageId);
  const color = '#9a948a';

  function moveTo(stageId) {
    const tasksInTarget = allTasks.filter((t) => t.projectId === project.id && t.stageId === stageId);
    actions.moveTask(task.id, stageId, tasksInTarget.length);
    setOpen(false);
  }

  return (
    <>
      <button
        ref={triggerRef}
        className={'sel-trigger' + (stage ? ' has-value' : '')}
        onClick={(e) => { e.stopPropagation(); setOpen(o => !o); }}
        style={stage ? { background: tint(color, 18), color: '#1c1b18', borderColor: 'transparent' } : undefined}
      >
        <span className="sel-trigger-label">{stage?.name || <em style={{ color: 'var(--ink-3)' }}>—</em>}</span>
        <span className="sel-trigger-caret">▾</span>
      </button>
      {open && triggerRef.current && (
        <Popover anchor={triggerRef.current} onClose={() => setOpen(false)} align="left">
          <div className="opt-list">
            {project.stages.map((st) => (
              <div key={st.id} className={'opt-item' + (st.id === task.stageId ? ' selected' : '')}
                onClick={() => moveTo(st.id)}>
                <span className="opt-chip" style={{ background: tint(color, 22), color: '#1c1b18' }}>{st.name}</span>
              </div>
            ))}
          </div>
        </Popover>
      )}
    </>
  );
}

function ParentFieldEditor({ project, task, allTasks }) {
  const [open, setOpen] = React.useState(false);
  const triggerRef = React.useRef(null);
  const epics = allTasks.filter(t => t.projectId === project.id && t.isEpic).sort((a, b) => (a.epicOrder ?? 0) - (b.epicOrder ?? 0));
  const stories = allTasks.filter(t => t.projectId === project.id && t.isStory);
  const current = task.storyId ? stories.find(e => e.id === task.storyId)
    : task.epicId ? epics.find(e => e.id === task.epicId)
    : null;
  const color = current ? ((task.storyId ? current.storyColor : current.epicColor) || '#9a948a') : '#9a948a';

  function pickEpic(epicId) { actions.setTaskEpic(task.id, epicId); setOpen(false); }
  function pickStory(storyId) { actions.setTaskStory(task.id, storyId); setOpen(false); }
  function clear() { actions.setTaskEpic(task.id, null); actions.setTaskStory(task.id, null); setOpen(false); }

  return (
    <>
      <button
        ref={triggerRef}
        className={'sel-trigger' + (current ? ' has-value' : '')}
        onClick={(e) => { e.stopPropagation(); setOpen(o => !o); }}
        style={current ? { background: tint(color, 18), color: '#1c1b18', borderColor: 'transparent' } : undefined}
      >
        <span className="sel-trigger-label">{current?.title || <em style={{ color: 'var(--ink-3)' }}>—</em>}</span>
        <span className="sel-trigger-caret">▾</span>
      </button>
      {open && triggerRef.current && (
        <Popover anchor={triggerRef.current} onClose={() => setOpen(false)} align="left">
          <div className="opt-list">
            <div className="opt-item opt-clear" onClick={clear}>
              <em>— none —</em>
            </div>
            {stories.length > 0 && <div className="opt-group-label">Stories</div>}
            {stories.map(st => (
              <div key={st.id} className={'opt-item' + (st.id === task.storyId ? ' selected' : '')}
                onClick={() => pickStory(st.id)}>
                <span className="opt-chip" style={{ background: tint(st.storyColor || '#7a9bc4', 22), color: '#1c1b18' }}>{st.title}</span>
              </div>
            ))}
            {epics.length > 0 && <div className="opt-group-label">Epics</div>}
            {epics.map(e => (
              <div key={e.id} className={'opt-item' + (e.id === task.epicId ? ' selected' : '')}
                onClick={() => pickEpic(e.id)}>
                <span className="opt-chip" style={{ background: tint(e.epicColor || '#9a948a', 22), color: '#1c1b18' }}>{e.title}</span>
              </div>
            ))}
            {stories.length === 0 && epics.length === 0 && <div className="opt-empty">No epics or stories in this project yet</div>}
          </div>
        </Popover>
      )}
    </>
  );
}

function EpicFieldEditor({ project, task, allTasks }) {
  const [open, setOpen] = React.useState(false);
  const triggerRef = React.useRef(null);
  const epics = allTasks.filter(t => t.projectId === project.id && t.isEpic).sort((a, b) => (a.epicOrder ?? 0) - (b.epicOrder ?? 0));
  const current = task.epicId ? epics.find(e => e.id === task.epicId) : null;
  const color = current?.epicColor || '#9a948a';

  function pick(epicId) {
    actions.setTaskEpic(task.id, epicId);
    setOpen(false);
  }

  return (
    <>
      <button
        ref={triggerRef}
        className={'sel-trigger' + (current ? ' has-value' : '')}
        onClick={(e) => { e.stopPropagation(); setOpen(o => !o); }}
        style={current ? { background: tint(color, 18), color: '#1c1b18', borderColor: 'transparent' } : undefined}
      >
        <span className="sel-trigger-label">{current?.title || <em style={{ color: 'var(--ink-3)' }}>—</em>}</span>
        <span className="sel-trigger-caret">▾</span>
      </button>
      {open && triggerRef.current && (
        <Popover anchor={triggerRef.current} onClose={() => setOpen(false)} align="left">
          <div className="opt-list">
            <div className="opt-item opt-clear" onClick={() => pick(null)}>
              <em>— none —</em>
            </div>
            {epics.length === 0 && <div className="opt-empty">No epics in this project yet</div>}
            {epics.map(e => (
              <div key={e.id} className={'opt-item' + (e.id === task.epicId ? ' selected' : '')}
                onClick={() => pick(e.id)}>
                <span className="opt-chip" style={{ background: tint(e.epicColor || '#9a948a', 22), color: '#1c1b18' }}>{e.title}</span>
              </div>
            ))}
          </div>
        </Popover>
      )}
    </>
  );
}

function DepRow({ task, project, onOpen, onRemove, warnIfNotDone, badge }) {
  const stage = project.stages.find(s => s.id === task.stageId);
  const notDone = warnIfNotDone && !task.done;
  return (
    <div className={'dep-row' + (notDone ? ' dep-row-warn' : '')}>
      <span className="prio-dot" style={{ background: priorityColor(project, task.values.priority) }}></span>
      <span className="dep-title" onClick={onOpen} title="Open">{task.title}</span>
      {badge && <span className="dep-badge">{badge}</span>}
      {notDone && <span className="dep-warn-badge" title="Not done yet">⚠</span>}
      <span className="dep-stage">{stage?.name}</span>
      <button className="dep-rm" onClick={onRemove} title="Remove">✕</button>
    </div>
  );
}

// ─── Add custom field popover ───
function AddFieldPopover({ project, anchor, onClose }) {
  const [step, setStep] = React.useState('type');
  const [type, setType] = React.useState('text');
  const [name, setName] = React.useState('');
  const [optDraft, setOptDraft] = React.useState('');
  const [opts, setOpts] = React.useState([]);

  function pickType(t) { setType(t); setName(''); setOpts([]); setStep('config'); }
  function commit() {
    const n = name.trim(); if (!n) return;
    const field = { name: n, type };
    if (type === 'select' || type === 'multi_select') field.options = opts;
    actions.addField(project.id, field);
    onClose();
  }
  function addOpt() {
    const v = optDraft.trim();
    if (v && !opts.includes(v)) setOpts([...opts, v]);
    setOptDraft('');
  }

  return (
    <Popover anchor={anchor} onClose={onClose} align="right">
      {step === 'type' &&
        <div className="field-type-picker">
          <div className="ftp-title">Field type</div>
          {FIELD_TYPE_CATALOG.map((ft) =>
            <div key={ft.type} className="ftp-row" onClick={() => pickType(ft.type)}>
              <span className="ftp-icon">{ft.icon}</span>
              <span className="ftp-label">{ft.label}</span>
              <span className="ftp-hint">{ft.hint}</span>
            </div>
          )}
        </div>
      }
      {step === 'config' &&
        <div className="field-config">
          <div className="fc-row">
            <button className="btn btn-ghost" onClick={() => setStep('type')} title="Back">‹</button>
            <span className="fc-type-label">{fieldTypeLabel(type)}</span>
          </div>
          <input autoFocus className="fc-name" placeholder="Field name…" value={name}
            onChange={(e) => setName(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Enter' && (type !== 'select' && type !== 'multi_select' || opts.length > 0)) commit(); }} />
          {(type === 'select' || type === 'multi_select') &&
            <div className="fc-opts">
              <div className="fc-opts-label">Options</div>
              {opts.map((o) =>
                <span key={o} className="ms-tag">{o}<button onClick={() => setOpts(opts.filter((x) => x !== o))}>✕</button></span>
              )}
              <input className="fc-opt-input" placeholder="Type & press Enter…" value={optDraft}
                onChange={(e) => setOptDraft(e.target.value)}
                onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); addOpt(); } }}
                onBlur={addOpt} />
            </div>
          }
          <div className="fc-actions">
            <button className="btn btn-ghost" onClick={onClose}>Cancel</button>
            <button className="btn" onClick={commit} disabled={!name.trim()}>Add field</button>
          </div>
        </div>
      }
    </Popover>
  );
}

// ─── Edit / delete custom field dialog ───
function EditFieldDialog({ project, field, onClose }) {
  const [name, setName] = React.useState(field.name);
  const [type, setType] = React.useState(field.type);
  const [opts, setOpts] = React.useState(field.options || []);
  const [optDraft, setOptDraft] = React.useState('');
  const [color, setColor] = React.useState(field.color || '#8a8780');
  const [showOnCard, setShowOnCard] = React.useState(field.showOnCard !== false);
  const isSelect = type === 'select' || type === 'multi_select';

  function save() {
    const patch = { name: name.trim() || field.name, type, color, showOnCard };
    if (isSelect) patch.options = opts;
    actions.updateField(project.id, field.id, patch);
    onClose();
  }
  function addOpt() {
    const v = optDraft.trim();
    if (v && !opts.includes(v)) setOpts([...opts, v]);
    setOptDraft('');
  }
  function remove() {
    if (confirm(`Delete field "${field.name}"? Values on all tasks will be cleared.`)) {
      actions.deleteField(project.id, field.id); onClose();
    }
  }

  return (
    <div className="modal-backdrop" onClick={onClose} style={{ zIndex: 250 }}>
      <div className="modal small" onClick={(e) => e.stopPropagation()}>
        <div className="modal-head">
          <span>Edit field</span>
          <button className="btn-ghost btn" onClick={onClose}>✕</button>
        </div>
        <div className="modal-body">
          <div className="fc-row" style={{ marginBottom: 10 }}>
            <select
              value={type}
              onChange={(e) => setType(e.target.value)}
              style={{
                background: 'var(--accent-bg)', color: 'var(--accent)',
                border: '1px solid transparent', borderRadius: 3,
                padding: '3px 8px', fontSize: 11, fontWeight: 600,
                cursor: 'pointer',
              }}
            >
              {FIELD_TYPE_CATALOG.map(ft => (
                <option key={ft.type} value={ft.type}>{ft.label}</option>
              ))}
            </select>
          </div>
          <input autoFocus className="fc-name" placeholder="Field name…" value={name}
            onChange={(e) => setName(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Enter') save(); }} />

          <div className="ef-grid" style={{ marginTop: 14 }}>
              <label className="ef-row">
              <span>Label color</span>
              <span className="ef-color">
                <ColorSwatchPicker value={color} onChange={setColor} />
              </span>
            </label>
            <label className="ef-row">
              <span>Show on card</span>
              <input type="checkbox" checked={showOnCard} onChange={(e) => setShowOnCard(e.target.checked)} />
            </label>
          </div>

          {isSelect &&
            <div className="fc-opts" style={{ marginTop: 10 }}>
              <div className="fc-opts-label">Options</div>
              {opts.map((o) =>
                <span key={o} className="ms-tag">{o}<button onClick={() => setOpts(opts.filter((x) => x !== o))}>✕</button></span>
              )}
              <input className="fc-opt-input" placeholder="Add option…" value={optDraft}
                onChange={(e) => setOptDraft(e.target.value)}
                onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); addOpt(); } }}
                onBlur={addOpt} />
            </div>
          }
        </div>
        <div className="modal-foot">
          <button className="btn btn-danger-ghost" onClick={remove}>Delete field</button>
          <span style={{ flex: 1 }} />
          <button className="btn btn-ghost" onClick={onClose}>Cancel</button>
          <button className="btn" onClick={save}>Save</button>
        </div>
      </div>
    </div>
  );
}

function fieldTypeLabel(type) {
  return (FIELD_TYPE_CATALOG.find((f) => f.type === type) || { label: type }).label;
}

function CommentRow({ comment, onDelete }) {
  const d = new Date(comment.createdAt);
  const dateStr = d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
  const timeStr = d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
  return (
    <div className="comment-row">
      <div className="comment-meta">
        <span className="comment-timestamp">{dateStr} · {timeStr}</span>
        <button className="comment-del" onClick={onDelete} title="Delete">✕</button>
      </div>
      <div className="comment-text">{comment.text}</div>
    </div>
  );
}

function CommentInput({ onAdd }) {
  const [text, setText] = React.useState('');
  function submit() {
    if (!text.trim()) return;
    onAdd(text.trim());
    setText('');
  }
  return (
    <div className="comment-input-wrap">
      <textarea
        className="comment-textarea"
        placeholder="Add a comment… (⌘↵ to post)"
        value={text}
        rows={2}
        onChange={e => setText(e.target.value)}
        onKeyDown={e => {
          if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); submit(); }
        }}
      />
      {text.trim() && (
        <div className="comment-actions">
          <button className="btn btn-ghost" style={{ fontSize: 11 }} onClick={() => setText('')}>Cancel</button>
          <button className="btn" style={{ fontSize: 11 }} onClick={submit}>Post comment</button>
        </div>
      )}
    </div>
  );
}

function SubtaskAdder({ taskId }) {
  const [adding, setAdding] = React.useState(false);
  const [text, setText] = React.useState('');
  const inputRef = React.useRef(null);

  function commit(continueAfter) {
    const v = text.trim();
    if (v) actions.addSubtask(taskId, v);
    setText('');
    if (continueAfter && v) {
      // Stay in adding mode, keep focus for the next subtask
      requestAnimationFrame(() => inputRef.current?.focus());
    } else {
      setAdding(false);
    }
  }

  if (!adding) {
    return <button className="add-inline" onClick={() => setAdding(true)}>+ Add subtask</button>;
  }
  return (
    <div className="subtask subtask-adding">
      <input type="checkbox" disabled />
      <input
        ref={inputRef}
        autoFocus
        className="st-text st-input"
        placeholder="Subtask name…"
        value={text}
        onChange={(e) => setText(e.target.value)}
        onBlur={() => commit(false)}
        onKeyDown={(e) => {
          if (e.key === 'Enter') { e.preventDefault(); commit(true); }
          if (e.key === 'Escape') { setText(''); setAdding(false); }
        }}
      />
    </div>
  );
}

window.SidePanel = SidePanel;
