/**
 * Zustand UI-state store for the calendar shell.
 *
 * Owns ONLY UI-shape state — no server data ever enters this store.
 * Server state (events, user profile) lives in TanStack Query.
 *
 * State contract (UI-SPEC §State Management Contract):
 *   selectedView    — persisted in localStorage keyed by breakpoint group
 *   selectedDate    — ISO string; NOT persisted
 *   openEventId     — null when popover is closed
 *   calendarRange   — { start, end } ISO date strings; drives TanStack Query key
 *
 * View default logic (D-05):
 *   phone (≤767px)         → 'month-agenda'
 *   tablet-desktop (≥768px) → 'month-grid'
 *
 * localStorage keys:
 *   calendarView.phone            — persisted view for phone breakpoint group
 *   calendarView.tablet-desktop   — persisted view for tablet-desktop
 *
 * Note: calendarRange is initialized to today ± buffer (current month ± 1 week)
 * so the first TanStack Query fetch uses a sensible window without depending on
 * onRangeUpdate firing on mount (A4/Open Q2 from RESEARCH.md).
 */

import { create } from 'zustand';
import { localDateISO } from '../lib/eventDateTime.js';

// ── Types ──────────────────────────────────────────────────────────────────

type BreakpointGroup = 'phone' | 'tablet-desktop';

export interface CalendarStore {
  selectedView: string;
  selectedDate: string;
  openEventId: string | null;
  calendarRange: { start: string; end: string };

  // ── EventForm UI state (Plan 03-05) ──────────────────────────────────────
  // Server state (events, calendars) lives in TanStack Query. These are pure
  // UI-shape keys: whether the form is open, what mode it is in, and which
  // event UID to pre-populate in edit mode.
  eventFormOpen: boolean;
  eventFormMode: 'create' | 'edit';
  eventFormUid: string | null;

  // ── Delete dialog + sync-state UI state (Plan 03-06) ─────────────────────
  // Drives DeleteConfirmationDialog visibility and SyncStateToast polling.
  deleteDialogOpen: boolean;
  deleteDialogUid: string | null;
  /** UID of the most recently enqueued write. SyncStateToast polls sync-status for this. */
  lastSyncedUid: string | null;

  // ── Session-expiry flag (Plan 06-05 / D-11) ─────────────────────────────
  // Set to true by the global QueryCache/MutationCache onError handler in main.tsx
  // when a SessionExpiredError is detected from any query or mutation.
  // Drives the "Session expired / Signing you back in…" interstitial in CalendarShell.
  sessionExpired: boolean;

  setSelectedView: (view: string) => void;
  setSelectedDate: (date: string) => void;
  setOpenEventId: (id: string | null) => void;
  setCalendarRange: (range: { start: string; end: string }) => void;

  /**
   * Open or close the EventForm.
   *
   * @param open   true to open, false to close
   * @param mode   'create' (default) or 'edit'
   * @param uid    UID of the event to pre-populate in edit mode (null otherwise)
   */
  setEventForm: (open: boolean, mode?: 'create' | 'edit', uid?: string | null) => void;

  /**
   * Open or close the DeleteConfirmationDialog.
   *
   * @param open true to open, false to close
   * @param uid  UID of the event to confirm-delete (null when closing)
   */
  setDeleteDialog: (open: boolean, uid?: string | null) => void;

  /**
   * Set the UID of the most recently enqueued write, driving SyncStateToast polling.
   * Pass null to dismiss the toast.
   */
  setLastSyncedUid: (uid: string | null) => void;

  /**
   * Arm the session-expiry interstitial (D-11).
   * Called imperatively from the global QueryCache/MutationCache onError handler
   * (outside React, via getState().setSessionExpired) when a SessionExpiredError
   * is caught from any query or mutation.
   */
  setSessionExpired: (expired: boolean) => void;
}

// ── Helpers ────────────────────────────────────────────────────────────────

/** Determine breakpoint group from current viewport width. */
function getBreakpointGroup(): BreakpointGroup {
  if (typeof window === 'undefined') return 'tablet-desktop';
  return window.matchMedia('(max-width: 767px)').matches ? 'phone' : 'tablet-desktop';
}

/** localStorage key for the persisted view of a breakpoint group. */
function viewStorageKey(group: BreakpointGroup): string {
  return `calendarView.${group}`;
}

/** Read the persisted view for the current breakpoint group. */
function readPersistedView(): string {
  const group = getBreakpointGroup();
  try {
    const stored = localStorage.getItem(viewStorageKey(group));
    if (stored) return stored;
  } catch {
    // localStorage may be unavailable (SSR, private mode)
  }
  // D-05 defaults: phone → 'month-agenda', tablet-desktop → 'month-grid'
  return group === 'phone' ? 'month-agenda' : 'month-grid';
}

/** Build the initial calendarRange: today ± ~1 week buffer (current month ± 7 days). */
function initialCalendarRange(): { start: string; end: string } {
  const today = new Date();
  const start = new Date(today.getFullYear(), today.getMonth(), 1);
  start.setDate(start.getDate() - 7);
  const end = new Date(today.getFullYear(), today.getMonth() + 1, 0);
  end.setDate(end.getDate() + 7);
  // WR-04: localDateISO uses local calendar accessors — NEVER toISOString().slice(0,10),
  // which returns the UTC date and (for the project's negative-offset zones after ~20:00
  // local) seeds the window/selectedDate on the wrong day.
  return {
    start: localDateISO(start),
    end: localDateISO(end),
  };
}

/** Today as an ISO date string (YYYY-MM-DD). Exported for use in EventForm (IN-03). */
export function todayIso(): string {
  // WR-04: local-date accessor, not the banned UTC slice.
  return localDateISO(new Date());
}

// ── Store ──────────────────────────────────────────────────────────────────

export const useCalendarStore = create<CalendarStore>((set) => ({
  selectedView: readPersistedView(),
  selectedDate: todayIso(),
  openEventId: null,
  calendarRange: initialCalendarRange(),

  // EventForm UI state — closed by default, create mode, no pre-fill UID
  eventFormOpen: false,
  eventFormMode: 'create',
  eventFormUid: null,

  // Delete dialog + sync-state UI state — closed/null by default
  deleteDialogOpen: false,
  deleteDialogUid: null,
  lastSyncedUid: null,

  // Session-expiry flag — false by default; set by global QueryCache/MutationCache handler
  sessionExpired: false,

  setSelectedView: (view: string) => {
    set({ selectedView: view });
    // Persist to localStorage keyed by breakpoint group
    const group = getBreakpointGroup();
    try {
      localStorage.setItem(viewStorageKey(group), view);
    } catch {
      // Ignore write failures
    }
  },

  setSelectedDate: (date: string) => set({ selectedDate: date }),

  setOpenEventId: (id: string | null) => set({ openEventId: id }),

  setCalendarRange: (range: { start: string; end: string }) => set({ calendarRange: range }),

  setEventForm: (open: boolean, mode: 'create' | 'edit' = 'create', uid: string | null = null) =>
    set({ eventFormOpen: open, eventFormMode: mode, eventFormUid: uid }),

  setDeleteDialog: (open: boolean, uid: string | null = null) =>
    set({ deleteDialogOpen: open, deleteDialogUid: uid }),

  setLastSyncedUid: (uid: string | null) => set({ lastSyncedUid: uid }),

  setSessionExpired: (expired: boolean) => set({ sessionExpired: expired }),
}));
