/**
 * Composition wireframe — a read-only schematic of the active spec's multi-view
 * structure (docs/architecture/08 → editor augmentation). Bare nested boxes for
 * `layer`/`hconcat`/`vconcat`/`concat`/`facet`/`repeat` down to the unit views; no
 * labels in the boxes (the caption names the hovered/selected one). Clicking a box
 * selects it and reveals that view's source range in the editor
 * (`AppStore.requestRevealView`) — the editor stays the source of truth.
 *
 * A disclosure popover (`usePopover`) off a glyph in the preview toolbar; the panel
 * is the WAI-ARIA APG **tree** widget — `tree`/`treeitem`/`group`, single-select,
 * roving tabindex, arrow-key nav in logical (document) order (arch 10 §5). Phase A
 * is read-only navigation; drag-reorder and resize land on this same tree.
 */

import {
  useCallback,
  useEffect,
  useMemo,
  useState,
  type KeyboardEvent,
  type ReactNode,
} from 'react';
import { createPortal } from 'react-dom';
import type { SpecPath } from '@core/spec-insert';
import { viewTree, type Orientation, type ViewNode } from '@core/spec-view-tree';
import { usePopover } from '../hooks/usePopover';
import { useAppStore } from '../stores/AppStore';
import { selectShownText, useSnippetStore } from '../stores/SnippetStore';
import { Icon } from './Icon';
import { IconButton } from './IconButton';
import styles from './CompositionWireframe.module.css';

const POPOVER_ID = 'composition-wireframe';
const INITIAL_FOCUS = ['[role="treeitem"]'] as const;

/** A DOM-safe, unique key for a node from its path. */
const keyOf = (path: SpecPath): string => (path.length ? path.join('|') : 'root');

/** A readable path like `vconcat[1].hconcat[0]` for the caption. */
function pathLabel(path: SpecPath): string {
  if (path.length === 0) return 'root';
  let out = '';
  for (const seg of path) out += typeof seg === 'number' ? `[${seg}]` : out ? `.${seg}` : seg;
  return out;
}

const descriptor = (n: ViewNode): string =>
  n.kind === 'unit' ? (n.mark ? `${n.mark} view` : 'view') : `${n.op} · ${n.children.length} views`;

const ariaLabelOf = (n: ViewNode): string =>
  n.kind === 'unit'
    ? n.mark
      ? `${n.mark} view`
      : 'view'
    : `${n.op}, ${n.orientation}, ${n.children.length} views`;

// TODO (deferred polish — docs/exploration/visual-composition-editing-exploration.md §6a):
// (1) render a simplified mark-type glyph inside each leaf box for at-a-glance ID;
// (2) replace the offset-rectangle `layered` look with a "stacked planes" primitive
//     (overlapping sheets/disks, like the database glyph) to read as one shared space.

/** Children layout class per orientation (layered overlaps in one grid cell). */
const LAYOUT: Record<Orientation, string> = {
  horizontal: styles.row,
  vertical: styles.col,
  grid: styles.grid,
  layered: styles.layered,
};

interface Flat {
  node: ViewNode;
  key: string;
  parentKey: string | null;
}

/** Pre-order flatten — drives roving keyboard nav (next/prev/parent/first-child). */
function flatten(root: ViewNode): Flat[] {
  const out: Flat[] = [];
  const walk = (n: ViewNode, parentKey: string | null) => {
    const key = keyOf(n.path);
    out.push({ node: n, key, parentKey });
    for (const c of n.children) walk(c, key);
  };
  walk(root, null);
  return out;
}

function WireframeTree({ tree }: { tree: ViewNode }) {
  const requestRevealView = useAppStore((s) => s.requestRevealView);
  const flat = useMemo(() => flatten(tree), [tree]);
  const rootKey = keyOf(tree.path);

  const [selectedKey, setSelectedKey] = useState<string | null>(null);
  const [focusedKey, setFocusedKey] = useState<string | null>(null);
  const [hoverKey, setHoverKey] = useState<string | null>(null);
  const effectiveFocus =
    (focusedKey && flat.some((f) => f.key === focusedKey) && focusedKey) || rootKey;

  const select = useCallback(
    (n: ViewNode) => {
      setSelectedKey(keyOf(n.path));
      setFocusedKey(keyOf(n.path));
      requestRevealView(n.offset, n.length);
    },
    [requestRevealView],
  );

  const onKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
    const i = flat.findIndex((f) => f.key === effectiveFocus);
    if (i < 0) return;
    const moveTo = (j: number) => {
      const target = flat[j];
      if (!target) return;
      e.preventDefault();
      setFocusedKey(target.key);
      e.currentTarget.querySelector<HTMLElement>(`[data-key="${target.key}"]`)?.focus();
    };
    switch (e.key) {
      case 'ArrowDown':
        return moveTo(i + 1);
      case 'ArrowUp':
        return moveTo(i - 1);
      case 'Home':
        return moveTo(0);
      case 'End':
        return moveTo(flat.length - 1);
      case 'ArrowRight': // first child is the next node in pre-order
        return flat[i].node.children.length ? moveTo(i + 1) : undefined;
      case 'ArrowLeft': {
        const pk = flat[i].parentKey;
        return pk ? moveTo(flat.findIndex((f) => f.key === pk)) : undefined;
      }
      case 'Enter':
      case ' ':
        e.preventDefault();
        return select(flat[i].node);
    }
  };

  const renderNode = (n: ViewNode): ReactNode => {
    const key = keyOf(n.path);
    const container = n.kind === 'composition';
    const generated = n.op === 'facet' || n.op === 'repeat';
    const cls = [
      styles.node,
      container ? styles.container : styles.leaf,
      key === selectedKey ? styles.selected : '',
      generated ? styles.generated : '',
    ]
      .filter(Boolean)
      .join(' ');
    return (
      <div
        key={key}
        data-key={key}
        role="treeitem"
        aria-label={ariaLabelOf(n)}
        aria-selected={key === selectedKey}
        aria-expanded={container ? true : undefined}
        tabIndex={key === effectiveFocus ? 0 : -1}
        className={cls}
        onClick={(e) => {
          e.stopPropagation();
          select(n);
        }}
        onMouseEnter={(e) => {
          e.stopPropagation();
          setHoverKey(key);
        }}
        onMouseLeave={() => setHoverKey(null)}
        onFocus={(e) => {
          e.stopPropagation();
          setFocusedKey(key);
        }}
      >
        {container && (
          <div
            role="group"
            className={`${styles.children} ${n.orientation ? LAYOUT[n.orientation] : ''}`}
          >
            {n.children.map(renderNode)}
          </div>
        )}
      </div>
    );
  };

  const captionNode =
    (hoverKey ?? selectedKey) ? flat.find((f) => f.key === (hoverKey ?? selectedKey))?.node : null;

  return (
    <>
      <div role="tree" aria-label="View composition" className={styles.tree} onKeyDown={onKeyDown}>
        {renderNode(tree)}
      </div>
      <p className={styles.caption} aria-hidden="true">
        {captionNode ? (
          <>
            <code>{pathLabel(captionNode.path)}</code> — {descriptor(captionNode)}
          </>
        ) : (
          <span className={styles.muted}>Hover a block to identify it · click to reveal it</span>
        )}
      </p>
    </>
  );
}

export function CompositionWireframe() {
  const { open, toggle, close, triggerRef, setPopNode } = usePopover({
    id: POPOVER_ID,
    align: 'right',
    flip: true,
    initialFocus: INITIAL_FOCUS,
  });
  const shownText = useSnippetStore(selectShownText);
  const tree = useMemo(() => viewTree(shownText), [shownText]);
  const hasComposition = !!tree && tree.kind === 'composition';

  // The wireframe is meaningless for a single-view spec — hide the affordance, and
  // close it if it was open when the composition is unwrapped away.
  useEffect(() => {
    if (!hasComposition && open) close();
  }, [hasComposition, open, close]);

  if (!tree || tree.kind !== 'composition') return null;

  return (
    <div className={styles.wrap}>
      <IconButton
        ref={triggerRef}
        label="View composition structure"
        aria-expanded={open}
        aria-controls={POPOVER_ID}
        onClick={toggle}
      >
        <Icon name="structure" />
      </IconButton>
      {open &&
        createPortal(
          <div
            ref={setPopNode}
            id={POPOVER_ID}
            className={styles.pop}
            role="group"
            aria-label="Composition structure"
          >
            <h4 className={styles.title}>Structure</h4>
            <WireframeTree tree={tree} />
          </div>,
          document.body,
        )}
    </div>
  );
}
