/**
 * The dialog layer — transient UI contexts hosted by the layer-3 shell.
 *
 * A dialog is an alternate view of a stack (`docs/bgm/interactions.md` §4):
 * the deck is "lifted" off the table into the dialog (it stays on its path),
 * shown in order with an insertion cursor. The cursor *is* the index:
 * `move(id, path, index)`'s index is discovered by scrolling the visible deck.
 *
 * Opening/closing a dialog never issues a command and never touches the game
 * state — it is pure UI. Only its action buttons issue commands.
 */
import { useMemo, useState } from 'react';
import type { Package } from '@tts/bgm';
import { useInteractionStore } from './interactions.js';
import { useTabletopStore, childrenByPath } from './state.js';

/**
 * Render the topmost dialog on the interaction store's stack as an HTML
 * overlay. Renders nothing when the stack is empty. The dialog shows the
 * stack at its path in order, with an insertion cursor the player scrolls;
 * the insert button issues a `move` of the held part (or the top of the
 * stack) to that path at the cursor.
 */
export function DialogLayer({ pkg }: { pkg: Package }) {
  const dialogs = useInteractionStore((s) => s.dialogs);
  const top = dialogs[dialogs.length - 1];
  if (!top) return null;

  const dialog = pkg.dialogs.get(top.id);
  if (!dialog) return null;

  return (
    <StackDialog
      key={top.path}
      pkg={pkg}
      title={dialog.title}
      body={dialog.body}
      path={top.path}
    />
  );
}

/** The stack-inspector dialog: a lifted, ordered view of a path's stack. */
function StackDialog({
  pkg,
  title,
  body,
  path,
}: {
  pkg: Package;
  title?: string;
  body?: string;
  path: string;
}) {
  const parts = useTabletopStore((s) => s.parts);
  const movePart = useTabletopStore((s) => s.movePart);
  const held = useInteractionStore((s) => s.held);
  const drop = useInteractionStore((s) => s.drop);
  const popDialog = useInteractionStore((s) => s.popDialog);

  // The stack at `path`, in order.
  const stack = useMemo(() => childrenByPath(parts)[path] ?? [], [parts, path]);
  const [cursor, setCursor] = useState(stack.length);

  // The part to insert: the held part, or the top of the stack when none is
  // held (a "look at the top" / reorder use).
  const source = held?.id;

  const insert = () => {
    if (!source) return;
    movePart(source, path, cursor);
    drop();
    popDialog();
  };

  return (
    <div className="pointer-events-auto absolute inset-0 z-20 flex items-center justify-center bg-zinc-950/60">
      <div className="w-full max-w-md rounded-lg border border-zinc-700 bg-zinc-900 p-4 shadow-xl">
        {title && <h2 className="text-lg font-semibold">{title}</h2>}
        {body && <p className="mt-1 text-sm text-zinc-400">{body}</p>}

        <div className="mt-3 flex flex-wrap items-center gap-1 rounded-md border border-zinc-800 bg-zinc-950 p-2">
          {stack.length === 0 && (
            <span className="text-xs text-zinc-500">Empty stack</span>
          )}
          {stack.map((id, i) => (
            <div key={id} className="flex items-center gap-1">
              {i === cursor && <Cursor />}
              <button
                onClick={() => setCursor(i)}
                className="rounded border border-zinc-700 bg-zinc-800 px-2 py-1 text-xs text-zinc-200 hover:bg-zinc-700"
                title={`Insert before ${label(pkg, id)}`}
              >
                {label(pkg, id)}
              </button>
            </div>
          ))}
          {stack.length > 0 && cursor === stack.length && <Cursor />}
        </div>

        <div className="mt-4 flex justify-end gap-2">
          <button
            onClick={popDialog}
            className="rounded-md border border-zinc-700 px-3 py-1.5 text-sm text-zinc-300 hover:bg-zinc-800"
          >
            Close
          </button>
          <button
            onClick={insert}
            disabled={!source}
            className="rounded-md bg-zinc-100 px-3 py-1.5 text-sm font-medium text-zinc-900 hover:bg-white disabled:opacity-40"
          >
            {source ? `Insert ${label(pkg, source)}` : 'Insert'}
          </button>
        </div>
      </div>
    </div>
  );
}

/** A small marker between cards showing the insertion point. */
function Cursor() {
  return (
    <span className="h-6 w-0.5 rounded bg-zinc-100" title="Insertion point" />
  );
}

/** A short label for a part id (`package:type#id` → `type#id`). */
function label(pkg: Package, id: string): string {
  const key = id.split(':').slice(1).join(':');
  const part = pkg.parts.get(key);
  return part?.id ?? key;
}
