// =========================================================
// MAIN APP — Desktop + Mobile side-by-side, both interactive.
// One shared store. Tweaks panel drives theme/type/density/habits.
// =========================================================

const { useState, useEffect, useRef, useMemo, useCallback } = React;

// ---------- Seed data ----------
// Empty — new users (and fresh-install teammates) start with a clean slate.
const SEED_TASKS  = [];
const SEED_INBOX  = [];
const SEED_HABITS = [];

// Kept for backwards compatibility — contexts now live in user state
// (state.contexts). Use `state.contexts` everywhere instead of this constant.
const DEFAULT_CONTEXTS = ['@computer', '@deep', '@calls', '@email', '@errands'];
const CONTEXTS = DEFAULT_CONTEXTS;

// ---------- Utilities ----------
const uid = () => Math.random().toString(36).slice(2, 9);
const pad = (n) => String(n).padStart(2, '0');
const fmtTime = (d) => `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
const fmtDate = (d) => d.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' });
const dayDots = ['M','T','W','T','F','S','S'];

// ---------- Time parsing for scheduled tasks ----------
// Accepts "9pm", "9:30am", "21:00", "14:30", "6 am", "6:15 p.m."
// Returns { dueAt: timestamp today, label: "9:00 PM" } or null
function parseTime(input) {
  if (!input) return null;
  const s = input.trim().toLowerCase().replace(/\s+/g, '');
  // 24h: HH:MM
  let m = s.match(/^(\d{1,2}):(\d{2})$/);
  let h, min, ampm;
  if (m) { h = +m[1]; min = +m[2]; }
  else {
    // 12h with optional minutes
    m = s.match(/^(\d{1,2})(?::(\d{2}))?(am|pm|a\.m\.|p\.m\.)$/);
    if (!m) return null;
    h = +m[1]; min = m[2] ? +m[2] : 0;
    ampm = m[3].replace(/\./g, '');
    if (ampm === 'pm' && h < 12) h += 12;
    if (ampm === 'am' && h === 12) h = 0;
  }
  if (h < 0 || h > 23 || min < 0 || min > 59) return null;
  const d = new Date();
  d.setHours(h, min, 0, 0);
  const label = `${((h % 12) || 12)}:${String(min).padStart(2,'0')} ${h < 12 ? 'AM' : 'PM'}`;
  return { dueAt: d.getTime(), label, h, min };
}

// Extract a time suffix from a task title: "Gym at 6pm" -> { title: "Gym", ...time }
function extractTimeFromTitle(title) {
  const re = /\s+(?:at|@)\s+(\d{1,2}(?::\d{2})?(?:\s?(?:am|pm|a\.m\.|p\.m\.))?|\d{1,2}:\d{2})\s*$/i;
  const m = title.match(re);
  if (!m) return null;
  const p = parseTime(m[1]);
  if (!p) return null;
  return { title: title.replace(re, '').trim(), ...p };
}

function fmtDueLabel(dueAt) {
  if (!dueAt) return null;
  const d = new Date(dueAt);
  const h = d.getHours(); const m = d.getMinutes();
  return `${((h % 12) || 12)}:${String(m).padStart(2,'0')} ${h < 12 ? 'AM' : 'PM'}`;
}

function fmtCountdown(dueAt, now) {
  const diff = dueAt - now.getTime();
  if (diff <= 0) return 'NOW';
  const mins = Math.floor(diff / 60000);
  const secs = Math.floor((diff % 60000) / 1000);
  if (mins >= 60) {
    const h = Math.floor(mins / 60); const rm = mins % 60;
    return `${h}h ${String(rm).padStart(2,'0')}m`;
  }
  if (mins >= 1) return `${mins}m ${String(secs).padStart(2,'0')}s`;
  return `${secs}s`;
}

// ---------- Theme tokens ----------
function themeTokens(theme) {
  if (theme === 'dark') return {
    bg: '#0A0B0D', panel: '#0F1114', panel2: '#14171C', line: 'rgba(255,255,255,0.08)',
    fg: '#EDEEF0', fg2: 'rgba(237,238,240,0.64)', fg3: 'rgba(237,238,240,0.38)',
    accent: 'oklch(78% 0.12 220)', accentSoft: 'oklch(78% 0.12 220 / 0.14)',
    done: 'rgba(237,238,240,0.28)', star: 'oklch(82% 0.13 85)',
    stage: '#060708',
  };
  if (theme === 'warm') return {
    bg: '#F6F2EA', panel: '#FBF8F2', panel2: '#F1ECE1', line: 'rgba(30,20,10,0.09)',
    fg: '#1A1612', fg2: 'rgba(26,22,18,0.64)', fg3: 'rgba(26,22,18,0.38)',
    accent: 'oklch(55% 0.11 40)', accentSoft: 'oklch(55% 0.11 40 / 0.12)',
    done: 'rgba(26,22,18,0.32)', star: 'oklch(62% 0.14 55)',
    stage: '#ECE6D9',
  };
  // light
  return {
    bg: '#F6F4EF', panel: '#FFFFFF', panel2: '#F0EDE6', line: 'rgba(10,11,13,0.08)',
    fg: '#0A0B0D', fg2: 'rgba(10,11,13,0.62)', fg3: 'rgba(10,11,13,0.36)',
    accent: 'oklch(52% 0.12 220)', accentSoft: 'oklch(52% 0.12 220 / 0.10)',
    done: 'rgba(10,11,13,0.28)', star: 'oklch(62% 0.14 80)',
    stage: '#E8E5DD',
  };
}

function typeTokens(mode) {
  if (mode === 'serif') return {
    sans: '"Instrument Serif", "Iowan Old Style", Georgia, serif',
    mono: '"Roboto Mono", ui-monospace, Menlo, monospace',
    headingWeight: 400,
    tight: '-0.01em',
    displayTight: '-0.02em',
  };
  return {
    sans: '"Inter", "Inter Display", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
    mono: '"Roboto Mono", ui-monospace, Menlo, monospace',
    headingWeight: 500,
    tight: '-0.01em',
    displayTight: '-0.04em',
  };
}

function densityTokens(d) {
  if (d === 'compact') return { rowPY: 8, rowPX: 14, gap: 14, sectionGap: 20, fontBase: 14 };
  if (d === 'cozy')    return { rowPY: 12, rowPX: 16, gap: 18, sectionGap: 28, fontBase: 15 };
  return { rowPY: 16, rowPX: 18, gap: 22, sectionGap: 36, fontBase: 15 };
}

// ---------- Date helpers (analytics) ----------
// Week starts on Monday. todayIdx = (getDay()+6)%7 maps Sun=6, Mon=0.
function mondayOf(d) {
  const x = new Date(d);
  x.setHours(0, 0, 0, 0);
  const offset = (x.getDay() + 6) % 7; // 0 on Monday
  x.setDate(x.getDate() - offset);
  return x;
}
function isoDate(d) {
  const x = new Date(d);
  const y = x.getFullYear();
  const m = String(x.getMonth() + 1).padStart(2, '0');
  const day = String(x.getDate()).padStart(2, '0');
  return `${y}-${m}-${day}`;
}
function addDays(d, n) {
  const x = new Date(d);
  x.setDate(x.getDate() + n);
  return x;
}
// Given a habit and a habitHistory map, return a 7-length 0/1 array for
// the current week (Mon..Sun) derived from history.
function currentWeekDays(habitId, history) {
  const monday = mondayOf(new Date());
  const out = [];
  const h = (history && history[habitId]) || {};
  for (let i = 0; i < 7; i++) {
    out.push(h[isoDate(addDays(monday, i))] ? 1 : 0);
  }
  return out;
}

// ---------- Responsive hook ----------
function useIsMobile(breakpoint = 720) {
  const [mobile, setMobile] = useState(
    typeof window !== 'undefined' && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
  );
  useEffect(() => {
    const mql = window.matchMedia(`(max-width: ${breakpoint}px)`);
    const handler = (e) => setMobile(e.matches);
    mql.addEventListener('change', handler);
    return () => mql.removeEventListener('change', handler);
  }, [breakpoint]);
  return mobile;
}

// ---------- Clock hook ----------
function useClock() {
  const [now, setNow] = useState(new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  return now;
}

Object.assign(window, {
  SEED_TASKS, SEED_INBOX, SEED_HABITS, CONTEXTS, DEFAULT_CONTEXTS,
  uid, pad, fmtTime, fmtDate, dayDots,
  parseTime, extractTimeFromTitle, fmtDueLabel, fmtCountdown,
  themeTokens, typeTokens, densityTokens,
  useClock, useIsMobile,
  mondayOf, isoDate, addDays, currentWeekDays,
  useState, useEffect, useRef, useMemo, useCallback,
});
