'use client';

import {useSyncExternalStore} from 'react';

/**
 * Shell breakpoints.
 *
 * These are LAYOUT decisions — they choose whether Loyaly AI is an inline
 * panel or a sheet, whether a table renders as rows or cards. Those are React
 * tree differences that CSS cannot express.
 *
 * Anything that CAN be done in CSS should be: use Grid's responsive `columns`
 * or a Tailwind breakpoint instead of reaching for this hook. Every consumer
 * here costs a client-side re-render on resize.
 */
export type Breakpoint =
  | 'mobile'
  | 'tablet'
  | 'laptop'
  | 'desktop'
  | 'ultrawide';

/**
 * Ordered widest-first — `read()` returns the first match.
 *
 *   mobile     <640    phones
 *   tablet     640–1024
 *   laptop     1024–1440
 *   desktop    1440–1920
 *   ultrawide  >1920   content gets capped rather than stretched
 */
export const BREAKPOINT_MIN = {
  ultrawide: 1920,
  desktop: 1440,
  laptop: 1024,
  tablet: 640,
  mobile: 0,
} as const;

const QUERIES: [Breakpoint, string][] = [
  ['ultrawide', `(min-width: ${BREAKPOINT_MIN.ultrawide}px)`],
  ['desktop', `(min-width: ${BREAKPOINT_MIN.desktop}px)`],
  ['laptop', `(min-width: ${BREAKPOINT_MIN.laptop}px)`],
  ['tablet', `(min-width: ${BREAKPOINT_MIN.tablet}px)`],
];

function read(): Breakpoint {
  for (const [name, q] of QUERIES) {
    if (window.matchMedia(q).matches) return name;
  }
  return 'mobile';
}

function subscribe(onChange: () => void) {
  const mqls = QUERIES.map(([, q]) => window.matchMedia(q));
  mqls.forEach((m) => m.addEventListener('change', onChange));
  return () => mqls.forEach((m) => m.removeEventListener('change', onChange));
}

/**
 * SSR resolves to 'desktop'. The shell is flex-based, so when a narrow client
 * corrects on the first frame the only visible change is the Loyaly AI panel
 * dropping out — no reflow of the content column.
 */
export function useBreakpoint(): Breakpoint {
  return useSyncExternalStore(subscribe, read, () => 'desktop');
}

/**
 * True when the side nav renders as an inline rail. Below this AppShell moves
 * it into the drawer, which is also the point where the workspace shell has to
 * put the top bar back at the shell root — see (workspace)/layout.tsx.
 *
 * Must stay in sync with AppShell's own `mobileNav.breakpoint`, which is
 * 'sm' (640px) — the same line as the mobile/tablet boundary.
 */
export function isSideNavInline(bp: Breakpoint): boolean {
  return bp !== 'mobile';
}

/**
 * True when Loyaly AI renders as a third column.
 *
 * Tablet is excluded deliberately: at 640–1024 a 320px panel leaves under
 * 400px of workspace once the rail is out, which is narrower than a single
 * chart needs. There it becomes a sheet, same as on mobile.
 */
export function isPanelInline(bp: Breakpoint): boolean {
  return bp === 'laptop' || bp === 'desktop' || bp === 'ultrawide';
}

/** Loyaly AI panel width. Laptop trades panel width for workspace. */
export function assistantWidth(bp: Breakpoint): number {
  if (bp === 'ultrawide') return 420;
  if (bp === 'desktop') return 380;
  return 320;
}

/**
 * Columns for a four-up metric row.
 *
 * Four KPIs must lay out 4-up, 2×2, or stacked — never 3+1, which reads as a
 * broken grid. Grid's `repeat: 'fit'` cannot express that: at a ~800px
 * workspace it yields exactly three tracks whatever minWidth you choose, and
 * the fourth card drops to an orphan row. So the count is computed.
 */
export function metricColumns(bp: Breakpoint, isPanelOpen: boolean): number {
  if (bp === 'mobile') return 1;
  if (bp === 'tablet') return 2;
  if (bp === 'laptop') return 2;
  // Desktop and ultrawide fit four unless Loyaly AI is taking 380–420px.
  return isPanelInline(bp) && isPanelOpen ? 2 : 4;
}

/**
 * Max content width above `desktop`.
 *
 * On a 2560px monitor an uncapped workspace stretches a 30-day line chart
 * across ~1900px, which flattens every trend it is supposed to show, and runs
 * body text past the ~90ch where reading breaks down. Capping and centring
 * costs nothing below the cap.
 */
export function contentMaxWidth(bp: Breakpoint): number | undefined {
  return bp === 'ultrawide' ? 1600 : undefined;
}
