'use client';

import type { ReactNode } from 'react';

import type { TreeAppearance } from './data/appearance';

export type TreeItemId = string;

/** A single node in the consumer's tree data. Generic over your payload. */
export interface TreeNode<T = unknown> {
  id: TreeItemId;
  data: T;
  /** Inline children. Omit (and provide a `loadChildren`) for async loading. */
  children?: TreeNode<T>[];
  /**
   * Set to `true` to mark a node as a folder even when its `children` array
   * is empty (e.g. an unloaded async folder). Default: derived from
   * `Array.isArray(children)`.
   */
  isFolder?: boolean;
  /** Disable interaction. */
  disabled?: boolean;
}

export interface TreeLabels {
  loading: string;
  empty: string;
  error: string;
  searchPlaceholder: string;
  searchMatches: (count: number) => string;
  ariaLabel: string;
}

export const DEFAULT_TREE_LABELS: TreeLabels = {
  loading: 'Loading…',
  empty: 'Nothing to show',
  error: 'Failed to load',
  searchPlaceholder: 'Search…',
  searchMatches: (n) => `${n} match${n === 1 ? '' : 'es'}`,
  ariaLabel: 'Tree',
};

export type TreeSelectionMode = 'none' | 'single' | 'multiple';

/**
 * How a node becomes "activated" (i.e. opened) on pointer interaction.
 *
 * - `'single-click'` (default): single click activates a leaf immediately;
 *   double-click also activates. Folders always toggle on single click.
 * - `'double-click'`: single click only selects + focuses; double-click is
 *   required to activate. Mirrors classic file-manager behaviour.
 * - `'single-click-preview'`: VSCode Explorer / Cursor behaviour. Single
 *   click activates with `{ preview: true }` (consumer renders a preview
 *   tab); double-click activates with `{ preview: false }` (pinned tab).
 *
 * Folders ignore this setting — they always toggle on single click and
 * never call `onActivate`.
 */
export type TreeActivationMode =
  | 'single-click'
  | 'double-click'
  | 'single-click-preview';

export interface TreeActivateOptions {
  /**
   * `true` when the activation came from a single click in
   * `'single-click-preview'` mode. `false` for double-click and for
   * non-preview modes. Consumers typically map this to a
   * preview-tab vs pinned-tab distinction.
   */
  preview: boolean;
}

/**
 * Async loader: called the first time a folder is expanded with no inline
 * `children`. Result is cached; concurrent expansions are de-duplicated.
 */
export type TreeLoadChildren<T> = (
  node: TreeNode<T>,
) => Promise<TreeNode<T>[]>;

export interface TreeRowRenderProps<T> {
  node: TreeNode<T>;
  level: number;
  isSelected: boolean;
  isExpanded: boolean;
  isFocused: boolean;
  isFolder: boolean;
  isLoading: boolean;
  isMatchingSearch: boolean;
}

export type TreeRowSlot<T> = (props: TreeRowRenderProps<T>) => ReactNode;

export type TreeContextMenuSlot<T> = (
  props: TreeRowRenderProps<T>,
  trigger: ReactNode,
) => ReactNode;

export interface TreeRootProps<T> {
  /** Root nodes. Top-level items are rendered directly (no synthetic root). */
  data: TreeNode<T>[];
  /** Returns the human-readable name for a node (used by search/type-ahead). */
  getItemName: (node: TreeNode<T>) => string;

  /** Async loader for folders without inline `children`. */
  loadChildren?: TreeLoadChildren<T>;

  /** Selection behaviour. Default: `'single'`. */
  selectionMode?: TreeSelectionMode;
  /** Pointer activation behaviour. Default: `'single-click'`. */
  activationMode?: TreeActivationMode;
  /** Initially expanded ids. */
  initialExpandedIds?: TreeItemId[];
  /** Initially selected ids. */
  initialSelectedIds?: TreeItemId[];
  /** Pixels of indent per nesting level. Default: 16. (Shortcut for `appearance.indent`.) */
  indent?: number;
  /** Cosmetic configuration: density, sizes, accent intensity, radius. */
  appearance?: TreeAppearance;

  /** Triggered when selection changes. */
  onSelectionChange?: (selectedIds: TreeItemId[]) => void;
  /** Triggered when expanded set changes. */
  onExpansionChange?: (expandedIds: TreeItemId[]) => void;
  /**
   * Triggered when a leaf is activated (Enter / dblclick / click depending
   * on `activationMode`). Folders never call this — they toggle instead.
   */
  onActivate?: (node: TreeNode<T>, opts: TreeActivateOptions) => void;

  /** Show built-in search input. Default: false. */
  enableSearch?: boolean;
  /** Type printable letters to jump to a matching name. Default: true. */
  enableTypeAhead?: boolean;
  /** Render vertical indent guides under expanded folders. Default: false. */
  showIndentGuides?: boolean;

  /** Custom row renderer. Falls back to the default <TreeRow />. */
  renderRow?: TreeRowSlot<T>;
  /** Replace default folder/file icon. */
  renderIcon?: TreeRowSlot<T>;
  /** Replace default label rendering. */
  renderLabel?: TreeRowSlot<T>;
  /** Right-side actions slot (per row). */
  renderActions?: TreeRowSlot<T>;
  /** Wrap each row in a context menu (right-click). Receives the row meta + trigger element. */
  renderContextMenu?: TreeContextMenuSlot<T>;

  /** Override built-in copy in your locale. */
  labels?: Partial<TreeLabels>;
  /** Persist expanded + (optional) selected ids in localStorage under this key. */
  persistKey?: string;
  /** Persist selection alongside expansion. Default: false. */
  persistSelection?: boolean;

  className?: string;
  style?: React.CSSProperties;
}

/** Internal flat-row representation used by the renderer + keyboard nav. */
export interface FlatRow<T> {
  node: TreeNode<T>;
  level: number;
  parentId: TreeItemId | null;
  isFolder: boolean;
  isExpanded: boolean;
  isLoading: boolean;
  hasError: boolean;
}
