/* global React, Icon, IconBtn, useLiveQuery, useKvRecord, useReceiveFrom, db */
/* global ttFmtDuration, ttFmtClock, ttDayKey, ttStartOfDay, ttStartOfWeek, ttFmtDayLabel, ttFmtWeekLabel */
/* global ttStartTimer, ttStopTimer, ttStopAll, ttResumeTask, ttUpdateRunning, ttDiscardRunning, ttDeleteTask, ttSetTaskArchived */
/* global ttEnsureCycle, ttCycleById, ttArchiveCycle, ttRestoreCycle */
/* global useTtRunning, useTtRunningList, useTtSettings, useTtNow */
/* global ttEnsureProjectsFromTodos, ttEnsureProject, ttProjectById, ttTaskById, ttEntryMs, ttSessionMs, ttTaskMs */
/* global TtProjectDot, TtProjectPicker, TtTagPicker, TtCyclePicker, TtTaskModal, TtCalendarView, TtSummaryView, TtCyclesView */
const { useState, useEffect, useLayoutEffect, useRef, useMemo, useCallback } = React;

// ============================================================
// Time Tracker — Task -> Session model.
// A task owns many sessions; resuming a task adds a session to it
// (never a second task), so a task's total duration spans all of its
// sessions. The tracker list groups sessions by week → day → task.
// ============================================================

const TT_DEFAULT_FILTERS = {
  range: '7d',          // 'today' | 'week' | 'month' | '7d' | 'all' | 'custom'
  from: null, to: null, // custom range (day timestamps)
  projectId: null,
  cycleId: null,
  tag: null,
  q: '',
  showArchived: false,
};

const TT_RANGES = [
  { id: '7d',    label: 'Last 7 days' },
  { id: 'today', label: 'Today' },
  { id: 'week',  label: 'This week' },
  { id: 'month', label: 'This month' },
  { id: 'all',   label: 'All time' },
  { id: 'custom',label: 'Custom…' },
];

function ttRangeBounds(filters, weekStartsMonday) {
  const now = Date.now();
  const today = ttStartOfDay(now);
  switch (filters.range) {
    case 'today':  return [today, today + 86400000];
    case 'week':   { const w = ttStartOfWeek(now, weekStartsMonday); return [w, w + 7 * 86400000]; }
    case 'month':  { const d = new Date(now); d.setDate(1); d.setHours(0,0,0,0); return [d.getTime(), now + 86400000]; }
    case 'all':    return [0, Infinity];
    case 'custom': return [filters.from || 0, (filters.to || today) + 86400000];
    case '7d':
    default:       return [today - 6 * 86400000, today + 86400000];
  }
}

// Show the "timer still running" toast at most once per page load
let ttToastShownThisLoad = false;

// ============================================================
// Main tool
// ============================================================
function TimeTrackerTool() {
  const sessions = useLiveQuery('timeEntries', { sort: ['startedAt', 'desc'] });
  const tasks = useLiveQuery('timeTasks');
  const cycles = useLiveQuery('timeCycles');
  const projects = useLiveQuery('projects');
  const todos = useLiveQuery('todos');
  const runningList = useTtRunningList();
  const [settings] = useTtSettings();
  const now = useTtNow(runningList.length > 0);

  const [view, setView] = useState('tracker'); // 'tracker' | 'calendar' | 'summary' | 'cycles'
  const [filters, setFilters] = useState(TT_DEFAULT_FILTERS);
  const [filtersLoaded, setFiltersLoaded] = useState(false);
  const [modal, setModal] = useState(null); // { task } | { defaults } | null
  const [toast, setToast] = useState(null);
  const descRef = useRef(null);

  const tasksById = useMemo(() => {
    const m = new Map();
    for (const t of tasks || []) m.set(t.id, t);
    return m;
  }, [tasks]);
  const cyclesById = useMemo(() => {
    const m = new Map();
    for (const c of cycles || []) m.set(c.id, c);
    return m;
  }, [cycles]);

  // Most recent non-archived task's config — used to prefill the composer
  const lastUsed = useMemo(() => {
    const t = (tasks || [])
      .filter(t => !t.archived)
      .sort((a, b) => (b.lastActiveAt || 0) - (a.lastActiveAt || 0))[0];
    return t
      ? { projectId: t.projectId || null, cycleId: t.cycleId || null, tags: t.tags || [] }
      : { projectId: null, cycleId: null, tags: [] };
  }, [tasks]);

  // Load persisted view + filters once
  useEffect(() => {
    db.kv.get(TT_VIEW_KEY).then(r => { if (r?.v) setView(r.v); });
    db.kv.get(TT_FILTERS_KEY).then(r => {
      if (r?.v) setFilters({ ...TT_DEFAULT_FILTERS, ...r.v });
      setFiltersLoaded(true);
    });
    ttEnsureProjectsFromTodos();
  }, []);
  const setViewPersist = (v) => { setView(v); db.kv.put({ k: TT_VIEW_KEY, v }); };
  const updateFilters = (patch) => {
    setFilters(f => {
      const next = { ...f, ...patch };
      db.kv.put({ k: TT_FILTERS_KEY, v: next });
      return next;
    });
  };

  // "Timers still running" toast on reopen
  useEffect(() => {
    if (ttToastShownThisLoad || runningList.length === 0) return;
    const oldest = Math.min(...runningList.map(r => r.startedAt));
    if (Date.now() - oldest < 60000) return;
    ttToastShownThisLoad = true;
    const n = runningList.length;
    setToast(n === 1
      ? `Timer still running — started ${relTime(oldest)}`
      : `${n} timers running — oldest started ${relTime(oldest)}`);
    const t = setTimeout(() => setToast(null), 6000);
    return () => clearTimeout(t);
  }, [runningList.length]);

  // Receive from other tools (start-from-todo, ⌘K selection)
  const onReceive = useCallback(async (payload) => {
    if (payload?.startFromTodo) {
      const t = payload.startFromTodo;
      const proj = t.project ? await ttEnsureProject(t.project) : null;
      await ttStartTimer({
        description: t.title || '',
        projectId: proj ? proj.id : null,
        tags: t.tags || [],
        todoId: t.id || null,
      });
    } else if (payload?.selectId) {
      let task = await db.timeTasks.get(payload.selectId);
      if (!task) {
        const s = await db.timeEntries.get(payload.selectId);
        if (s) task = await db.timeTasks.get(s.taskId);
      }
      if (task) setModal({ task });
    }
  }, []);
  useReceiveFrom('timetracker', onReceive);

  // Keyboard shortcuts: ⌘/Ctrl+Shift+S stop all (or focus composer) · ⌘/Ctrl+Shift+T focus composer
  useEffect(() => {
    const onKey = (e) => {
      if (!(e.metaKey || e.ctrlKey) || !e.shiftKey) return;
      const k = e.key.toLowerCase();
      if (k === 's') {
        e.preventDefault();
        if (runningList.length) ttStopAll();
        else descRef.current?.focus();
      } else if (k === 't') {
        e.preventDefault();
        descRef.current?.focus();
      }
    };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [runningList.length]);

  // Known tags across the workspace (todos + tasks)
  const knownTags = useMemo(() => {
    const s = new Set();
    (todos || []).forEach(t => (t.tags || []).forEach(x => s.add(x)));
    (tasks || []).forEach(t => (t.tags || []).forEach(x => s.add(x)));
    return [...s].sort();
  }, [todos, tasks]);

  // Filtered sessions (tracker list) — predicates join to the session's task
  const filtered = useMemo(() => {
    if (!sessions) return [];
    const [lo, hi] = ttRangeBounds(filters, settings.weekStartsMonday);
    const q = filters.q.trim().toLowerCase();
    return sessions.filter(s => {
      if (s.startedAt < lo || s.startedAt >= hi) return false;
      const task = tasksById.get(s.taskId);
      if (task && task.archived && !filters.showArchived) return false;
      const projId = task ? (task.projectId || null) : null;
      if (filters.projectId === '__none__') { if (projId) return false; }
      else if (filters.projectId && projId !== filters.projectId) return false;
      const cycId = task ? (task.cycleId || null) : null;
      if (filters.cycleId === '__none__') { if (cycId) return false; }
      else if (filters.cycleId && cycId !== filters.cycleId) return false;
      if (filters.tag && !((task && task.tags) || []).includes(filters.tag)) return false;
      if (q && !((task && task.description) || '').toLowerCase().includes(q)) return false;
      return true;
    });
  }, [sessions, tasksById, filters, settings.weekStartsMonday]);

  // Headline stats — today + this week, running timers included
  const stats = useMemo(() => {
    const today = ttStartOfDay(Date.now());
    const week = ttStartOfWeek(Date.now(), settings.weekStartsMonday);
    let t = 0, w = 0;
    for (const s of sessions || []) {
      if (s.startedAt >= today) t += ttSessionMs(s);
      if (s.startedAt >= week) w += ttSessionMs(s);
    }
    for (const r of runningList) {
      const ms = Date.now() - r.startedAt;
      if (r.startedAt >= today) t += ms;
      if (r.startedAt >= week) w += ms;
    }
    return { today: t, week: w };
  }, [sessions, runningList, now, settings.weekStartsMonday]);

  const hasAny = (sessions || []).length > 0 || runningList.length > 0;
  const filterActive = filters.projectId || filters.cycleId || filters.tag || filters.q.trim() || filters.range !== '7d' || filters.showArchived;

  return (
    <div className="tool trk-tool">
      <TtTimerBar
        runningList={runningList}
        now={now}
        projects={projects || []}
        cycles={cycles || []}
        cyclesById={cyclesById}
        knownTags={knownTags}
        lastUsed={lastUsed}
        descRef={descRef}
        onOpenTask={async (taskId) => { const t = await db.timeTasks.get(taskId); if (t) setModal({ task: t }); }}
      />

      <div className="trk-head">
        <div className="trk-views">
          {[
            { id: 'tracker',  label: 'Tracker',  icon: 'view_agenda' },
            { id: 'calendar', label: 'Calendar', icon: 'calendar_view_week' },
            { id: 'summary',  label: 'Summary',  icon: 'bar_chart' },
            { id: 'cycles',   label: 'Cycles',   icon: 'cycle' },
          ].map(v => (
            <button
              key={v.id}
              className={`trk-view-tab ${view === v.id ? 'is-active' : ''}`}
              onClick={() => setViewPersist(v.id)}
            >
              <Icon name={v.icon}></Icon>
              <span>{v.label}</span>
            </button>
          ))}
        </div>
        <div className="trk-head-stats">
          <div className="trk-head-stat">
            <span className="l">Today</span>
            <span className="v">{ttFmtDuration(stats.today, { seconds: false })}</span>
          </div>
          <div className="trk-head-stat">
            <span className="l">This week</span>
            <span className="v">{ttFmtDuration(stats.week, { seconds: false })}</span>
          </div>
        </div>
        <div className="trk-head-actions">
          <span className="trk-kbd-hint" title="Stop all: ⌘⇧S · Focus composer: ⌘⇧T">
            <kbd>⌘⇧S</kbd>
          </span>
          <button className="lh-btn ghost" onClick={() => setModal({ defaults: {} })}>
            <Icon name="add"></Icon>Add entry
          </button>
        </div>
      </div>

      {view === 'tracker' && (
        <>
          {hasAny && (
            <TtFilterBar
              filters={filters}
              onChange={updateFilters}
              projects={projects || []}
              cycles={cycles || []}
              knownTags={knownTags}
            />
          )}
          {!hasAny && filtersLoaded ? (
            <div className="lh-empty trk-empty">
              <Icon name="timelapse"></Icon>
              <div className="title">Nothing tracked yet</div>
              <div className="sub">Time you track shows up here, grouped by day and by task.</div>
              <button className="lh-btn primary" onClick={() => { descRef.current?.focus(); }}>
                <Icon name="play_arrow"></Icon>Start your first timer
              </button>
            </div>
          ) : (
            <TtTaskList
              sessions={filtered}
              tasksById={tasksById}
              projects={projects || []}
              cyclesById={cyclesById}
              settings={settings}
              filterActive={filterActive}
              onResume={(task) => ttResumeTask(task.id)}
              onOpenTask={(task) => setModal({ task })}
              onArchive={(task) => ttSetTaskArchived(task.id, !task.archived)}
              onDeleteSession={async (s, task) => {
                const ok = await window.lhDialog.confirm({
                  title: 'Delete this session?',
                  message: `${ttFmtClock(s.startedAt)} – ${ttFmtClock(s.stoppedAt)} (${ttFmtDuration(ttSessionMs(s))}) from “${task?.description || '(no description)'}” will be removed.`,
                  confirmLabel: 'Delete', danger: true, icon: 'delete',
                });
                if (ok) db.timeEntries.delete(s.id);
              }}
            />
          )}
        </>
      )}

      {view === 'calendar' && (
        <TtCalendarView
          sessions={sessions || []}
          tasksById={tasksById}
          projects={projects || []}
          runningList={runningList}
          settings={settings}
          onEditTask={(task) => setModal({ task })}
          onCreate={(defaults) => setModal({ defaults })}
        />
      )}

      {view === 'summary' && (
        <TtSummaryView
          sessions={sessions || []}
          tasksById={tasksById}
          cyclesById={cyclesById}
          projects={projects || []}
          settings={settings}
        />
      )}

      {view === 'cycles' && (
        <TtCyclesView
          cycles={cycles || []}
          tasks={tasks || []}
          sessions={sessions || []}
          settings={settings}
          onViewTasks={(cycleId) => {
            setViewPersist('tracker');
            updateFilters({ cycleId, showArchived: true, range: 'all' });
          }}
        />
      )}

      {modal && (
        <TtTaskModal
          task={modal.task || null}
          defaults={modal.defaults || {}}
          projects={projects || []}
          cycles={cycles || []}
          todos={todos || []}
          knownTags={knownTags}
          runningList={runningList}
          onClose={() => setModal(null)}
        />
      )}

      {toast && (
        <div className="trk-toast" data-screen-label="Time tracker toast">
          <span className="trk-widget-pulse"></span>
          <span>{toast}</span>
          <button className="lh-iconbtn" onClick={() => setToast(null)} title="Dismiss">
            <Icon name="close"></Icon>
          </button>
        </div>
      )}
    </div>
  );
}

// ============================================================
// Timer bar — a "start new" composer plus a stack of concurrent running
// timers. Multiple tasks can run at once; each running row has its own
// live elapsed and Stop. Click a running row to open its task.
// ============================================================
function TtTimerBar({ runningList, now, projects, cycles, cyclesById, knownTags, lastUsed, descRef, onOpenTask }) {
  const [desc, setDesc] = useState('');
  const [draftProject, setDraftProject] = useState(null);
  const [draftCycle, setDraftCycle] = useState(null);
  const [draftTags, setDraftTags] = useState([]);
  // While the user hasn't touched the pickers, mirror the most recent task's
  // project/cycle/tags so starting a similar task needs no reconfiguring.
  const [edited, setEdited] = useState(false);
  const lu = lastUsed || { projectId: null, cycleId: null, tags: [] };
  const luKey = `${lu.projectId}|${lu.cycleId}|${(lu.tags || []).join(',')}`;

  useEffect(() => {
    if (edited) return;
    setDraftProject(lu.projectId || null);
    setDraftCycle(lu.cycleId || null);
    setDraftTags(lu.tags || []);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [luKey, edited]);

  const setProject = (id) => { setEdited(true); setDraftProject(id); };
  const setCycle = (id) => { setEdited(true); setDraftCycle(id); };
  const setTags = (t) => { setEdited(true); setDraftTags(t); };
  const clearConfig = () => { setEdited(true); setDraftProject(null); setDraftCycle(null); setDraftTags([]); };

  const start = () => {
    ttStartTimer({ description: desc.trim(), projectId: draftProject, tags: draftTags, cycleId: draftCycle });
    setDesc('');
    setEdited(false); // re-prefill from the new latest task
    descRef.current?.focus();
  };

  const hasConfig = !!(draftProject || draftCycle || (draftTags && draftTags.length));
  const sorted = [...runningList].sort((a, b) => a.startedAt - b.startedAt);
  const anyRunning = runningList.length > 0;

  return (
    <div className={`trk-timerbar ${anyRunning ? 'is-running' : ''}`} data-screen-label="Timer bar">
      <div className="trk-composer">
        <input
          ref={descRef}
          className="trk-composer-desc"
          placeholder={anyRunning ? 'Start another task…' : 'What are you working on?'}
          value={desc}
          onChange={e => setDesc(e.target.value)}
          onKeyDown={e => { if (e.key === 'Enter') start(); }}
          maxLength={200}
        />
        <TtProjectPicker projects={projects} value={draftProject} onChange={setProject} compact={true}></TtProjectPicker>
        <TtCyclePicker cycles={cycles} value={draftCycle} onChange={setCycle} compact={true}></TtCyclePicker>
        <TtTagPicker value={draftTags} onChange={setTags} knownTags={knownTags} compact={true}></TtTagPicker>
        {hasConfig && (
          <button className="trk-composer-clear" onClick={clearConfig} title="Clear project, cycle & tags">
            <Icon name="close"></Icon>
          </button>
        )}
        <button className="trk-timerbar-btn is-start" onClick={start} title="Start a timer (⌘⇧S)">
          <Icon name="play_arrow"></Icon>
          <span>Start</span>
        </button>
      </div>

      {anyRunning && (
        <div className="trk-running-stack">
          {sorted.map(r => {
            const proj = ttProjectById(projects, r.projectId);
            const cyc = (cyclesById && r.cycleId) ? cyclesById.get(r.cycleId) : null;
            return (
              <div className="trk-running-row" key={r.id}>
                <span className="trk-running-pulse"></span>
                <button
                  className="trk-running-main"
                  onClick={() => r.taskId && onOpenTask(r.taskId)}
                  title="Open task"
                >
                  <span className="trk-running-desc">
                    {r.description || <em className="trk-row-nodesc">(no description)</em>}
                  </span>
                  <span className="trk-running-meta">
                    {cyc && (
                      <span className="trk-row-cycle" style={{ '--trk-cycle-color': cyc.color || 'var(--text-faint)' }}>
                        <Icon name="cycle"></Icon>{cyc.name}
                      </span>
                    )}
                    {r.projectId && (
                      <span className="trk-row-proj">
                        <TtProjectDot project={proj}></TtProjectDot>
                        {proj ? proj.name : '(deleted project)'}
                      </span>
                    )}
                    {(r.tags || []).map(t => <span className="trk-row-tag" key={t}>#{t}</span>)}
                  </span>
                </button>
                <span className="trk-running-elapsed">{ttFmtDuration(now - r.startedAt)}</span>
                <button className="trk-running-stop" onClick={() => ttStopTimer(r.id)} title="Stop this timer">
                  <Icon name="stop"></Icon>
                  <span>Stop</span>
                </button>
                <IconBtn
                  name="close"
                  tone="danger"
                  title="Discard without saving"
                  onClick={async () => {
                    const ok = await window.lhDialog.confirm({
                      title: 'Discard timer?',
                      message: `“${r.description || '(no description)'}” will be thrown away without saving a session.`,
                      confirmLabel: 'Discard', danger: true, icon: 'delete',
                    });
                    if (ok) ttDiscardRunning(r.id);
                  }}
                ></IconBtn>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ============================================================
// Filter bar
// ============================================================
function TtFilterBar({ filters, onChange, projects, cycles, knownTags }) {
  return (
    <div className="trk-filterbar">
      <div className="trk-filter-search">
        <Icon name="search"></Icon>
        <input
          placeholder="Search tasks…"
          value={filters.q}
          onChange={e => onChange({ q: e.target.value })}
        />
      </div>
      <select
        className="lh-input trk-filter-select"
        value={filters.range}
        onChange={e => onChange({ range: e.target.value })}
      >
        {TT_RANGES.map(r => <option key={r.id} value={r.id}>{r.label}</option>)}
      </select>
      {filters.range === 'custom' && (
        <>
          <input
            type="date" className="lh-input trk-filter-date"
            value={filters.from ? ttDateInputVal(filters.from) : ''}
            onChange={e => onChange({ from: e.target.value ? ttParseDateTime(e.target.value, '00:00') : null })}
          />
          <span className="trk-filter-dash">–</span>
          <input
            type="date" className="lh-input trk-filter-date"
            value={filters.to ? ttDateInputVal(filters.to) : ''}
            onChange={e => onChange({ to: e.target.value ? ttParseDateTime(e.target.value, '00:00') : null })}
          />
        </>
      )}
      <select
        className="lh-input trk-filter-select"
        value={filters.projectId || ''}
        onChange={e => onChange({ projectId: e.target.value || null })}
      >
        <option value="">All projects</option>
        <option value="__none__">No project</option>
        {projects.filter(p => !p.archived).map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
      </select>
      <select
        className="lh-input trk-filter-select"
        value={filters.cycleId || ''}
        onChange={e => onChange({ cycleId: e.target.value || null })}
      >
        <option value="">All cycles</option>
        <option value="__none__">No cycle</option>
        {(cycles || []).filter(c => !c.archived).map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
      </select>
      <select
        className="lh-input trk-filter-select"
        value={filters.tag || ''}
        onChange={e => onChange({ tag: e.target.value || null })}
      >
        <option value="">All tags</option>
        {knownTags.map(t => <option key={t} value={t}>#{t}</option>)}
      </select>
      <label className="trk-archive-toggle" title="Include archived tasks">
        <input type="checkbox" checked={!!filters.showArchived} onChange={e => onChange({ showArchived: e.target.checked })} />
        <span>Archived</span>
      </label>
      {(filters.projectId || filters.cycleId || filters.tag || filters.q || filters.range !== '7d' || filters.showArchived) && (
        <button className="lh-chip" onClick={() => onChange({ ...TT_DEFAULT_FILTERS })}>
          <Icon name="filter_alt_off"></Icon>Reset
        </button>
      )}
    </div>
  );
}

// ============================================================
// Row actions menu — rendered fixed-position (anchored to the trigger)
// so it is never clipped by the scrolling task list.
// ============================================================
function TtRowMenu({ rect, items, onClose }) {
  const ref = useRef(null);
  const [pos, setPos] = useState({ top: -9999, left: -9999, ready: false });

  useLayoutEffect(() => {
    const el = ref.current;
    if (!el || !rect) return;
    const h = el.offsetHeight, w = el.offsetWidth;
    let top = rect.bottom + 4;
    if (top + h > window.innerHeight - 8) top = Math.max(8, rect.top - h - 4);
    let left = rect.right - w;
    if (left < 8) left = 8;
    setPos({ top, left, ready: true });
  }, [rect]);

  useEffect(() => {
    const close = () => onClose();
    document.addEventListener('click', close);
    document.addEventListener('scroll', close, true);
    window.addEventListener('resize', close);
    return () => {
      document.removeEventListener('click', close);
      document.removeEventListener('scroll', close, true);
      window.removeEventListener('resize', close);
    };
  }, [onClose]);

  return (
    <div
      ref={ref}
      className="trk-row-menu is-fixed"
      style={{ top: pos.top, left: pos.left, visibility: pos.ready ? 'visible' : 'hidden' }}
      onClick={(e) => e.stopPropagation()}
    >
      {items.map((it, i) => (
        <button key={i} className={it.danger ? 'is-danger' : ''} onClick={() => { onClose(); it.onClick(); }}>
          <Icon name={it.icon}></Icon>{it.label}
        </button>
      ))}
    </div>
  );
}

// ============================================================
// Task list — ONE row per task (within the active filter range). Each
// row shows the task's total across all of its sessions, a session
// count, and expands to the individual sessions with their dates.
// Click a row to open the full task detail. Sorted by most recent activity.
// ============================================================
function TtTaskList({ sessions, tasksById, projects, cyclesById, settings, filterActive, onResume, onOpenTask, onArchive, onDeleteSession }) {
  const [menu, setMenu] = useState(null);   // { key, rect }
  const [expanded, setExpanded] = useState(() => new Set());

  const toggleExpand = (key) => {
    setExpanded(prev => {
      const next = new Set(prev);
      if (next.has(key)) next.delete(key); else next.add(key);
      return next;
    });
  };

  const groups = useMemo(() => {
    // sessions already sorted desc by startedAt
    const map = new Map();
    for (const s of sessions) {
      if (!map.has(s.taskId)) {
        map.set(s.taskId, { taskId: s.taskId, sessions: [], total: 0, lastStop: 0, firstStart: Infinity });
      }
      const g = map.get(s.taskId);
      g.sessions.push(s);
      g.total += ttSessionMs(s);
      g.lastStop = Math.max(g.lastStop, s.stoppedAt || Date.now());
      g.firstStart = Math.min(g.firstStart, s.startedAt);
    }
    return [...map.values()].sort((a, b) => b.lastStop - a.lastStop);
  }, [sessions]);

  const total = useMemo(() => groups.reduce((a, g) => a + g.total, 0), [groups]);

  if (sessions.length === 0) {
    return (
      <div className="lh-empty trk-empty">
        <Icon name="search_off"></Icon>
        <div className="title">No sessions in this view</div>
        <div className="sub">{filterActive ? 'Try widening the date range or clearing filters.' : 'Sessions you track will appear here.'}</div>
      </div>
    );
  }

  const secs = settings.listSeconds;
  const dayDelta = (a, b) => Math.max(0, Math.round((ttStartOfDay(b) - ttStartOfDay(a)) / 86400000));

  return (
    <div className="trk-list" data-screen-label="Task list">
      <div className="trk-list-summary">
        <span className="trk-list-summary-count">{groups.length} {groups.length === 1 ? 'task' : 'tasks'}</span>
        <span className="trk-list-summary-total">{ttFmtDuration(total, { seconds: secs })}</span>
      </div>

      {groups.map(g => {
        const task = tasksById.get(g.taskId);
        const proj = ttProjectById(projects, task && task.projectId);
        const cycle = (cyclesById && task && task.cycleId) ? cyclesById.get(task.cycleId) : null;
        const archived = !!(task && task.archived);
        const multi = g.sessions.length > 1;
        const isOpen = expanded.has(g.taskId);
        const single = g.sessions[0];
        return (
          <div className={`trk-taskcard ${isOpen ? 'is-open' : ''} ${archived ? 'is-archived' : ''}`} key={g.taskId}>
            <div
              className="trk-row"
              onClick={() => task && onOpenTask(task)}
              role="button"
              tabIndex={0}
              onKeyDown={(ev) => { if (ev.key === 'Enter' && task) onOpenTask(task); }}
            >
              <button
                className="trk-row-play"
                title="Resume — add a session to this task"
                onClick={(ev) => { ev.stopPropagation(); if (task) onResume(task); }}
              >
                <Icon name="play_arrow"></Icon>
              </button>
              <div className="trk-row-main">
                <div className="trk-row-desc">
                  {task && task.description
                    ? task.description
                    : <em className="trk-row-nodesc">(no description)</em>}
                  {archived && <span className="trk-archived-badge">Archived</span>}
                  {task && task.todoId && <Icon name="check_box" className="trk-row-todo" title="Linked to a todo"></Icon>}
                </div>
                <div className="trk-row-meta">
                  {cycle && (
                    <span className="trk-row-cycle" style={{ '--trk-cycle-color': cycle.color || 'var(--text-faint)' }}>
                      <Icon name="cycle"></Icon>{cycle.name}
                    </span>
                  )}
                  {task && task.projectId && (
                    <span className="trk-row-proj">
                      <TtProjectDot project={proj}></TtProjectDot>
                      {proj ? proj.name : '(deleted project)'}
                    </span>
                  )}
                  {((task && task.tags) || []).map(t => <span className="trk-row-tag" key={t}>#{t}</span>)}
                  <span className="trk-row-when">{ttFmtDayLabel(g.lastStop)}</span>
                </div>
              </div>
              {multi ? (
                <button
                  className={`trk-row-count ${isOpen ? 'is-open' : ''}`}
                  title={isOpen ? 'Hide sessions' : 'Show sessions'}
                  onClick={(ev) => { ev.stopPropagation(); toggleExpand(g.taskId); }}
                >
                  <Icon name="layers"></Icon>
                  {g.sessions.length}
                  <Icon name="expand_more" className="trk-row-count-caret"></Icon>
                </button>
              ) : (
                <div className="trk-row-times">
                  {ttFmtClock(single.startedAt)} – {ttFmtClock(single.stoppedAt)}
                  {dayDelta(single.startedAt, single.stoppedAt) > 0 && (
                    <span className="trk-day-cross">+{dayDelta(single.startedAt, single.stoppedAt)}d</span>
                  )}
                </div>
              )}
              <div className="trk-row-dur" title="Total across all sessions in view">{ttFmtDuration(g.total, { seconds: secs })}</div>
              <div className="trk-row-menu-wrap" onClick={(ev) => ev.stopPropagation()}>
                <IconBtn
                  name="more_vert"
                  title="Task actions"
                  onClick={(ev) => {
                    const rect = ev.currentTarget.getBoundingClientRect();
                    setMenu(menu && menu.key === g.taskId ? null : { key: g.taskId, rect });
                  }}
                ></IconBtn>
                {menu && menu.key === g.taskId && (
                  <TtRowMenu
                    rect={menu.rect}
                    onClose={() => setMenu(null)}
                    items={[
                      { icon: 'play_arrow', label: 'Resume', onClick: () => task && onResume(task) },
                      ...(multi ? [{ icon: 'layers', label: isOpen ? 'Hide sessions' : 'Show sessions', onClick: () => toggleExpand(g.taskId) }] : []),
                      { icon: 'open_in_full', label: 'Open task', onClick: () => task && onOpenTask(task) },
                      { icon: archived ? 'unarchive' : 'inventory_2', label: archived ? 'Restore' : 'Archive', onClick: () => task && onArchive(task) },
                    ]}
                  ></TtRowMenu>
                )}
              </div>
            </div>

            {isOpen && (
              <div className="trk-sessions">
                {g.sessions.map(s => (
                  <div className="trk-session" key={s.id} onClick={() => task && onOpenTask(task)}>
                    <span className="trk-session-dot"></span>
                    <span className="trk-session-day">{ttFmtDayLabel(s.startedAt)}</span>
                    <span className="trk-session-times">
                      {ttFmtClock(s.startedAt)} – {ttFmtClock(s.stoppedAt)}
                      {dayDelta(s.startedAt, s.stoppedAt) > 0 && (
                        <span className="trk-day-cross">+{dayDelta(s.startedAt, s.stoppedAt)}d</span>
                      )}
                    </span>
                    <span className="trk-session-dur">{ttFmtDuration(ttSessionMs(s), { seconds: secs })}</span>
                    <IconBtn
                      name="delete"
                      title="Delete session"
                      onClick={(ev) => { ev.stopPropagation(); onDeleteSession(s, task); }}
                    ></IconBtn>
                  </div>
                ))}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

window.TimeTrackerTool = TimeTrackerTool;

// ============================================================
// Per-tool settings panel (Settings → Tools → Time Tracker)
// ============================================================
function TimeTrackerSettings() {
  const [settings, update] = useTtSettings();
  return (
    <div className="trk-settings">
      <label className="trk-settings-row">
        <input
          type="checkbox"
          checked={settings.weekStartsMonday}
          onChange={e => update({ weekStartsMonday: e.target.checked })}
        />
        <div>
          <div className="trk-settings-label">Weeks start on Monday</div>
          <div className="trk-settings-sub">Affects week grouping, weekly totals, and the calendar grid.</div>
        </div>
      </label>
      <label className="trk-settings-row">
        <input
          type="checkbox"
          checked={settings.listSeconds}
          onChange={e => update({ listSeconds: e.target.checked })}
        />
        <div>
          <div className="trk-settings-label">Show seconds in lists</div>
          <div className="trk-settings-sub">Durations in the task list and summaries include seconds (1:23:45 instead of 1h 23m).</div>
        </div>
      </label>
    </div>
  );
}
window.toolSettingsRegistry = window.toolSettingsRegistry || {};
window.toolSettingsRegistry.timetracker = TimeTrackerSettings;
