// Backend-only store
const STORAGE_KEY = 'merida.v5';

function loadStore(){
  try{
    const raw = localStorage.getItem(STORAGE_KEY);
    if(!raw) return { users: [], courses: [], classes: [], meetings: [], session: null };
    const parsed = JSON.parse(raw);
    return { users: [], courses: [], classes: [], meetings: [], session: parsed.session || null };
  }catch(e){
    return { users: [], courses: [], classes: [], meetings: [], session: null };
  }
}
function saveStore(state){
  localStorage.setItem(STORAGE_KEY, JSON.stringify({ session: state.session }));
}

// React context-style hook (no context, just a simple global)
const StoreCtx = React.createContext(null);

function StoreProvider({ children }){
  const [state, setState] = React.useState(() => loadStore());
  const [apiUrl, setApiUrlState] = React.useState(() => (window.api?.getApiUrl?.() || ''));
  const [apiBusy, setApiBusy] = React.useState(false);
  const [apiError, setApiError] = React.useState('');

  React.useEffect(() => { saveStore(state); }, [state]);

  const online = true; // Always online

  // Run op through API
  async function withApi(remoteFn){
    setApiBusy(true); setApiError('');
    try {
      return await remoteFn();
    } catch(e){
      setApiError(e.message || 'Request failed');
      throw e;
    } finally {
      setApiBusy(false);
    }
  }

  // Pull all data from backend into local state
  async function syncFromBackend(){
    try {
      setApiBusy(true);
      const data = await window.api.bootstrap();
      setState(s => ({
        ...s,
        users: data.users || [],
        courses: data.courses || [],
        classes: data.classes || [],
        meetings: data.meetings || [],
      }));
    } catch(e){
      setApiError(e.message || 'Sync failed');
    } finally {
      setApiBusy(false);
    }
  }

  // Re-sync after token changes
  React.useEffect(() => {
    if(state.session) syncFromBackend();
    // eslint-disable-next-line
  }, [apiUrl, state.session?.userId]);

  const api = React.useMemo(() => ({
    state,
    online,
    apiUrl,
    apiBusy,
    apiError,
    syncFromBackend,
    reset(){ window.api.setToken(''); setState({ users: [], courses: [], classes: [], meetings: [], session: null }); },

    // -------- backend config --------
    disconnectBackend(){
      window.api.setApiUrl('');
      window.api.setToken('');
      setApiUrlState('');
      setApiError('');
    },

    // -------- session --------
    async login(email, password){
      try {
        const { token, user } = await window.api.login(email, password);
        window.api.setToken(token);
        const data = await window.api.bootstrap();
        setState(s => ({
          ...s,
          users: data.users || [],
          courses: data.courses || [],
          classes: data.classes || [],
          meetings: data.meetings || [],
          session: { userId: user.id },
        }));
        return { ok: true, user };
      } catch(e){
        return { ok: false, error: e.message || 'Login failed' };
      }
    },
    async requestPasswordReset(email){
      return withApi(() => window.api.forgotPassword(email).then(() => ({ ok: true })));
    },
    async resetPassword(email, code, newPassword){
      return withApi(() => window.api.resetPassword(email, code, newPassword).then(() => ({ ok: true })));
    },
    logout(){
      window.api.setToken('');
      setState(s => ({ ...s, session: null }));
    },
    currentUser(){
      if(!state.session) return null;
      return state.users.find(u => u.id === state.session.userId) || null;
    },

    // -------- users --------
    async createUser(data){
      return withApi(() => window.api.createUser(data).then(u => { setState(s => ({ ...s, users: [...s.users, u] })); return u.id; }));
    },
    async updateUser(id, patch){
      return withApi(() => window.api.updateUser(id, patch).then(u => setState(s => ({ ...s, users: s.users.map(x => x.id===id?u:x) }))));
    },
    async deleteUser(id){
      return withApi(() => window.api.deleteUser(id).then(() => setState(s => ({
        ...s,
        users: s.users.filter(u => u.id !== id),
        meetings: s.meetings.map(m => ({ ...m, attendees: m.attendees.filter(a => a!==id) })),
        session: s.session?.userId === id ? null : s.session,
      }))));
    },

    // -------- courses --------
    async createCourse(data){
      return withApi(() => window.api.createCourse(data).then(c => { setState(s => ({ ...s, courses: [...s.courses, c] })); return c.id; }));
    },
    async updateCourse(id, patch){
      return withApi(() => window.api.updateCourse(id, patch).then(c => setState(s => ({ ...s, courses: s.courses.map(x => x.id===id?c:x) }))));
    },
    async deleteCourse(id){
      return withApi(() => window.api.deleteCourse(id).then(() => setState(s => ({
        ...s,
        courses: s.courses.filter(c => c.id !== id),
        classes: s.classes.filter(cl => cl.courseId !== id),
        users: s.users.map(u => ({ ...u, courses: (u.courses||[]).filter(cid => cid !== id) })),
      }))));
    },
    async assignCourseStudents(courseId, studentIds){
      return withApi(() => window.api.assignCourseStudents(courseId, studentIds).then(() => {
        setState(s => ({
          ...s,
          users: s.users.map(u => {
            if(u.role !== 'student') return u;
            const courses = new Set(u.courses || []);
            if(studentIds.includes(u.id)) courses.add(courseId);
            else courses.delete(courseId);
            return { ...u, courses: [...courses] };
          })
        }));
      }));
    },

    // -------- classes --------
    async createClass(data){
      return withApi(() => window.api.createClass(data).then(c => { setState(s => ({ ...s, classes: [...s.classes, c] })); return c.id; }));
    },
    async updateClass(id, patch){
      return withApi(() => window.api.updateClass(id, patch).then(c => setState(s => ({ ...s, classes: s.classes.map(x => x.id===id?c:x) }))));
    },
    async deleteClass(id){
      return withApi(() => window.api.deleteClass(id).then(() => setState(s => ({ ...s, classes: s.classes.filter(c => c.id !== id) }))));
    },
    skipClassDate(id, dateIso){
      const cl = state.classes.find(c => c.id === id);
      const next = [...new Set([...(cl?.skippedDates||[]), dateIso])];
      return withApi(() => window.api.updateClass(id, { skippedDates: next }).then(c => setState(s => ({ ...s, classes: s.classes.map(x => x.id===id?c:x) }))));
    },
    unskipClassDate(id, dateIso){
      const cl = state.classes.find(c => c.id === id);
      const next = (cl?.skippedDates||[]).filter(d => d !== dateIso);
      return withApi(() => window.api.updateClass(id, { skippedDates: next }).then(c => setState(s => ({ ...s, classes: s.classes.map(x => x.id===id?c:x) }))));
    },

    // -------- meetings --------
    async createMeeting(data){
      return withApi(() => window.api.createMeeting(data).then(m => { setState(s => ({ ...s, meetings: [...s.meetings, m] })); return m.id; }));
    },
    async updateMeeting(id, patch){
      return withApi(() => window.api.updateMeeting(id, patch).then(m => setState(s => ({ ...s, meetings: s.meetings.map(x => x.id===id?m:x) }))));
    },
    async deleteMeeting(id){
      return withApi(() => window.api.deleteMeeting(id).then(() => setState(s => ({ ...s, meetings: s.meetings.filter(m => m.id !== id) }))));
    },
    skipMeetingDate(id, dateIso){
      const m = state.meetings.find(x => x.id === id);
      const next = [...new Set([...(m?.skippedDates||[]), dateIso])];
      return withApi(() => window.api.updateMeeting(id, { skippedDates: next }).then(mm => setState(s => ({ ...s, meetings: s.meetings.map(x => x.id===id?mm:x) }))));
    },
    unskipMeetingDate(id, dateIso){
      const m = state.meetings.find(x => x.id === id);
      const next = (m?.skippedDates||[]).filter(d => d !== dateIso);
      return withApi(() => window.api.updateMeeting(id, { skippedDates: next }).then(mm => setState(s => ({ ...s, meetings: s.meetings.map(x => x.id===id?mm:x) }))));
    },
  }), [state, online, apiUrl, apiBusy, apiError]);

  return <StoreCtx.Provider value={api}>{children}</StoreCtx.Provider>;
}
function useStore(){ return React.useContext(StoreCtx); }

// ---------- Date / time helpers ----------
const DAY_KEYS = ['sun','mon','tue','wed','thu','fri','sat'];
const DAY_LABELS = { sun:'Sun', mon:'Mon', tue:'Tue', wed:'Wed', thu:'Thu', fri:'Fri', sat:'Sat' };
const DAY_FULL = { sun:'Sunday', mon:'Monday', tue:'Tuesday', wed:'Wednesday', thu:'Thursday', fri:'Friday', sat:'Saturday' };
const WEEKDAYS = ['mon','tue','wed','thu','fri','sat'];

function fmtTime(t){
  if(!t) return '';
  const [h,m] = t.split(':').map(Number);
  const period = h < 12 ? 'AM' : 'PM';
  const h12 = ((h + 11) % 12) + 1;
  return `${h12}:${String(m).padStart(2,'0')} ${period}`;
}
function fmtDate(iso){
  if(!iso) return '';
  const d = new Date(iso + 'T00:00:00');
  return d.toLocaleDateString('en-IN', { weekday:'long', day:'numeric', month:'short' });
}
function fmtDayHeader(iso, now = new Date()){
  const d = new Date(iso + 'T00:00:00');
  const today = new Date(now); today.setHours(0,0,0,0);
  const diff = Math.round((d - today) / 86400000);
  let label = d.toLocaleDateString('en-IN', { weekday:'long' });
  if(diff === 0) label = 'Today';
  else if(diff === 1) label = 'Tomorrow';
  else if(diff === -1) label = 'Yesterday';
  const date = d.toLocaleDateString('en-IN', { day:'numeric', month:'long', year: d.getFullYear()!==today.getFullYear()?'numeric':undefined });
  return { label, date, diff };
}
function todayKey(){ return DAY_KEYS[new Date().getDay()]; }

// For weekly classes/meetings, return next N occurrences from now
function expandToOccurrences(item, daysAhead = 14, kind = 'class'){
  const out = [];
  const skipped = new Set(item.skippedDates || []);
  if(!item.schedule || item.schedule === 'once'){
    out.push({ ...item, occurrenceDate: item.date, kind });
    return out;
  }
  const start = new Date(); start.setHours(0,0,0,0);
  for(let i=0;i<daysAhead;i++){
    const d = new Date(start); d.setDate(d.getDate()+i);
    const k = DAY_KEYS[d.getDay()];
    if(item.days?.includes(k)){
      const iso = d.toISOString().slice(0,10);
      if(skipped.has(iso)) continue;
      out.push({ ...item, occurrenceDate: iso, kind });
    }
  }
  return out;
}
// Back-compat alias
function expandClassToOccurrences(cl, daysAhead = 14){
  return expandToOccurrences(cl, daysAhead, 'class');
}

function nowMin(){
  const d = new Date();
  return d.getHours()*60 + d.getMinutes();
}
function toMin(t){
  if(!t) return 0;
  const [h,m] = t.split(':').map(Number);
  return h*60+m;
}
function isLive(dateIso, startTime, endTime){
  if(!dateIso) return false;
  const today = new Date().toISOString().slice(0,10);
  if(dateIso !== today) return false;
  const m = nowMin();
  return m >= toMin(startTime) && m <= toMin(endTime);
}
function isPast(dateIso, endTime){
  const now = new Date();
  const end = new Date(dateIso + 'T' + endTime + ':00');
  return end < now;
}

Object.assign(window, {
  StoreProvider, useStore, StoreCtx,
  DAY_KEYS, DAY_LABELS, DAY_FULL, WEEKDAYS,
  fmtTime, fmtDate, fmtDayHeader, todayKey,
  expandClassToOccurrences, expandToOccurrences, nowMin, toMin, isLive, isPast,
});
