// App shell — sidebar (collapsible), topbar (with filter bar), screen routing.

// Shared timeline-zoom state between Topbar (the picker) and Screen (the
// TimelineScreen consumer) — previously synced via a polled window global,
// replaced with a plain context to avoid a perpetual 200ms re-render tick.
const TlZoomCtx = React.createContext(['week', () => {}]);

// ─── WorkspaceSection ──────────────────────────────────────────────────────
function WorkspaceSection() {
  const s = useStore();
  const isServer = !!window.serverAPI;
  const [workspaces, setWorkspaces] = React.useState([]);
  const [current, setCurrent] = React.useState('');
  const [open, setOpen] = React.useState(false);
  const btnRef = React.useRef(null);
  const [busy, setBusy] = React.useState(false);
  const [creating, setCreating] = React.useState(false);

  function refresh() {
    if (!isServer) return;
    window.serverAPI.listWorkspaces().then(list => {
      setWorkspaces(list || []);
      setCurrent(window.getActiveWorkspaceName() || window.serverAPI.getActiveWorkspace() || '');
    }).catch(() => {});
  }

  React.useEffect(() => { refresh(); }, []);

  async function handleSwitch(name) {
    if (busy || name === current) { setOpen(false); return; }
    setBusy(true);
    try {
      await actions.switchWorkspace(name);
      setCurrent(name);
      showToast(`Switched to "${name}"`);
    } finally { setBusy(false); setOpen(false); }
  }

  async function handleCreate(name) {
    if (!name || !name.trim()) return;
    setBusy(true);
    try {
      await actions.createWorkspace(name.trim());
      refresh();
      showToast(`Created "${name.trim()}"`);
      setCreating(false);
    } catch (e) {
      alert(e.message || 'Could not create workspace');
    } finally { setBusy(false); setOpen(false); }
  }

  async function handleLogout() {
    await window.serverAPI.logout();
    window.appBoot();
  }

  if (!isServer) {
    // Design-preview / non-server fallback: keep it simple.
    return (
      <>
        <div className="sb-section">Data</div>
        <div className="sb-item" onClick={() => {
          const blob = new Blob([JSON.stringify(s, null, 2)], { type: 'application/json' });
          const url = URL.createObjectURL(blob);
          const a = document.createElement('a');
          a.href = url; a.download = 'tasks-export.json';
          document.body.appendChild(a); a.click(); a.remove();
          URL.revokeObjectURL(url);
          showToast('Exported');
        }}>Export JSON</div>
        <div className="sb-item" onClick={() => {
          if (confirm('Reset all data to seed examples?')) { actions.resetData(); showToast('Reset'); }
        }}>Reset to seed</div>
      </>
    );
  }

  return (
    <>
      <div className="sb-section">Workspace</div>
      <div className="sb-db-info" ref={btnRef} onClick={() => setOpen(o => !o)} style={{ cursor: 'pointer' }} title={current}>
        <span className="sb-db-name">{current || 'Loading…'}</span>
        <button className="sb-db-show" title="Switch workspace">▾</button>
      </div>
      {open && btnRef.current && (
        <Popover anchor={btnRef.current} onClose={() => setOpen(false)} align="left">
          <div className="ftp-title">Workspaces</div>
          {workspaces.map(w => (
            <div key={w.name} className={'item' + (w.name === current ? ' selected' : '')} onClick={() => handleSwitch(w.name)}>
              {w.name === current ? '● ' : ''}{w.name}
            </div>
          ))}
          <div className="sep" />
          <div className="item" onClick={() => { setCreating(true); setOpen(false); }}>+ New workspace…</div>
        </Popover>
      )}
      {creating && (
        <PromptDialog
          title="New workspace"
          placeholder="Workspace name…"
          confirmLabel="Create"
          onConfirm={handleCreate}
          onCancel={() => setCreating(false)}
        />
      )}
      <div className="sb-item" onClick={handleLogout}>Log out</div>
      <div className="sb-sep" />
    </>
  );
}

function Sidebar() {
  const s = useStore();
  const collapsed = !!s.settings?.sidebarCollapsed;
  const [addingProject, setAddingProject] = React.useState(false);

  const [sbWidth, setSbWidth] = React.useState(() => {
    const saved = localStorage.getItem('sb_width');
    return saved ? Math.max(160, Math.min(480, parseInt(saved, 10))) : 220;
  });
  const dragRef = React.useRef(null);
  const widthRef = React.useRef(sbWidth);
  const handleRef = React.useRef(null);

  React.useEffect(() => {
    function onMove(e) {
      if (!dragRef.current) return;
      const newW = Math.max(160, Math.min(480, dragRef.current.w0 + e.clientX - dragRef.current.x0));
      setSbWidth(newW);
      widthRef.current = newW;
    }
    function onUp() {
      if (!dragRef.current) return;
      localStorage.setItem('sb_width', String(widthRef.current));
      dragRef.current = null;
      if (handleRef.current) handleRef.current.classList.remove('dragging');
      document.body.style.cursor = '';
      document.body.style.userSelect = '';
    }
    document.addEventListener('mousemove', onMove);
    document.addEventListener('mouseup', onUp);
    return () => {
      document.removeEventListener('mousemove', onMove);
      document.removeEventListener('mouseup', onUp);
    };
  }, []);

  function startResize(e) {
    e.preventDefault();
    dragRef.current = { x0: e.clientX, w0: sbWidth };
    widthRef.current = sbWidth;
    if (handleRef.current) handleRef.current.classList.add('dragging');
    document.body.style.cursor = 'ew-resize';
    document.body.style.userSelect = 'none';
  }

  return (
    <div className={'sidebar' + (collapsed ? ' collapsed' : '')} style={collapsed ? {} : { width: sbWidth }}>
      <div className="sb-top">
        {!collapsed && <span className="sb-brand">Cookie Planner</span>}
        <button
          className="sb-collapse-btn"
          title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
          onClick={() => actions.setSidebarCollapsed(!collapsed)}
        >{collapsed ? '»' : '«'}</button>
      </div>

      {!collapsed && (
        <div className="sb-section">
          <span>Projects</span>
          <button title="New project" onClick={() => setAddingProject(true)}>+</button>
        </div>
      )}

      {s.projects.map(p => {
        const taskCount = s.tasks.filter(t => t.projectId === p.id).length;
        return (
          <div key={p.id}
            className={'sb-item' + (s.activeProjectId === p.id ? ' active' : '')}
            title={collapsed ? p.name : ''}
            onClick={() => actions.setActiveProject(p.id)}>
            <span className="sb-color" style={{ background: p.color }}></span>
            {!collapsed && <span style={{overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{p.name}</span>}
            {!collapsed && <span className="count">{taskCount}</span>}
          </div>
        );
      })}

      {collapsed && (
        <button className="sb-item sb-add-mini" title="New project" onClick={() => setAddingProject(true)}>+</button>
      )}

      <div style={{flex:1}} />

      {!collapsed && (
        <div className="sb-resize-handle" ref={handleRef} onMouseDown={startResize} />
      )}

      {addingProject && (
        <PromptDialog
          title="New project"
          placeholder="Project name…"
          confirmLabel="Create"
          onConfirm={(name) => { actions.addProject(name); setAddingProject(false); }}
          onCancel={() => setAddingProject(false)}
        />
      )}
    </div>
  );
}

function FilterBar({ project }) {
  const filters = project.filters || [];
  const [picking, setPicking] = React.useState(null);
  const search = project.searchText || '';

  function addFilter(fieldId) {
    const f = fieldOf(project, fieldId);
    const isSelect = f.type === 'select' || f.type === 'multi_select';
    const isDate = f.type === 'date';
    const op = isSelect ? 'eq' : isDate ? 'after' : (f.type === 'checkbox' ? 'eq' : 'contains');
    const value = isSelect && f.options?.[0] ? f.options[0] : (f.type === 'checkbox' ? true : '');
    actions.setFilters(project.id, [...filters, { id: Math.random().toString(36).slice(2,8), fieldId, op, value }]);
    setPicking(null);
  }
  function updateFilter(id, patch) {
    actions.setFilters(project.id, filters.map(f => f.id === id ? { ...f, ...patch } : f));
  }
  function removeFilter(id) {
    actions.setFilters(project.id, filters.filter(f => f.id !== id));
  }

  return (
    <div className="filter-bar">
      <span className="lab">Filter:</span>
      <div className="filter-search">
        <span className="filter-search-icon">⌕</span>
        <input
          type="text" placeholder="Search title or notes…"
          value={search}
          onChange={(e) => actions.setSearchText(project.id, e.target.value)}
        />
        {search && (
          <button className="filter-search-clear" onClick={() => actions.setSearchText(project.id, '')}>✕</button>
        )}
      </div>
      {filters.map(f => {
        const field = fieldOf(project, f.fieldId);
        if (!field) return null;
        return (
          <FilterPill key={f.id} project={project} field={field} filter={f}
            onChange={(patch) => updateFilter(f.id, patch)}
            onRemove={() => removeFilter(f.id)} />
        );
      })}
      <button className="filter-add" onClick={(e) => setPicking(e.currentTarget)}>+ Add filter</button>
      {picking && (
        <Popover anchor={picking} onClose={() => setPicking(null)} align="left">
          <div className="ftp-title">Filter by</div>
          <div className="popover-item-row" onClick={() => addFilter('__epic__')}>
            <span className="field-color-dot" style={{ background: '#9a948a' }} />
            <span>Epic</span>
            <span className="builtin-tag">built-in</span>
          </div>
          <div className="popover-item-row" onClick={() => addFilter('__story__')}>
            <span className="field-color-dot" style={{ background: '#7a9bc4' }} />
            <span>Story</span>
            <span className="builtin-tag">built-in</span>
          </div>
          <div className="sep" />
          {project.fields.map(f => (
            <div key={f.id} className="popover-item-row" onClick={() => addFilter(f.id)}>
              <span className="field-color-dot" style={{ background: f.color || '#9a948a' }} />
              <span>{f.name}</span>
              {f.builtin && <span className="builtin-tag">built-in</span>}
            </div>
          ))}
        </Popover>
      )}
    </div>
  );
}

function FilterPill({ project, field, filter, onChange, onRemove }) {
  const t = field.type;
  const opts = ['select','multi_select','checkbox'].includes(t)
    ? [['eq','is'],['neq','is not'],['is_empty','is empty'],['is_not_empty','is set']]
    : t === 'date'
      ? [['eq','on'],['before','before'],['after','after'],['is_empty','is empty'],['is_not_empty','is set']]
      : [['contains','contains'],['eq','equals'],['neq','not equals'],['is_empty','is empty'],['is_not_empty','is set']];

  const showValue = filter.op !== 'is_empty' && filter.op !== 'is_not_empty';

  return (
    <span className="filter-pill">
      <span className="field-color-dot" style={{ background: field.color || '#8a8780' }} />
      <span className="fp-name">{field.name}</span>
      <select className="fp-op" value={filter.op} onChange={(e) => onChange({ op: e.target.value })}>
        {opts.map(([k,l]) => <option key={k} value={k}>{l}</option>)}
      </select>
      {showValue && (t === 'select' || t === 'multi_select') && (
        <select className="fp-val" value={filter.value || ''} onChange={(e) => onChange({ value: e.target.value })}>
          <option value="">—</option>
          {(field.options || []).map(o => <option key={o} value={o}>{field.optionLabels ? field.optionLabels[o] : o}</option>)}
        </select>
      )}
      {showValue && t === 'date' && (
        <DateEditor value={filter.value} onChange={(v) => onChange({ value: v })} compact />
      )}
      {showValue && t === 'checkbox' && (
        <select className="fp-val" value={String(!!filter.value)} onChange={(e) => onChange({ value: e.target.value === 'true' })}>
          <option value="true">checked</option>
          <option value="false">unchecked</option>
        </select>
      )}
      {showValue && !['select','multi_select','date','checkbox'].includes(t) && (
        <input className="fp-val" type="text" placeholder="value" value={filter.value || ''}
          onChange={(e) => onChange({ value: e.target.value })} />
      )}
      <button className="fp-rm" onClick={onRemove} title="Remove">✕</button>
    </span>
  );
}

function Topbar() {
  const s = useStore();
  const project = s.projects.find(p => p.id === s.activeProjectId);
  const [groupAnchor, setGroupAnchor] = React.useState(null);
  const [tlGroupAnchor, setTlGroupAnchor] = React.useState(null);
  const [zoomAnchor, setZoomAnchor] = React.useState(null);
  const [zoom, setZoom] = React.useContext(TlZoomCtx);
  if (!project) return null;

  const groupField   = project.groupBy === '__epic__' ? { name: 'Epic' } : project.groupBy === '__story__' ? { name: 'Story' } : project.fields.find(f => f.id === project.groupBy);
  const tlGroupField = (project.tlGroupBy || 'none') === '__epic__' ? { name: 'Epic' } : (project.tlGroupBy || 'none') === '__story__' ? { name: 'Story' } : project.fields.find(f => f.id === (project.tlGroupBy || 'none'));


  const showFilter = s.activeView === 'board' || s.activeView === 'list' || s.activeView === 'timeline' || s.activeView === 'calendar';

  return (
    <div className="topbar-wrap">
      <div className="topbar">
        <h1>{project.name}</h1>
        <span className="sub">· {s.tasks.filter(t => t.projectId === project.id).length} tasks</span>
        <div className="spacer" />

        {s.activeView === 'board' && (
          <div className="group-pill">
            <span className="lab">Group:</span>
            <button onClick={e => setGroupAnchor(e.currentTarget)}>
              {project.groupBy === 'none' ? 'None' : (groupField?.name || 'None')} ▾
            </button>
          </div>
        )}
        {s.activeView === 'timeline' && (
          <>
            <div className="group-pill">
              <span className="lab">Group:</span>
              <button onClick={e => setTlGroupAnchor(e.currentTarget)}>
                {(project.tlGroupBy || 'none') === 'none' ? 'None' : (tlGroupField?.name || 'None')} ▾
              </button>
            </div>
            <div className="group-pill">
              <span className="lab">Zoom:</span>
              <button onClick={e => setZoomAnchor(e.currentTarget)}>{zoom} ▾</button>
            </div>
          </>
        )}
        {showFilter && (() => {
          const bothHidden = project.hideEpicCards && project.hideStoryCards;
          return (
            <div className="group-pill">
              <button
                className={bothHidden ? 'on' : ''}
                title="Hide Epic and Story summary cards from this view — their linked tasks stay visible. Still shown on the Epics/Stories tabs, or when grouped by Epic/Story."
                onClick={() => {
                  actions.setHideEpicCards(project.id, !bothHidden);
                  actions.setHideStoryCards(project.id, !bothHidden);
                }}>
                ◆◇ {bothHidden ? 'Epics & Stories hidden' : 'Hide Epics and Stories'}
              </button>
            </div>
          );
        })()}
        {showFilter && (() => {
          const epics = getEpics(project.id);
          const stories = getStories(project.id);
          const parents = [...epics, ...stories];
          const allCollapsed = parents.length > 0 && parents.every(p =>
            p.isEpic ? p.epicChildrenVisible === false : p.storyChildrenVisible === false);
          return (
            <div className="group-pill">
              <button
                className={allCollapsed ? 'on' : ''}
                title={allCollapsed
                  ? 'Show every Epic\'s and Story\'s linked cards on the board again'
                  : 'Hide every Epic\'s and Story\'s linked cards from the board, all at once'}
                onClick={() => actions.setAllEpicStoryChildrenVisible(project.id, allCollapsed)}>
                {allCollapsed ? '▤ Expand Epics and Stories' : '▤ Collapse Epics and Stories'}
              </button>
            </div>
          );
        })()}
        {showFilter && (
          <div className="group-pill">
            <button
              className={project.hideDoneCards ? 'on' : ''}
              title="Hide cards that have been marked done from this view."
              onClick={() => actions.setHideDoneCards(project.id, !project.hideDoneCards)}>
              ☑ {project.hideDoneCards ? 'Done hidden' : 'Hide Done'}
            </button>
          </div>
        )}

        <div className="viewpicker">
          {[
            ['board','Board'],
            ['epics','Epics'],
            ['stories','Stories'],
            ['list','List'],
            ['calendar','Calendar'],
            ['timeline','Timeline'],
            ['settings','Settings'],
          ].map(([k, l]) => (
            <button key={k} className={s.activeView === k ? 'active' : ''} onClick={() => actions.setActiveView(k)}>{l}</button>
          ))}
        </div>

        {groupAnchor && (
          <Popover anchor={groupAnchor} onClose={() => setGroupAnchor(null)}>
            <div className="item" onClick={() => { actions.setGroupBy(project.id, 'none'); setGroupAnchor(null); }}>None (flat board)</div>
            <div className="sep" />
            <div className="item" onClick={() => { actions.setGroupBy(project.id, '__epic__'); setGroupAnchor(null); }}>
              ◆ Epic
            </div>
            <div className="item" onClick={() => { actions.setGroupBy(project.id, '__story__'); setGroupAnchor(null); }}>
              ◇ Story
            </div>
            <div className="sep" />
            {project.fields.map(f => (
              <div key={f.id} className="item" onClick={() => { actions.setGroupBy(project.id, f.id); setGroupAnchor(null); }}>
                {f.name} {f.builtin ? '' : <span style={{fontSize:10, color:'var(--accent)'}}>custom</span>}
              </div>
            ))}
          </Popover>
        )}
        {tlGroupAnchor && (
          <Popover anchor={tlGroupAnchor} onClose={() => setTlGroupAnchor(null)}>
            <div className="item" onClick={() => { actions.setTlGroupBy(project.id, 'none'); setTlGroupAnchor(null); }}>None</div>
            <div className="sep" />
            <div className="item" onClick={() => { actions.setTlGroupBy(project.id, '__epic__'); setTlGroupAnchor(null); }}>
              ◆ Epic
            </div>
            <div className="item" onClick={() => { actions.setTlGroupBy(project.id, '__story__'); setTlGroupAnchor(null); }}>
              ◇ Story
            </div>
            <div className="sep" />
            {project.fields.map(f => (
              <div key={f.id} className="item" onClick={() => { actions.setTlGroupBy(project.id, f.id); setTlGroupAnchor(null); }}>
                {f.name} {f.builtin ? '' : <span style={{fontSize:10, color:'var(--accent)'}}>custom</span>}
              </div>
            ))}
          </Popover>
        )}
        {zoomAnchor && (
          <Popover anchor={zoomAnchor} onClose={() => setZoomAnchor(null)}>
            {['day','week','month','quarter'].map(z => (
              <div key={z} className="item" onClick={() => { setZoom(z); setZoomAnchor(null); }}>{z}</div>
            ))}
          </Popover>
        )}
      </div>
      {showFilter && <FilterBar project={project} />}
    </div>
  );
}

function Screen() {
  const s = useStore();
  const [zoom] = React.useContext(TlZoomCtx);

  switch (s.activeView) {
    case 'board':    return <BoardScreen />;
    case 'epics':    return <EpicsScreen />;
    case 'stories':  return <StoriesScreen />;
    case 'list':     return <ListScreen />;
    case 'calendar': return <CalendarScreen />;
    case 'timeline': return <TimelineScreen zoom={zoom} />;
    case 'settings': return <SettingsScreen />;
    default:         return <BoardScreen />;
  }
}

function App() {
  const tlZoomState = React.useState('week');
  React.useEffect(() => {
    // Global catch-all: keep the drag cursor as "move" everywhere in the app,
    // not just over recognized drop targets, so it never flips to "not-allowed"
    // while dragging a card/column across gaps, headers, or other chrome.
    function onDragOver(e) {
      e.preventDefault();
      if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
    }
    document.addEventListener('dragover', onDragOver);
    return () => document.removeEventListener('dragover', onDragOver);
  }, []);

  return (
    <TlZoomCtx.Provider value={tlZoomState}>
      <div className="app">
        <Sidebar />
        <div className="main">
          <Topbar />
          <div className="body"><Screen /></div>
        </div>
        <SidePanel />
      </div>
    </TlZoomCtx.Provider>
  );
}
