/**
 * Extract-to-Dataset as an editor action (spec §03F; docs/architecture/08 →
 * editor augmentation) — the data-facing counterpart to the wrap/config actions.
 *
 * Resolves the embedded data of the **view the cursor sits in** (the nearest
 * enclosing `data` binding, honoring Vega-Lite's parent→child inheritance), seeds
 * the Extract modal with it, and opens the modal. The cursor picks which view to
 * extract; a single-view spec resolves to the root binding from any cursor, so the
 * common case needs no thought. Two binding shapes are liftable — a view's inline
 * `data.values`, and a `{ name }` reference to the spec's own top-level `datasets`
 * (the data lives in the `datasets` map; we pre-fill the modal with its name).
 *
 * The toolbar offers the action whenever *some* view has extractable data
 * (`hasExtractableData`); when the focused view itself has none — it references a
 * library dataset, a url, a generator — there is nothing to lift here, so guide the
 * user to a view that does rather than silently extract the wrong one.
 */

import type * as monaco from 'monaco-editor/esm/vs/editor/edcore.main';
import { pathAtOffset } from '@core/spec-cursor';
import { dataBindingAtPath } from '@core/spec-data';
import { inlineValuesOf, selfDefinedPayloadOf } from '@core/spec-inline-data';
import { openModal } from '../modals/ModalCoordinator';
import { type ExtractTarget, useExtractStore } from '../stores/ExtractStore';
import { notify } from '../stores/NotificationStore';

// TODO(ux-second-pass): toolbar-only — no F1-palette accelerator like the sibling
// wrap/config actions, since this opens a modal rather than an in-place edit.
/** Open Extract-to-Dataset scoped to the view at the cursor. */
export function runExtract(editor: monaco.editor.IStandaloneCodeEditor): void {
  const model = editor.getModel();
  if (!model) return;
  const text = model.getValue();

  let spec: unknown;
  try {
    spec = JSON.parse(text);
  } catch {
    notify({
      kind: 'info',
      title: 'Can’t extract yet',
      message: 'Fix the JSON so the spec parses, then extract.',
    });
    return;
  }

  const position = editor.getPosition();
  const offset = position ? model.getOffsetAt(position) : 0;
  const binding = dataBindingAtPath(spec, pathAtOffset(text, offset));

  // Inline `values` → rewrite this view's data block; a self-defined `datasets`
  // reference → lift the named entry and pre-fill the modal with that name.
  const inline = binding && inlineValuesOf(binding.data);
  const selfDefined = binding && !inline && selfDefinedPayloadOf(spec, binding.data);
  let seed: {
    source: ReturnType<typeof inlineValuesOf>;
    target: ExtractTarget;
    name?: string;
  } | null = null;
  if (binding && inline) {
    seed = { source: inline, target: { kind: 'inline', anchorPath: binding.anchorPath } };
  } else if (binding && selfDefined) {
    const datasetName = (binding.data as { name: string }).name;
    seed = {
      source: selfDefined,
      target: { kind: 'self-defined', datasetName },
      name: datasetName,
    };
  }

  if (!seed || !seed.source) {
    notify({
      kind: 'info',
      title: 'No data to extract here',
      message: 'Place the cursor in a view with inline or embedded data to extract it.',
    });
    return;
  }

  useExtractStore.getState().begin({ source: seed.source, target: seed.target, name: seed.name });
  openModal('extract');
}
