import { createContext, useContext } from 'react';

import type { DropPosition } from './layout-utils';
import type { PanelConfig } from './types';
import type { WorkspaceIdentity } from './workspace-identity';

export type WorkspaceContextValue = {
  /** Which dashboard or screen this panel is on. Null in a preview or a settings pane. */
  workspace: WorkspaceIdentity | null;
  cwd: string;
  root?: string;
  // Per-panel app settings, by panel id. A map rather than a getter so a config change re-renders the
  // panel that owns it — apps read this through `usePanelConfig`, not directly.
  panelConfigs: Record<string, PanelConfig>;
  setPanelConfig: (panelId: string, config: PanelConfig | undefined) => void;
  // Say what to do when this panel is deliberately closed. Apps call it through `usePanelClose`, never
  // directly. Registering twice for a panel id replaces the handler; nothing ever unregisters, because
  // "this component went away" is the signal we are specifically refusing to trust.
  registerPanelClose: (panelId: string, handler: () => void) => void;
  swapSourceId: string | null;
  setSwapSourceId: (id: string | null) => void;
  onSwap: (sourceId: string, targetId: string) => void;
  dragSourceId: string | null;
  setDragSourceId: (id: string | null) => void;
  onMove: (sourceId: string, targetId: string, position: DropPosition) => void;
  onSetZoom: (panelId: string, zoom: number) => void;
  /**
   * The one panel filling the window, if any. There is a single depth: it takes the whole browser
   * window, nav header included — the shell hides its own chrome for it (`panel-fullscreen.ts`).
   *
   * There used to be a shallower one that stopped below the nav header, driven by a green/amber pair of
   * traffic lights. It was only ever shallow because a panel cannot paint over the header from inside
   * the content region's stacking context, and once full screen worked the half-measure had no use.
   */
  fullscreenPanelId: string | null;
  setFullscreenPanelId: (id: string | null) => void;
  transitioningPanelId: string | null;
  isMobile: boolean;
  onMobileBack: (() => void) | null;
};

const noop = () => {};

/**
 * The interaction half of the context, switched off — every field a panel uses to rearrange the
 * workspace, plus the state those interactions run on. `WorkspaceLayout` renders a tree nobody can
 * rearrange (job detail panes, settings, dashboard previews), and it used to say so by repeating
 * eleven fields at the call site, which is a list you can silently fall behind: add a field to the
 * context and only one of the two providers gets it.
 *
 * `root` and `cwd` are deliberately not in here. They are facts about where the workspace *is*, not
 * interactions, and the inert renderer has no answer for them — its consumers only read `root` when
 * `cwd` is scoped, which it never is there.
 */
export const inertInteraction = {
  setPanelConfig: noop,
  registerPanelClose: noop,
  swapSourceId: null,
  setSwapSourceId: noop,
  onSwap: noop,
  dragSourceId: null,
  setDragSourceId: noop,
  onMove: noop,
  onSetZoom: noop,
  fullscreenPanelId: null,
  setFullscreenPanelId: noop,
  transitioningPanelId: null,
} satisfies Partial<WorkspaceContextValue>;

const WorkspaceContext = createContext<WorkspaceContextValue>({
  workspace: null,
  cwd: '~',
  panelConfigs: {},
  ...inertInteraction,
  isMobile: false,
  onMobileBack: null,
});

export const WorkspaceProvider = WorkspaceContext.Provider;

export const useWorkspace = () => useContext(WorkspaceContext);
