// widgets/tasks.jsx — D1-backed CRUD via /api/tasks
//
// Same UI as before; the storage layer moved from localStorage to D1
// behind a Pages Function. Optimistic updates: toggle/add/remove patch
// local state immediately, then fire the network call. On failure we
// reload from server so state never lies.
//
// One-time migration: on first mount, if D1 is empty AND localStorage
// has a tasks list under config.storageKey (default "tasks"), POST each
// task to /api/tasks then clear the localStorage key.
//
// Config (in layout.yml):
//   - type: tasks
//     # no config needed; D1 is per-user via CF Access email

const LEGACY_KEY = 'tasks';

async function migrateLegacyLocalStorage(legacyKey) {
  let local;
  try { local = JSON.parse(localStorage.getItem(legacyKey) || '[]'); } catch { local = []; }
  if (!Array.isArray(local) || local.length === 0) return 0;
  // Don't double-migrate: only seed D1 if it's currently empty for this user.
  const r = await fetch('/api/tasks');
  if (!r.ok) return 0;
  const existing = await r.json();
  if (existing.length > 0) {
    localStorage.removeItem(legacyKey);
    return 0;
  }
  for (const t of local) {
    await fetch('/api/tasks', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ text: t.text, tag: t.tag, kind: t.kind, done: !!t.done }),
    }).catch(() => {});
  }
  localStorage.removeItem(legacyKey);
  return local.length;
}

function TasksWidget({ config }) {
  const size = useWidgetSize();
  const [tasks, reload] = useTasksList(true);
  const [draft, setDraft] = useState('');
  const inputRef = React.useRef(null);

  // One-time migration on first mount.
  React.useEffect(() => {
    const legacyKey = config?.storageKey || LEGACY_KEY;
    migrateLegacyLocalStorage(legacyKey).then(n => {
      if (n > 0) {
        console.log(`tasks: migrated ${n} legacy localStorage tasks to D1`);
        window.dispatchEvent(new CustomEvent('tasks-updated'));
      }
    });
    // Run only once. The mounted hook captures config by reference;
    // since we only read storageKey, this is safe.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Sidebar's Quick Capture (and pressing N) focuses the task input.
  React.useEffect(() => {
    const focus = () => inputRef.current?.focus();
    const onKey = (e) => {
      const tag = (e.target?.tagName || '').toLowerCase();
      if (tag === 'input' || tag === 'textarea') return;
      if (e.key === 'n' || e.key === 'N') { e.preventDefault(); focus(); }
    };
    window.addEventListener('focus-task-input', focus);
    window.addEventListener('keydown', onKey);
    return () => {
      window.removeEventListener('focus-task-input', focus);
      window.removeEventListener('keydown', onKey);
    };
  }, []);

  // Optimistic mutators. Each one updates local state immediately, fires
  // the network call, then reloads from server to reconcile (resync ids
  // for adds, drop the optimistic row on failure, etc.).
  const toggle = async (id) => {
    const t = tasks?.find(x => x.id === id);
    if (!t) return;
    window.dispatchEvent(new CustomEvent('tasks-updated', {
      detail: { optimistic: { id, done: !t.done } },
    }));
    try {
      await fetch(`/api/tasks/${id}`, {
        method: 'PATCH',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ done: !t.done }),
      });
    } finally {
      reload();
    }
  };

  const remove = async (id) => {
    try {
      await fetch(`/api/tasks/${id}`, { method: 'DELETE' });
    } finally {
      reload();
      window.dispatchEvent(new CustomEvent('tasks-updated'));
    }
  };

  const add = async () => {
    const text = draft.trim();
    if (!text) return;
    setDraft('');
    try {
      await fetch('/api/tasks', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ text, tag: 'Personal', kind: 'personal' }),
      });
    } finally {
      reload();
      window.dispatchEvent(new CustomEvent('tasks-updated'));
    }
  };

  const clearDone = async () => {
    const done = (tasks || []).filter(t => t.done);
    await Promise.allSettled(
      done.map(t => fetch(`/api/tasks/${t.id}`, { method: 'DELETE' }))
    );
    reload();
    window.dispatchEvent(new CustomEvent('tasks-updated'));
  };

  const list = tasks ?? [];
  const loading = tasks === null;

  if (size === 'compact') {
    const open = list.filter(t => !t.done);
    const next = open.slice(0, 3);
    return (
      <Panel title="Tasks" action={<span className="muted" style={{fontSize: 11}}>{open.length} open</span>}>
        <div className="task-list" style={{padding: '4px 0'}}>
          {next.map((t) => (
            <div key={t.id} className="task" style={{padding: '6px 4px'}}>
              <div className="task-check" onClick={() => toggle(t.id)}></div>
              <div className="task-text" style={{fontSize: 12.5}}>{t.text}</div>
              <div className={`task-tag ${t.kind || 'personal'}`}>{t.tag || ''}</div>
            </div>
          ))}
          {!loading && open.length === 0 && <div className="muted" style={{padding: 8}}>All clear ✓</div>}
          {loading && <div className="muted" style={{padding: 8}}>Loading…</div>}
        </div>
      </Panel>
    );
  }

  const action = (
    <button className="panel-action" onClick={clearDone}>Clear done</button>
  );

  return (
    <Panel title="Tasks" action={action}>
      <div className="task-list">
        {list.map((t) => (
          <div key={t.id} className={`task ${t.done ? 'done' : ''}`}>
            <div className={`task-check ${t.done ? 'done' : ''}`} onClick={() => toggle(t.id)}></div>
            <div className="task-text" onDoubleClick={() => remove(t.id)}>{t.text}</div>
            <div className={`task-tag ${t.kind || 'personal'}`}>{t.tag || ''}</div>
          </div>
        ))}
        {loading && <div className="muted" style={{padding: 12}}>Loading…</div>}
        {!loading && list.length === 0 && <div className="muted" style={{padding: 12}}>No tasks yet — add one below.</div>}
      </div>
      <div className="task-add">
        <span className="plus" onClick={add} style={{cursor: 'pointer'}}>+</span>
        <input
          ref={inputRef}
          type="text"
          placeholder="Add task (Enter)"
          value={draft}
          onChange={(e) => setDraft(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === 'Enter') add();
            else if (e.key === 'Escape') e.target.blur();
          }}
          style={{
            background: 'transparent', border: 'none', outline: 'none',
            flex: 1, font: 'inherit', color: 'inherit'
          }}
        />
      </div>
    </Panel>
  );
}

registerWidget('tasks', TasksWidget);
