import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useEditor } from '@craftjs/core';
import { useNodeActions } from '../../hooks/useNodeActions';
import { useMobileChrome } from '../../state/MobileChromeContext';

/**
 * Phase B mobile selection toolbar -- the on-canvas action bar that makes a
 * tap-selected node actually EDITABLE by touch (reorder/duplicate/delete/
 * select-parent/edit-styles), mirroring the desktop right-click ContextMenu.
 * Both share the exact same behavior via `useNodeActions` (item 1).
 *
 * Mounted ONCE (in `EditorShell`'s mobile branch), reading the current
 * selection from `useEditor` directly -- NOT per-node -- and positioned
 * bottom-fixed just above the tab bar (simplest, robust; a node-anchored
 * floating toolbar would have to constantly reposition as the canvas
 * scrolls, and risks being clipped at the viewport edge for a node near the
 * top or bottom of a tall page). This is a deliberate design call: see the
 * report for the alternative (floating over the node) that was passed over.
 *
 * Hidden whenever a bottom sheet is open (`activeSheet !== null`) -- the
 * sheet supersedes it, and the two must never visually stack.
 *
 * Delete is a two-tap in-app confirm (tap once -> the button becomes
 * "Confirm?" for a few seconds; tap again within that window commits the
 * delete; selecting something else cancels it) rather than a native
 * `confirm()`, matching the project's no-native-dialogs rule.
 */
const DELETE_CONFIRM_TIMEOUT_MS = 3000;

export const MobileSelectionToolbar: React.FC = () => {
  const { activeSheet, openSheet } = useMobileChrome();
  const { selectedId } = useEditor((state) => {
    const selected = state.events.selected;
    const id = selected && selected.size > 0 ? (Array.from(selected)[0] as string) : null;
    return { selectedId: id && id !== 'ROOT' ? id : null };
  });
  // Collector-free -- an always-live reference (same pattern as
  // GuidedStyles.tsx/BlocksPanel.tsx), used only for imperative DOM lookups
  // below (scroll-into-view), never for anything that needs to react to
  // store changes on its own.
  const { query } = useEditor();

  const nodeActions = useNodeActions(selectedId);
  const [confirmingDelete, setConfirmingDelete] = useState(false);
  const confirmTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // `useNodeActions` computes canMoveUp/canMoveDown/canDelete synchronously
  // from a live (never-cached) Craft.js query -- see that hook's doc comment
  // for why it deliberately does NOT hold its own store subscription to force
  // a refresh. Moving/duplicating/deleting via THIS toolbar changes the
  // selected node's position/siblings WITHOUT changing `selectedId` itself
  // (the node stays selected), so nothing else would otherwise trigger a
  // re-render of this component to pick up the new boundary flags after,
  // say, tapping "Move Up" until the node reaches the top. Bumping a plain
  // local tick from these buttons' own click handlers -- an ordinary,
  // locally-owned re-render -- covers exactly that, without the broader
  // store-wide subscription that was found to trip React's "setState while
  // rendering a different component" warning whenever some unrelated
  // container mounts (see useNodeActions.ts).
  const [, forceRefresh] = useState(0);
  const bumpTick = useCallback(() => forceRefresh((n) => n + 1), []);

  const clearConfirmTimeout = useCallback(() => {
    if (confirmTimeoutRef.current) {
      clearTimeout(confirmTimeoutRef.current);
      confirmTimeoutRef.current = null;
    }
  }, []);

  // Reset the two-tap delete confirmation whenever the selection changes
  // (including being cleared) so a stale "Confirm?" state never lingers onto
  // a different node.
  const prevSelectedRef = useRef(selectedId);
  useEffect(() => {
    if (prevSelectedRef.current !== selectedId) {
      prevSelectedRef.current = selectedId;
      clearConfirmTimeout();
      setConfirmingDelete(false);
    }
  }, [selectedId, clearConfirmTimeout]);

  useEffect(() => clearConfirmTimeout, [clearConfirmTimeout]);

  // Fast-follow item 3: the Styles bottom sheet covers the bottom ~65dvh of
  // the canvas, so a selected node sitting anywhere in that band goes
  // invisible the instant the sheet opens -- live style edits then have no
  // visible effect until the user closes the sheet to check. Whenever the
  // sheet becomes 'styles' (however it got opened -- currently only this
  // toolbar's own "Style" button, but this is keyed on the sheet/selection
  // state rather than the button tap so it stays correct if another entry
  // point is added later) scroll the selected node to the top of the
  // canvas's scroll area, into the sliver of screen the sheet leaves clear.
  useEffect(() => {
    if (activeSheet !== 'styles' || !selectedId) return;
    try {
      query.node(selectedId).get()?.dom?.scrollIntoView({ behavior: 'smooth', block: 'start' });
    } catch {
      // Best-effort -- not fatal if the node/DOM isn't found.
    }
  }, [activeSheet, selectedId, query]);

  const handleDeleteTap = useCallback(() => {
    if (confirmingDelete) {
      clearConfirmTimeout();
      setConfirmingDelete(false);
      nodeActions.deleteNode();
      bumpTick();
      return;
    }
    setConfirmingDelete(true);
    clearConfirmTimeout();
    confirmTimeoutRef.current = setTimeout(() => setConfirmingDelete(false), DELETE_CONFIRM_TIMEOUT_MS);
  }, [confirmingDelete, clearConfirmTimeout, nodeActions, bumpTick]);

  const handleEditStyles = useCallback(() => {
    openSheet('styles');
  }, [openSheet]);

  const handleMoveUp = useCallback(() => {
    nodeActions.moveUp();
    bumpTick();
  }, [nodeActions, bumpTick]);

  const handleMoveDown = useCallback(() => {
    nodeActions.moveDown();
    bumpTick();
  }, [nodeActions, bumpTick]);

  const handleDuplicate = useCallback(() => {
    const newId = nodeActions.duplicate();
    bumpTick();
    if (!newId) return;
    // Mirror BlocksPanel's tap-to-add pattern: the new node's real DOM
    // element isn't attached yet the instant `duplicate()` returns (Craft.js
    // mounts it asynchronously after this handler's synchronous state
    // update), so defer two animation frames before reading `.dom` off the
    // query, same as `handleTapToAdd` does.
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        try {
          query.node(newId).get()?.dom?.scrollIntoView({ behavior: 'smooth', block: 'center' });
        } catch {
          // Best-effort scroll -- not fatal if the node/DOM isn't found.
        }
      });
    });
  }, [nodeActions, bumpTick, query]);

  if (!selectedId || activeSheet !== null) return null;

  return (
    <div className="mobile-selection-toolbar" role="toolbar" aria-label="Selected element actions">
      <button
        type="button"
        className="mobile-selection-toolbar-btn"
        onClick={handleMoveUp}
        disabled={!nodeActions.canMoveUp}
        aria-label="Move up"
      >
        <i className="fa fa-arrow-up" aria-hidden="true" />
        <span>Up</span>
      </button>
      <button
        type="button"
        className="mobile-selection-toolbar-btn"
        onClick={handleMoveDown}
        disabled={!nodeActions.canMoveDown}
        aria-label="Move down"
      >
        <i className="fa fa-arrow-down" aria-hidden="true" />
        <span>Down</span>
      </button>
      <button
        type="button"
        className="mobile-selection-toolbar-btn"
        onClick={handleDuplicate}
        aria-label="Duplicate"
      >
        <i className="fa fa-clone" aria-hidden="true" />
        <span>Copy</span>
      </button>
      <button
        type="button"
        className="mobile-selection-toolbar-btn"
        onClick={nodeActions.selectParent}
        disabled={!nodeActions.canSelectParent}
        aria-label="Select parent"
      >
        <i className="fa fa-level-up" aria-hidden="true" />
        <span>Parent</span>
      </button>
      <button
        type="button"
        className="mobile-selection-toolbar-btn"
        onClick={handleEditStyles}
        aria-label="Edit styles"
      >
        <i className="fa fa-paint-brush" aria-hidden="true" />
        <span>Style</span>
      </button>
      <button
        type="button"
        className={`mobile-selection-toolbar-btn danger${confirmingDelete ? ' confirming' : ''}`}
        onClick={handleDeleteTap}
        disabled={!nodeActions.canDelete}
        aria-label={confirmingDelete ? 'Tap again to confirm delete' : 'Delete'}
      >
        <i className="fa fa-trash" aria-hidden="true" />
        <span>{confirmingDelete ? 'Confirm?' : 'Delete'}</span>
      </button>
    </div>
  );
};
