// Tempo — global app state hook backed by Supabase
// Returns habits (grouped by section), today's completions, today's agenda, day journals,
// trades + stats, and CRUD callbacks for everything.

const SECTION_LABELS = { matin: 'Matin', journee: 'Journée', soir: 'Soir', pretrade: 'Pré-trade' };
const SECTION_ORDER  = ['matin', 'journee', 'soir', 'pretrade'];

// Map UI group label → DB section code.
function sectionCodeFromLabel(label) {
  const s = String(label || '').toLowerCase();
  if (s.startsWith('matin'))   return 'matin';
  if (s.startsWith('journ'))   return 'journee';
  if (s.startsWith('soir'))    return 'soir';
  if (s.startsWith('pré-trade') || s.startsWith('pre-trade') || s.startsWith('pretrade')) return 'pretrade';
  return 'matin';
}

function useAppState() {
  const [user, setUser]         = React.useState(null);
  const [loading, setLoading]   = React.useState(true);
  const [habitsRows, setHabitsRows] = React.useState([]); // raw DB rows
  const [completionsToday, setCompletionsToday] = React.useState(new Set());
  // Historique des complétions sur HABIT_HISTORY_DAYS jours, pour le suivi dans le temps
  // (page Routines). Set de clés `habit_id|YYYY-MM-DD`.
  const [completionsHist, setCompletionsHist] = React.useState(() => new Set());
  const [agendaRows, setAgendaRows] = React.useState([]);
  const [dayJournals, setDayJournals] = React.useState({}); // keyed by ISO date
  const [dayAttachments, setDayAttachments] = React.useState({}); // keyed by ISO date
  const [trades, setTrades]     = React.useState([]);
  const [tradesLoading, setTradesLoading] = React.useState(true);
  const [refreshTick, setRefreshTick]     = React.useState(0);
  const [profile, setProfile]   = React.useState(null); // profiles row or null

  // Devise / fuseau dérivés du profil (fallbacks sains). Propagés AVANT le calcul
  // de `today` pour que todayISO() (qui lit window.__appTz) soit déjà dans le bon
  // fuseau au tout premier rendu où le profil est connu.
  const currency = profile?.currency || 'USD';
  const timezone = profile?.timezone || 'Europe/Paris';
  if (typeof window !== 'undefined') { window.__appCurrency = currency; window.__appTz = timezone; }

  const today = todayISO();

  // Historique des habitudes : nb de jours chargés pour le suivi (dots + série).
  const HABIT_HISTORY_DAYS = 28;
  const isoMinusDays = (iso, n) => {
    const [y, m, d] = String(iso).slice(0, 10).split('-').map(Number);
    const dt = new Date(y, m - 1, d - n);
    return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')}`;
  };

  // ── Boot: get user, fetch habits + completions + today's agenda
  React.useEffect(() => {
    let live = true;
    (async () => {
      if (!window.sb) { setLoading(false); return; }
      const { data: { user: u } } = await window.sb.auth.getUser();
      if (!live) return;
      setUser(u || null);
      if (!u) { setLoading(false); setTradesLoading(false); return; }
      await Promise.all([
        loadHabits(),
        loadCompletions(today),
        loadCompletionsHistory(),
        loadAgenda(today),
        loadTrades(),
        loadProfile(),
      ]);
      if (!live) return;
      setLoading(false);
      setTradesLoading(false);
    })();
    return () => { live = false; };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [refreshTick]);

  // Subscribe to auth changes to reload state
  React.useEffect(() => {
    if (!window.sb) return;
    const { data: sub } = window.sb.auth.onAuthStateChange((_evt, session) => {
      setUser(session?.user || null);
      if (session?.user) setRefreshTick(t => t + 1);
      else {
        setHabitsRows([]); setCompletionsToday(new Set()); setCompletionsHist(new Set()); setAgendaRows([]);
        setDayJournals({}); setDayAttachments({}); setTrades([]); setProfile(null);
      }
    });
    return () => { try { sub.subscription.unsubscribe(); } catch (e) {} };
  }, []);

  async function loadHabits() {
    const { data, error } = await window.sb.from('habits')
      .select('*').eq('archived', false)
      .order('section').order('position').order('created_at');
    if (error) { console.error('[Tempo] habits', error); return; }
    setHabitsRows(data || []);
  }

  async function loadCompletions(date) {
    const { data, error } = await window.sb.from('habit_completions')
      .select('habit_id').eq('date', date);
    if (error) { console.error('[Tempo] completions', error); return; }
    setCompletionsToday(new Set((data || []).map(r => r.habit_id)));
  }

  // Charge l'historique récent (HABIT_HISTORY_DAYS jours) pour le suivi dans le temps.
  async function loadCompletionsHistory() {
    const since = isoMinusDays(today, HABIT_HISTORY_DAYS - 1);
    const { data, error } = await window.sb.from('habit_completions')
      .select('habit_id, date').gte('date', since);
    if (error) { console.error('[Tempo] completionsHist', error); return; }
    setCompletionsHist(new Set((data || []).map(r => r.habit_id + '|' + r.date)));
  }

  async function loadAgenda(date) {
    const { data, error } = await window.sb.from('agenda_events')
      .select('*').eq('date', date).order('time_at');
    if (error) { console.error('[Tempo] agenda', error); return; }
    setAgendaRows(data || []);
  }

  async function loadTrades() {
    setTradesLoading(true);
    const data = await listTrades({});
    setTrades(data);
    setTradesLoading(false);
  }

  async function loadProfile() {
    const { data: { user: u } } = await window.sb.auth.getUser();
    const uid = u?.id;
    if (!uid) { setProfile(null); return; }
    const { data, error } = await window.sb.from('profiles')
      .select('*').eq('id', uid).maybeSingle();
    if (error) {
      // "table missing" (PGRST205) = migration not run yet → expected, stay quiet.
      // Anything else is a real warning worth surfacing in the console.
      if (error.code !== 'PGRST205') console.warn('[Tempo] profile load:', error.message);
      setProfile(null);
      return;
    }
    setProfile(data || null);
  }

  async function loadDayJournal(date) {
    const { data, error } = await window.sb.from('day_journals')
      .select('*').eq('date', date).maybeSingle();
    if (error) { console.error('[Tempo] dayJournal', error); return null; }
    setDayJournals(prev => ({ ...prev, [date]: data || { notes: '', mood: null } }));
    return data;
  }

  async function loadDayAttachments(date) {
    const { data, error } = await window.sb.from('day_attachments')
      .select('*').eq('date', date).order('created_at', { ascending: false });
    if (error) { console.error('[Tempo] dayAttach', error); return []; }
    setDayAttachments(prev => ({ ...prev, [date]: data || [] }));
    return data || [];
  }

  // ── Habits CRUD ─────────────────────────────────────────────────
  // Group buckets for ALL 4 sections (used by Routines page).
  const habitsBySectionAll = React.useMemo(() => {
    const buckets = { matin: [], journee: [], soir: [], pretrade: [] };
    for (const h of habitsRows) {
      const sec = SECTION_ORDER.includes(h.section) ? h.section : 'matin';
      buckets[sec].push({
        id: h.id,
        t: h.name,
        d: completionsToday.has(h.id),
        section: sec,
      });
    }
    return SECTION_ORDER.map(sec => ({
      g: SECTION_LABELS[sec],
      sec,
      items: buckets[sec],
    }));
  }, [habitsRows, completionsToday]);

  // Daily habits (matin/journee/soir) only — used on Home's "Habitudes" card.
  const habitsBySection = React.useMemo(
    () => habitsBySectionAll.filter(g => g.sec !== 'pretrade'),
    [habitsBySectionAll]
  );

  // Pre-trade items (flat list) — used on Home's pre-trade checklist.
  const pretradeItems = React.useMemo(
    () => (habitsBySectionAll.find(g => g.sec === 'pretrade') || { items: [] }).items,
    [habitsBySectionAll]
  );

  // ── Suivi des habitudes dans le temps (7 derniers jours + série en cours) ──
  const recentDays = React.useMemo(() => {
    const arr = [];
    for (let i = 6; i >= 0; i--) arr.push(isoMinusDays(today, i)); // ancien → récent
    return arr;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [today]);
  const habitDoneOn = React.useCallback(
    (id, date) => completionsHist.has(id + '|' + date), [completionsHist]
  );
  const habitStreak = React.useCallback((id) => {
    let s = 0;
    // Si aujourd'hui n'est pas encore coché, on part d'hier : la série n'est pas
    // "cassée" tant que la journée en cours n'est pas terminée.
    const start = completionsHist.has(id + '|' + today) ? 0 : 1;
    for (let i = start; i <= HABIT_HISTORY_DAYS; i++) {
      if (completionsHist.has(id + '|' + isoMinusDays(today, i))) s++;
      else break;
    }
    return s;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [completionsHist, today]);

  async function toggleHabit(id) {
    if (!user) return;
    const isChecked = completionsToday.has(id);
    // Optimistic — on met à jour l'instantané du jour ET l'historique récent.
    setCompletionsToday(prev => {
      const next = new Set(prev);
      if (isChecked) next.delete(id); else next.add(id);
      return next;
    });
    setCompletionsHist(prev => {
      const next = new Set(prev);
      const key = id + '|' + today;
      if (isChecked) next.delete(key); else next.add(key);
      return next;
    });
    if (isChecked) {
      const { error } = await window.sb.from('habit_completions')
        .delete().eq('habit_id', id).eq('date', today);
      if (error) { console.error('[Tempo] toggleHabit del', error); loadCompletions(today); }
    } else {
      const { error } = await window.sb.from('habit_completions')
        .insert({ user_id: user.id, habit_id: id, date: today });
      if (error) { console.error('[Tempo] toggleHabit add', error); loadCompletions(today); }
    }
  }

  async function addHabit(groupLabelOrSection, name) {
    const trimmed = (name || '').trim();
    if (!trimmed) return { error: { message: 'Nom vide.' } };
    // Resolve the user just-in-time (handles boot race where local `user` state may lag).
    let uid = user?.id;
    if (!uid && window.sb) {
      try { const { data } = await window.sb.auth.getUser(); uid = data?.user?.id; } catch (e) {}
    }
    if (!uid) { console.warn('[Tempo] addHabit: pas de session active'); return { error: { message: 'Tu dois être connecté.' } }; }
    const section = SECTION_ORDER.includes(groupLabelOrSection)
      ? groupLabelOrSection
      : sectionCodeFromLabel(groupLabelOrSection);
    const pos = habitsRows.filter(h => h.section === section).length;
    const { data, error } = await window.sb.from('habits')
      .insert({ user_id: uid, section, name: trimmed, position: pos })
      .select().single();
    if (error) { console.error('[Tempo] addHabit', error); return { error }; }
    setHabitsRows(prev => [...prev, data]);
    return { data };
  }

  async function removeHabit(id) {
    setHabitsRows(prev => prev.filter(h => h.id !== id));
    const { error } = await window.sb.from('habits').delete().eq('id', id);
    if (error) { console.error('[Tempo] removeHabit', error); loadHabits(); }
  }

  async function renameHabit(id, name) {
    setHabitsRows(prev => prev.map(h => h.id === id ? { ...h, name } : h));
    const { error } = await window.sb.from('habits').update({ name }).eq('id', id);
    if (error) { console.error('[Tempo] renameHabit', error); loadHabits(); }
  }

  // ── Agenda CRUD ─────────────────────────────────────────────────
  const agenda = React.useMemo(() => {
    return agendaRows.map(r => ({
      id: r.id,
      h: (r.time_at || '00:00').slice(0, 5),
      e: r.title,
      tag: r.tag || '',
      done: !!r.is_done,
      _row: r,
    })).sort((a, b) => a.h.localeCompare(b.h));
  }, [agendaRows]);

  async function toggleAgenda(id) {
    const row = agendaRows.find(r => r.id === id);
    if (!row) return;
    const next = !row.is_done;
    setAgendaRows(prev => prev.map(r => r.id === id ? { ...r, is_done: next } : r));
    const { error } = await window.sb.from('agenda_events')
      .update({ is_done: next }).eq('id', id);
    if (error) { console.error('[Tempo] toggleAgenda', error); loadAgenda(today); }
  }

  async function addAgenda(item) {
    if (!item || !item.e || !item.e.trim()) return { error: { message: 'Titre vide.' } };
    let uid = user?.id;
    if (!uid && window.sb) {
      try { const { data } = await window.sb.auth.getUser(); uid = data?.user?.id; } catch (e) {}
    }
    if (!uid) { console.warn('[Tempo] addAgenda: pas de session active'); return { error: { message: 'Tu dois être connecté.' } }; }
    const row = {
      user_id: uid,
      date: item.date || today,
      time_at: item.h || '12:00',
      title: item.e.trim(),
      tag: item.tag || null,
      is_done: false,
    };
    const { data, error } = await window.sb.from('agenda_events').insert(row).select().single();
    if (error) { console.error('[Tempo] addAgenda', error); return { error }; }
    if (row.date === today) setAgendaRows(prev => [...prev, data]);
    return { data };
  }

  async function removeAgenda(id) {
    setAgendaRows(prev => prev.filter(r => r.id !== id));
    const { error } = await window.sb.from('agenda_events').delete().eq('id', id);
    if (error) { console.error('[Tempo] removeAgenda', error); loadAgenda(today); }
  }

  // ── Day journal (notes + mood) ──────────────────────────────────
  async function getDayJournal(date) {
    if (!date) return null;
    if (dayJournals[date]) return dayJournals[date];
    return await loadDayJournal(date);
  }

  async function setDayJournalNotes(date, notes) {
    if (!user || !date) return;
    setDayJournals(prev => ({ ...prev, [date]: { ...(prev[date] || {}), notes } }));
    const payload = { user_id: user.id, date, notes, updated_at: new Date().toISOString() };
    const { error } = await window.sb.from('day_journals')
      .upsert(payload, { onConflict: 'user_id,date' });
    if (error) console.error('[Tempo] setDayJournalNotes', error);
  }

  async function setDayJournalMood(date, mood) {
    if (!user || !date) return;
    setDayJournals(prev => ({ ...prev, [date]: { ...(prev[date] || {}), mood } }));
    const payload = { user_id: user.id, date, mood, updated_at: new Date().toISOString() };
    const { error } = await window.sb.from('day_journals')
      .upsert(payload, { onConflict: 'user_id,date' });
    if (error) console.error('[Tempo] setDayJournalMood', error);
  }

  // ── Day attachments (storage) ───────────────────────────────────
  async function getDayAttachments(date) {
    if (!date) return [];
    if (dayAttachments[date]) return dayAttachments[date];
    return await loadDayAttachments(date);
  }

  async function uploadDayAttachment(date, file) {
    if (!user || !file || !date) return { error: { message: 'Données manquantes.' } };
    const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, '_');
    const path = `${user.id}/${date}/${Date.now()}_${safeName}`;
    const { error: upErr } = await window.sb.storage.from('day-attachments')
      .upload(path, file, { cacheControl: '3600', upsert: false, contentType: file.type });
    if (upErr) { console.error('[Tempo] storage upload', upErr); return { error: upErr }; }
    const { data, error } = await window.sb.from('day_attachments').insert({
      user_id: user.id, date,
      file_path: path, file_name: file.name,
      size_bytes: file.size || 0, mime_type: file.type || null,
    }).select().single();
    if (error) { console.error('[Tempo] day_attachments insert', error); return { error }; }
    setDayAttachments(prev => ({ ...prev, [date]: [data, ...(prev[date] || [])] }));
    return { data };
  }

  async function deleteDayAttachment(date, id) {
    const row = (dayAttachments[date] || []).find(r => r.id === id);
    setDayAttachments(prev => ({ ...prev, [date]: (prev[date] || []).filter(r => r.id !== id) }));
    if (row?.file_path) {
      const { error: rmErr } = await window.sb.storage.from('day-attachments').remove([row.file_path]);
      if (rmErr) console.error('[Tempo] storage remove', rmErr);
    }
    const { error } = await window.sb.from('day_attachments').delete().eq('id', id);
    if (error) console.error('[Tempo] del attachment', error);
  }

  async function getAttachmentUrl(file_path) {
    if (!file_path) return null;
    const { data, error } = await window.sb.storage.from('day-attachments')
      .createSignedUrl(file_path, 3600);
    if (error) { console.error('[Tempo] signed url', error); return null; }
    return data?.signedUrl || null;
  }

  // ── Trades ──────────────────────────────────────────────────────
  const stats = React.useMemo(() => computeStats(trades), [trades]);
  // Groupement par jour LOCAL du fuseau de l'user (calendrier/daily cohérents).
  const tradesByDay = React.useMemo(() => groupTradesByDay(trades, timezone), [trades, timezone]);

  async function refreshTrades() { await loadTrades(); }

  // ── Profile ─────────────────────────────────────────────────────
  // Upsert the user's profile row (on conflict id) and update local state.
  async function saveProfile(patch) {
    let uid = user?.id;
    if (!uid && window.sb) {
      try { const { data } = await window.sb.auth.getUser(); uid = data?.user?.id; } catch (e) {}
    }
    if (!uid) return { error: { message: 'Tu dois être connecté.' } };
    const payload = { id: uid, ...patch, updated_at: new Date().toISOString() };
    const { data, error } = await window.sb.from('profiles')
      .upsert(payload, { onConflict: 'id' })
      .select().single();
    if (error) {
      if (error.code !== 'PGRST205') console.warn('[Tempo] saveProfile:', error.message);
      return { error };
    }
    setProfile(data);
    return { data };
  }

  // Derived display name: profile.display_name → profile.first_name →
  // email-derived first name → 'Trader'. Always a non-empty string.
  const displayName = React.useMemo(() => {
    const dn = (profile?.display_name || '').trim();
    if (dn) return dn;
    const fn = (profile?.first_name || '').trim();
    if (fn) return fn;
    const email = user?.email || '';
    if (email) {
      const handle = email.split('@')[0].split('.')[0];
      if (handle) return handle.charAt(0).toUpperCase() + handle.slice(1);
    }
    return 'Trader';
  }, [profile, user]);

  // Helper to refresh agenda by date if user selects another day
  async function loadAgendaFor(date) {
    if (!date || date === today) return loadAgenda(today);
    const { data, error } = await window.sb.from('agenda_events')
      .select('*').eq('date', date).order('time_at');
    if (error) return [];
    return data || [];
  }

  return {
    user, loading,
    // habits — daily groups (matin/journee/soir) for Home, all four for Routines.
    habits: habitsBySection,
    habitsAll: habitsBySectionAll,
    pretradeItems,
    toggleHabit, addHabit, removeHabit, renameHabit,
    // suivi des habitudes dans le temps
    recentDays, habitDoneOn, habitStreak,
    // agenda
    agenda, toggleAgenda, addAgenda, removeAgenda, loadAgendaFor,
    // day journal
    dayJournals, getDayJournal, setDayJournalNotes, setDayJournalMood,
    // attachments
    dayAttachments, getDayAttachments, uploadDayAttachment, deleteDayAttachment, getAttachmentUrl,
    // trades
    trades, tradesLoading, stats, tradesByDay, refreshTrades,
    // profile
    profile, saveProfile, displayName, currency, timezone,
    // misc
    refresh: () => setRefreshTick(t => t + 1),
    today,
  };
}

Object.assign(window, { useAppState, sectionCodeFromLabel, SECTION_LABELS, SECTION_ORDER });
