// API-backed store — replaces the localStorage mock from the design.
// Talks to Express JSON routes. Mutations return fresh state; we replace in one shot.

const API_OPTS = { credentials: 'same-origin', headers: { 'Content-Type': 'application/json' } };

async function apiGet(path) {
  const r = await fetch(path, { credentials: 'same-origin' });
  if (r.status === 401) { window.location.href = '/login'; return null; }
  if (!r.ok) throw new Error(`GET ${path} ${r.status}`);
  return r.json();
}

async function apiSend(method, path, body) {
  const r = await fetch(path, {
    method,
    ...API_OPTS,
    body: body !== undefined ? JSON.stringify(body) : undefined,
  });
  if (r.status === 401) { window.location.href = '/login'; return null; }
  if (!r.ok) throw new Error(`${method} ${path} ${r.status}`);
  return r.json();
}

function useDashboardStore() {
  const [tasks, setTasks] = React.useState([]);
  const [updates, setUpdates] = React.useState([]);
  const [me, setMe] = React.useState('');
  const [loaded, setLoaded] = React.useState(false);

  const [categories, setCategories] = React.useState([]);

  const applyCategories = (cats) => {
    if (!Array.isArray(cats)) return;
    setCategories(cats);
    window.MOCK.CATEGORIES.splice(0, window.MOCK.CATEGORIES.length, ...cats.map((c) => c.name));
    if (window.A_CAT_COLOR) {
      for (const c of cats) window.A_CAT_COLOR[c.name] = c.color;
    }
  };

  React.useEffect(() => {
    (async () => {
      const me = await apiGet('/api/me');
      if (!me) return;
      const state = await apiGet('/api/state');
      if (!state) return;
      setMe(me.name);
      applyCategories(me.categories);
      setTasks(state.tasks);
      setUpdates(state.updates);
      setLoaded(true);
    })().catch((e) => console.error('load failed', e));
  }, []);

  const applyState = (s) => {
    if (!s) return;
    if (s.tasks) setTasks(s.tasks);
    if (s.updates) setUpdates(s.updates);
  };

  const addTask = async (t) => {
    const res = await apiSend('POST', '/api/tasks', t);
    applyState(res);
    return res && res.task;
  };

  const updateTask = async (id, patch) => {
    const res = await apiSend('PATCH', `/api/tasks/${id}`, patch);
    applyState(res);
  };

  const deleteTask = async (id) => {
    const res = await apiSend('DELETE', `/api/tasks/${id}`);
    applyState(res);
  };

  const addUpdate = async (taskId, body) => {
    const res = await apiSend('POST', `/api/tasks/${taskId}/updates`, { body });
    applyState(res);
  };

  const toggleReaction = async (updateId, emoji) => {
    const res = await apiSend('POST', `/api/updates/${updateId}/reactions`, { emoji });
    applyState(res);
  };

  const refresh = async () => {
    const state = await apiGet('/api/state');
    applyState(state);
  };

  const addCategory = async (name, color) => {
    const res = await apiSend('POST', '/api/categories', { name, color });
    if (res && res.categories) applyCategories(res.categories);
    return res;
  };

  const removeCategory = async (name) => {
    const res = await apiSend('DELETE', `/api/categories/${encodeURIComponent(name)}`);
    if (res && res.categories) applyCategories(res.categories);
    if (res && res.tasks) setTasks(res.tasks);
  };

  return { tasks, updates, categories, me, setMe: () => {}, loaded, addTask, updateTask, deleteTask, addUpdate, toggleReaction, refresh, addCategory, removeCategory };
}

// ─── helpers from the original design (still pure client-side) ───

function parseQuickAdd(raw) {
  let s = raw.trim();
  const out = { title: '', status: 'To Do', priority: 'Medium', owner: 'Both', category: 'Other', due_date: null, next_step: '' };

  const pri = s.match(/^(high|med|medium|low)[:!\s]/i);
  if (pri) {
    out.priority = pri[1].toLowerCase().startsWith('h') ? 'High' : pri[1].toLowerCase().startsWith('l') ? 'Low' : 'Medium';
    s = s.slice(pri[0].length).trim();
  }

  const own = s.match(/^(phil|bob|both)[:\s]/i);
  if (own) {
    out.owner = own[1][0].toUpperCase() + own[1].slice(1).toLowerCase();
    s = s.slice(own[0].length).trim();
  }

  const cat = s.match(/#(acquisition|bic|mhc|operations|marketing|other)\b/i);
  if (cat) {
    out.category = cat[1][0].toUpperCase() + cat[1].slice(1).toLowerCase();
    s = (s.slice(0, cat.index) + s.slice(cat.index + cat[0].length)).replace(/\s+/g, ' ').trim();
  }

  const today = new Date();
  const addDays = (n) => { const t = new Date(today); t.setDate(t.getDate() + n); return t.toISOString().slice(0, 10); };
  const dayMap = { sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6 };
  const dateRe = /\b(today|tomorrow|tmrw|mon|tue|wed|thu|fri|sat|sun|monday|tuesday|wednesday|thursday|friday|saturday|sunday|next week|eow|eom)\b/i;
  const dm = s.match(dateRe);
  if (dm) {
    const word = dm[1].toLowerCase();
    if (word === 'today') out.due_date = addDays(0);
    else if (word === 'tomorrow' || word === 'tmrw') out.due_date = addDays(1);
    else if (word === 'next week') out.due_date = addDays(7);
    else if (word === 'eow') out.due_date = addDays((5 - today.getDay() + 7) % 7 || 7);
    else if (word === 'eom') { const e = new Date(today.getFullYear(), today.getMonth() + 1, 0); out.due_date = e.toISOString().slice(0, 10); }
    else {
      const k = word.slice(0, 3);
      const target = dayMap[k];
      if (target !== undefined) {
        const diff = (target - today.getDay() + 7) % 7 || 7;
        out.due_date = addDays(diff);
      }
    }
    s = (s.slice(0, dm.index) + s.slice(dm.index + dm[0].length)).replace(/\s+/g, ' ').trim();
  }

  if (out.category === 'Other') {
    const low = s.toLowerCase();
    if (/\b(loi|acqui|diligence|nda|close|deal)\b/.test(low)) out.category = 'Acquisition';
    else if (/\b(bic|board)\b/.test(low)) out.category = 'BIC';
    else if (/\b(mhc|patient|campaign)\b/.test(low)) out.category = 'MHC';
    else if (/\b(ai|emr|scribe|clinic|hire|credential|ops)\b/.test(low)) out.category = 'Operations';
    else if (/\b(seo|brand|website|marketing)\b/.test(low)) out.category = 'Marketing';
  }

  out.title = s;
  return out;
}

function dueLabel(iso) {
  if (!iso) return null;
  const today = new Date(); today.setHours(0,0,0,0);
  const d = new Date(iso + 'T00:00:00');
  const diff = Math.round((d - today) / 86400000);
  if (diff === 0) return { text: 'Today', tone: 'today' };
  if (diff === 1) return { text: 'Tomorrow', tone: 'soon' };
  if (diff === -1) return { text: 'Yesterday', tone: 'overdue' };
  if (diff < 0) return { text: `${-diff}d overdue`, tone: 'overdue' };
  if (diff <= 3) return { text: `in ${diff}d`, tone: 'soon' };
  if (diff <= 7) return { text: `in ${diff}d`, tone: 'week' };
  if (diff <= 14) return { text: `in ${diff}d`, tone: 'far' };
  return { text: new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }), tone: 'far' };
}

function relativeTime(iso) {
  if (!iso) return '';
  const then = new Date(iso.replace(' ', 'T') + 'Z');
  const now = new Date();
  const secs = Math.round((now - then) / 1000);
  if (isNaN(secs)) return iso;
  if (secs < 45) return 'just now';
  if (secs < 3600) return `${Math.round(secs / 60)}m ago`;
  if (secs < 86400) return `${Math.round(secs / 3600)}h ago`;
  if (secs < 172800) return 'yesterday';
  if (secs < 86400 * 14) return `${Math.round(secs / 86400)}d ago`;
  return then.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
}

function renderBody(body, MentionClass = 'mention') {
  const parts = body.split(/(@\w+)/g);
  return parts.map((p, i) => p.startsWith('@')
    ? React.createElement('span', { key: i, className: MentionClass }, p)
    : p);
}

Object.assign(window, { useDashboardStore, parseQuickAdd, dueLabel, renderBody, relativeTime });
