// VYB LIFE — Safe Activity Events (Step 4)
// ─────────────────────────────────────────────────────────────────
// Generates system-controlled progress events from current state.
// NEVER includes user-generated text (habit names, task titles,
// idea text, reading notes, quotes, insights, mood, project names).
// Only milestone counts, ids, types, timestamps.
//
// Storage:
//   vyb-safe-activity-events   → array of events (cap 100, sorted desc)
//   vyb-safe-activity-last-aura → last-seen active aura id (for change detect)
//
// Future server publish must filter by SAFE_SOCIAL_EVENT_TYPES +
// safeForPublic before exposing anything outside the authenticated app.
// ─────────────────────────────────────────────────────────────────

const SAFE_ACTIVITY_EVENT_TYPES = {
  aura_unlocked: {
    type: 'aura_unlocked',
    label: '{user} unlocked {auraTitle}.',
    icon: 'sparkles',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
  active_aura_changed: {
    type: 'active_aura_changed',
    label: '{user} set {auraTitle} as Active Aura.',
    icon: 'zap',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
  aura_path_stage_completed: {
    type: 'aura_path_stage_completed',
    label: '{user} completed a {pathTitle} stage.',
    icon: 'check-circle',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
  today_system_completed: {
    type: 'today_system_completed',
    label: '{user} completed Today\u2019s System.',
    icon: 'circle-check',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
  weekly_goal_completed: {
    type: 'weekly_goal_completed',
    label: '{user} completed a weekly goal.',
    icon: 'target',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
  streak_reached: {
    type: 'streak_reached',
    label: '{user} reached a {n}-day rhythm.',
    icon: 'flame',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
  book_finished: {
    type: 'book_finished',
    // Title intentionally omitted from public template — book title is
    // user-content. Local UI may show "Book finished" only.
    label: '{user} finished a book.',
    icon: 'book-open',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
  reading_milestone: {
    type: 'reading_milestone',
    label: '{user} reached a reading milestone.',
    icon: 'book',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
  ideas_milestone: {
    type: 'ideas_milestone',
    label: '{user} captured {n} ideas.',
    icon: 'lightbulb',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
  tasks_milestone: {
    type: 'tasks_milestone',
    label: '{user} completed {n} tasks.',
    icon: 'list-todo',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
  focus_milestone: {
    type: 'focus_milestone',
    label: '{user} completed {n} focus sessions.',
    icon: 'timer',
    safeForPublic: true,
    includesUserGeneratedContent: false,
  },
};

const _LS_EVENTS    = 'vyb-safe-activity-events';
const _LS_LAST_AURA = 'vyb-safe-activity-last-aura';
const _EVENT_CAP    = 100;

const STREAK_THRESHOLDS  = [3, 7, 14, 30];
const IDEAS_THRESHOLDS   = [10, 25, 50, 100];
const TASKS_THRESHOLDS   = [10, 25, 50, 100];
const READING_THRESHOLDS = [3, 10, 25, 50, 100];
const FOCUS_THRESHOLDS   = [5, 25, 50];

const _readEvents = () => {
  try {
    const raw = localStorage.getItem(_LS_EVENTS);
    if (!raw) return [];
    const arr = JSON.parse(raw);
    return Array.isArray(arr) ? arr : [];
  } catch { return []; }
};
const _writeEvents = (arr) => {
  try {
    const trimmed = arr.slice(0, _EVENT_CAP);
    localStorage.setItem(_LS_EVENTS, JSON.stringify(trimmed));
  } catch {}
};
const _todayKey = () => {
  const d = new Date(); const p = (n) => String(n).padStart(2,'0');
  return `${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())}`;
};
const _now = () => Date.now();
const _crossedThresholds = (current, list) => list.filter(t => current >= t);

// Build a safe event object. Internal-only fields like metadata.bookId
// are kept locally but MUST NOT be emitted publicly.
const _mkEvent = (type, { id, title, description, metadata = {} }) => ({
  id, // unique dedupe key
  type,
  title,
  description,
  timestamp: _now(),
  visibility: 'private_preview',
  safeForPublic: !!(SAFE_ACTIVITY_EVENT_TYPES[type] && SAFE_ACTIVITY_EVENT_TYPES[type].safeForPublic),
  includesUserGeneratedContent: !!(SAFE_ACTIVITY_EVENT_TYPES[type] && SAFE_ACTIVITY_EVENT_TYPES[type].includesUserGeneratedContent),
  metadata,
});

// Public templating — never includes user-generated text. Used for
// "what others would see" preview.
const renderSafePublicLabel = (event, userName) => {
  const def = SAFE_ACTIVITY_EVENT_TYPES[event.type];
  if (!def) return event.title || '';
  const md = event.metadata || {};
  const u = userName || 'Someone';
  return def.label
    .replace('{user}', u)
    .replace('{auraTitle}', md.auraTitle || 'an aura')
    .replace('{pathTitle}', md.pathTitle || 'a path')
    .replace('{n}', md.n != null ? md.n : '');
};

// Relative time formatter (small, English-only).
const formatActivityTime = (ts) => {
  if (!ts) return '';
  const diff = Math.max(0, _now() - ts);
  const m = Math.floor(diff / 60000);
  if (m < 1) return 'Just now';
  if (m < 60) return `${m}m ago`;
  const h = Math.floor(m / 60);
  if (h < 24) return `${h}h ago`;
  const d = Math.floor(h / 24);
  if (d < 7) return `${d}d ago`;
  const date = new Date(ts);
  return date.toLocaleDateString('en-US', { month:'short', day:'numeric' });
};

// Main builder. Reads current app state + Aura, merges with existing
// stored events using stable dedupe keys. Returns events sorted desc.
//
// Inputs (all optional):
//   storeState       — store.state from useStore
//   momentum         — result of window.calculateMomentum (optional; recomputed if absent)
//   activeAuraId     — string | null
//   unlockedAuraIds  — Set<string> | string[]
const buildSafeActivityEvents = (input = {}) => {
  const state = input.storeState || {};
  const habits = state.habits || [];
  const tasks  = state.tasks  || [];
  const ideas  = (state.ideas  || []).filter(i => i && !i.archived);
  const books  = state.books  || [];
  const focusSessions = state.focusSessions || [];
  const readingEntries = books.flatMap(b => Array.isArray(b && b.entries) ? b.entries : []);

  // Momentum (re-calc if not provided).
  let m = input.momentum;
  if (!m && typeof window !== 'undefined' && window.calculateMomentum) {
    try {
      m = window.calculateMomentum({
        habits, tasks, ideas: state.ideas || [], books,
        habitCategories: state.habitCategories || [],
      });
    } catch { m = null; }
  }

  // Aura state.
  const ARTIFACTS = (typeof window !== 'undefined' && window.ARTIFACTS) || {};
  const PATHS = (typeof window !== 'undefined' && window.PATHS) || [];
  const auraTitleOf = (id) => (ARTIFACTS[id] && ARTIFACTS[id].title) || id;
  const auraRarityOf = (id) => (ARTIFACTS[id] && ARTIFACTS[id].rarity) || '';

  const unlockedIds = (() => {
    if (input.unlockedAuraIds instanceof Set) return Array.from(input.unlockedAuraIds);
    if (Array.isArray(input.unlockedAuraIds)) return input.unlockedAuraIds;
    try {
      const raw = localStorage.getItem('vyb-unlocked-aura') || localStorage.getItem('vyb-unlocked-artifacts');
      const arr = raw ? JSON.parse(raw) : [];
      return Array.isArray(arr) ? arr : [];
    } catch { return []; }
  })();

  let activeId = input.activeAuraId;
  if (activeId === undefined) {
    try { activeId = localStorage.getItem('vyb-active-aura') || localStorage.getItem('vyb-active-artifact') || null; }
    catch { activeId = null; }
  }

  const existing = _readEvents();
  const seenIds = new Set(existing.map(e => e.id));
  const additions = [];
  const push = (ev) => {
    if (!ev || seenIds.has(ev.id)) return;
    seenIds.add(ev.id);
    additions.push(ev);
  };

  // 1) aura_unlocked — one per unlocked aura id
  unlockedIds.forEach(id => {
    push(_mkEvent('aura_unlocked', {
      id: `aura_unlocked:${id}`,
      title: 'Aura unlocked',
      description: `${auraTitleOf(id)}${auraRarityOf(id) ? ` · ${auraRarityOf(id)} Aura` : ''}`,
      metadata: { auraId: id, auraTitle: auraTitleOf(id), rarity: auraRarityOf(id) },
    }));
  });

  // 2) active_aura_changed — emit when active aura id changes
  let lastActive = null;
  try { lastActive = localStorage.getItem(_LS_LAST_AURA) || null; } catch {}
  if (activeId && activeId !== lastActive) {
    push(_mkEvent('active_aura_changed', {
      id: `active_aura_changed:${activeId}:${_todayKey()}`,
      title: 'Active Aura updated',
      description: `${auraTitleOf(activeId)} is now active.`,
      metadata: { auraId: activeId, auraTitle: auraTitleOf(activeId) },
    }));
    try { localStorage.setItem(_LS_LAST_AURA, activeId); } catch {}
  }

  // 3) today_system_completed — once per day
  if (m && m.today && m.today.isDailyHabitsComplete && (m.today.totalHabits || 0) > 0) {
    push(_mkEvent('today_system_completed', {
      id: `today_system_completed:${_todayKey()}`,
      title: 'Today\u2019s System complete',
      description: 'All planned habits completed today.',
      metadata: { dateKey: _todayKey() },
    }));
  }

  // 4) streak_reached — per crossed threshold
  const streakDays = (m && m.streak && (m.streak.current || m.streak.days)) || 0;
  _crossedThresholds(streakDays, STREAK_THRESHOLDS).forEach(n => {
    push(_mkEvent('streak_reached', {
      id: `streak_reached:${n}`,
      title: `${n}-day rhythm reached`,
      description: 'Consistency is building.',
      metadata: { n },
    }));
  });

  // 5) aura_path_stage_completed — from computePathsState (or fallback)
  if (typeof window !== 'undefined' && window.computePathsState && m) {
    try {
      const ps = window.computePathsState(m, state);
      (ps.paths || []).forEach(p => {
        (p.stages || []).forEach((s, idx) => {
          if (s.locked || !s.complete) return;
          push(_mkEvent('aura_path_stage_completed', {
            id: `path_stage_completed:${p.id}:${s.id}`,
            title: 'Path stage completed',
            description: `${p.title} · Stage ${idx + 1}`,
            metadata: { pathId: p.id, pathTitle: p.title, stageId: s.id, stageNumber: idx + 1 },
          }));
        });
      });
    } catch {}
  }

  // 6) book_finished — once per finished book id (no title in public template)
  books.forEach(b => {
    if (!b || !b.id) return;
    if (b.totalPages && (b.currentPage || 0) >= b.totalPages) {
      push(_mkEvent('book_finished', {
        id: `book_finished:${b.id}`,
        title: 'Book finished',
        description: 'Another one closed.',
        metadata: { bookId: b.id }, // title intentionally omitted
      }));
    }
  });

  // 7) ideas_milestone
  _crossedThresholds(ideas.length, IDEAS_THRESHOLDS).forEach(n => {
    push(_mkEvent('ideas_milestone', {
      id: `ideas_milestone:${n}`,
      title: 'Idea milestone reached',
      description: `${n} ideas captured.`,
      metadata: { n },
    }));
  });

  // 8) tasks_milestone
  const completedTasks = tasks.filter(t => t && t.done).length;
  _crossedThresholds(completedTasks, TASKS_THRESHOLDS).forEach(n => {
    push(_mkEvent('tasks_milestone', {
      id: `tasks_milestone:${n}`,
      title: 'Task milestone reached',
      description: `${n} tasks completed.`,
      metadata: { n },
    }));
  });

  // 9) reading_milestone (sessions; pages not yet reliable)
  _crossedThresholds(readingEntries.length, READING_THRESHOLDS).forEach(n => {
    push(_mkEvent('reading_milestone', {
      id: `reading_milestone:${n}`,
      title: 'Reading milestone reached',
      description: `${n} reading sessions completed.`,
      metadata: { n },
    }));
  });

  // 10) focus_milestone (only if focus exists)
  if (focusSessions.length) {
    _crossedThresholds(focusSessions.length, FOCUS_THRESHOLDS).forEach(n => {
      push(_mkEvent('focus_milestone', {
        id: `focus_milestone:${n}`,
        title: 'Focus milestone reached',
        description: `${n} focus sessions completed.`,
        metadata: { n },
      }));
    });
  }

  // Persist if anything new.
  let merged = existing;
  if (additions.length) {
    merged = [...additions, ...existing].sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
    _writeEvents(merged);
  }
  return merged;
};

// Helper for tests / dev: clear safe activity log.
const clearSafeActivityEvents = () => {
  try {
    localStorage.removeItem(_LS_EVENTS);
    localStorage.removeItem(_LS_LAST_AURA);
  } catch {}
};

Object.assign(window, {
  SAFE_ACTIVITY_EVENT_TYPES,
  buildSafeActivityEvents,
  renderSafePublicLabel,
  formatActivityTime,
  clearSafeActivityEvents,
});
