import type { FunctionalComponent } from 'preact';
import { useT } from '../../hooks/useT';
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { computeAnchoredPosition, autoUpdateAnchoredPosition, type VirtualElementLike } from '../../core/positioning';

import { getRenderEngineSelectedObjects, type AsukaObjectSummary, type AsukaRenderEngine } from '../../core/renderEngine';
import { ensureModuleAvailable, getModule, useModuleRegistryVersion, type AsukaModuleContext } from '../../core/modules';
import { useAsukaStore } from '../../store/asukaStore';
import { DEFAULT_PERMISSIONS, canOpenSelectionModuleId } from '../../core/permissions';
import { OverlayHost } from '../overlays/OverlayHost';
import { AsukaIcon } from '../core/AsukaIcon';
import { getVanillaUiHelpers } from '../../core/moduleUi';
import { getObjectFixedSlotConstraints } from '../../core/fixedSlotConstraints';
interface SelectionBox {
  left: number;
  top: number;
  width: number;
  height: number;
}

interface SelectionToolbarProps {
  box: SelectionBox | null;
  visible: boolean;
  engine?: AsukaRenderEngine | null;
}

/**
 * Contextual selection menu (F7).
 *
 * - Tracks the Fabric bounding box (virtual element)
 * - Rendered only when an object is selected
 * - Contenu configurable via Zustand + API (layout.selectionMenu.*)
 * - Internal stack navigation: menu -> module -> back
 */
export const SelectionToolbar: FunctionalComponent<SelectionToolbarProps> = ({
  box,
  visible,
  engine = null,
}) => {
  const t = useT();
  // Register placeholders so items like "history" always resolve.

  const floatingRef = useRef<HTMLDivElement | null>(null);
  const [tracked3DBox, setTracked3DBox] = useState<SelectionBox | null>(null);

const [isInlineEditing, setIsInlineEditing] = useState(false);
const [inlineText, setInlineText] = useState('');
const inlineRef = useRef<HTMLDivElement | null>(null);
const inlineInputRef = useRef<HTMLInputElement | null>(null);
const canvas2d = engine?.canvas2d ?? null;

  useEffect(() => {
    if (engine?.kind !== '3d') {
      setTracked3DBox(null);
      return;
    }
    setTracked3DBox(box);
  }, [box, engine?.kind]);

  useEffect(() => {
    if (!visible || engine?.kind !== '3d') return;
    let disposed = false;
    let frame = 0;
    const sameBox = (left: SelectionBox | null, right: SelectionBox | null) => {
      if (!left || !right) return left === right;
      return Math.abs(left.left - right.left) < 0.5
        && Math.abs(left.top - right.top) < 0.5
        && Math.abs(left.width - right.width) < 0.5
        && Math.abs(left.height - right.height) < 0.5;
    };
    const update = () => {
      if (disposed) return;
      const next = engine.selection.getAnchorBox();
      if (next) {
        const normalized = {
          left: Number(next.left) || 0,
          top: Number(next.top) || 0,
          width: Math.max(1, Number(next.width) || 1),
          height: Math.max(1, Number(next.height) || 1),
        };
        setTracked3DBox((current) => sameBox(current, normalized) ? current : normalized);
      }
      frame = window.requestAnimationFrame(update);
    };
    frame = window.requestAnimationFrame(update);
    return () => {
      disposed = true;
      window.cancelAnimationFrame(frame);
    };
  }, [visible, engine]);

  const activeBox = engine?.kind === '3d' ? (tracked3DBox ?? box) : box;

const InlineEditor: FunctionalComponent = () => {
  if (!isInlineEditing) return null as any;

  const onDone = () => {
    if (canvas2d) void canvas2d.updateSelectedTextCurve({ text: inlineText }, { commit: true, source: 'inline-text-editor' }).catch(() => {});
    setIsInlineEditing(false);
  };

  return (
    <div
      ref={inlineRef}
      class="ask-z-[9999] ask-bg-background ask-border ask-border-border ask-rounded-lg ask-shadow-lg ask-p-2 ask-flex ask-items-center ask-gap-2"
    >
      <input
        ref={inlineInputRef as any}
        class="ask-theme-input ask-h-9 ask-w-[220px] ask-rounded-lg ask-border ask-px-3 ask-text-sm"
        value={inlineText}
        onInput={(e) => setInlineText((e.currentTarget as HTMLInputElement).value)}
        onKeyDown={(e) => {
          if (e.key === 'Enter') onDone();
          if (e.key === 'Escape') setIsInlineEditing(false);
        }}
      />
      <button
        class="ask-text-xs ask-px-2 ask-py-1 ask-rounded-md ask-border ask-border-border"
        onClick={onDone}
      >{t('ui.ok')}</button>
    </div>
  );
};


  const type = useAsukaStore((s) => s.selectionMenuType);
  const permissions = useAsukaStore((s: any) => s.permissions ?? DEFAULT_PERMISSIONS);
  const items = useAsukaStore((s) => s.selectionMenuItems);
  const isOpen = useAsukaStore((s) => s.selectionMenuOpen);
  const stack = useAsukaStore((s) => s.selectionMenuStack);
  const close = useAsukaStore((s) => s.closeSelectionMenu);
  const goTo = useAsukaStore((s) => s.selectionMenuGoTo);
  const openModule = useAsukaStore((s) => s.openModule);
  const back = useAsukaStore((s) => s.selectionMenuBack);
  const resetNav = useAsukaStore((s) => s.resetSelectionMenuNav);
  const setModuleOverlayAnchorBox = useAsukaStore((s) => s.setModuleOverlayAnchorBox);
  const selectionMenuAnchorBox = useAsukaStore((s) => (s as any).selectionMenuAnchorBox);
  const setSelectionMenuAnchorBox = useAsukaStore((s) => (s as any).setSelectionMenuAnchorBox);
  const setPendingRestoreSelectionMenu = useAsukaStore((s) => (s as any).setPendingRestoreSelectionMenu);

  // If selection truly disappears, reset navigation so the next selection starts clean.
  // Do not reset during transient geometry updates caused by programmatic object edits
  // from modules like Transform, otherwise the open module closes on each change.
  useEffect(() => {
    if (visible && activeBox) return;
    const hasActiveObject = getRenderEngineSelectedObjects(engine).length > 0;
    if (hasActiveObject) return;
    resetNav();
    setSelectionMenuAnchorBox(null);
    setModuleOverlayAnchorBox(null);
  }, [visible, activeBox, engine, resetNav, setModuleOverlayAnchorBox, setSelectionMenuAnchorBox]);

  // Share the current selection rect with generic popover anchoring and keep selection modules synced.
  useEffect(() => {
    if (!visible || !activeBox) return;
    setModuleOverlayAnchorBox(activeBox);
  }, [visible, activeBox, setModuleOverlayAnchorBox]);

  const activeModuleId = stack.length > 0 ? stack[stack.length - 1] : null;
  const moduleRegistryVersion = useModuleRegistryVersion();

  useEffect(() => {
    if (!activeModuleId) return;
    void ensureModuleAvailable(activeModuleId as any);
  }, [activeModuleId]);

  const activeDef = useMemo(() => {
    void moduleRegistryVersion;
    return getModule(activeModuleId);
  }, [activeModuleId, moduleRegistryVersion]);

  // Module popovers opened from the selection menu must be anchored to the selected
  // object itself, not to the previous toolbar position. This lets the popover flip
  // above/beside the object when there is not enough room below.
  const liveSelectionModuleAnchorBox = activeBox
    ? {
      left: activeBox.left,
      top: activeBox.top,
      width: Math.max(1, activeBox.width),
      height: Math.max(1, activeBox.height),
    }
    : null;

  const context: AsukaModuleContext = useMemo(
    () => ({
      getEngine: () => engine,
      ui: getVanillaUiHelpers(),
    }),
    [engine],
  );

  useEffect(() => {
    if (!visible || !activeBox) return;
    if (!isOpen) return;
    if (!floatingRef.current) return;

    const virtualReference: VirtualElementLike = {
      getBoundingClientRect: () => new DOMRect(activeBox.left, activeBox.top, activeBox.width, activeBox.height),
    };

    const floatingEl = floatingRef.current;
    let cancelled = false;

    const updatePosition = () => {
      if (!floatingEl) return;

      // IMPORTANT: Floating UI expects a "placement" keyword (e.g. "bottom"), not CSS classes.
      // We always place the selection UI under the selected object (Figma-like).
      const placement = 'bottom';
      // Keep a safe distance from the object's bounding box (Figma-like).
      // User-requested: +20px minimum, and a bit more when expanded.
      const o = activeModuleId || type === 'popover' ? 24 : 20;

      computeAnchoredPosition(virtualReference, floatingEl, {
        placement: placement as any,
        offset: o,
        flip: true,
        shift: true,
        padding: 8,
      })
        .then(({ x, y }) => {
          if (cancelled) return;
          floatingEl.style.left = `${x}px`;
          floatingEl.style.top = `${y}px`;
          const rect = floatingEl.getBoundingClientRect();
          setSelectionMenuAnchorBox({ left: rect.left, top: rect.top, width: rect.width, height: rect.height });
        })
        .catch(() => {
          // soft fail
        });
    };

    updatePosition();
    const cleanupAutoUpdate = autoUpdateAnchoredPosition(updatePosition);

    return () => {
      cancelled = true;
      cleanupAutoUpdate();
    };
  }, [activeBox, visible, isOpen, type, activeModuleId, setSelectionMenuAnchorBox]);

  // Inline editor: double click on the canvas to edit curved text in place.
  useEffect(() => {
    if (!canvas2d) return;
    return canvas2d.subscribeTextEditRequests((selection) => {
      setInlineText(selection.text);
      setIsInlineEditing(true);
      window.setTimeout(() => inlineInputRef.current?.focus?.(), 0);
    });
  }, [canvas2d]);

  // Reposition the inline editor near the active object.
  useEffect(() => {
    if (!isInlineEditing || !engine) return;
    const anchor = engine.selection.getAnchorBox() ?? activeBox;
    if (!anchor) return;

    const virtualEl: VirtualElementLike = {
      getBoundingClientRect: () => new DOMRect(anchor.left, anchor.top, anchor.width, anchor.height),
    };

    (async () => {
      if (!inlineRef.current) return;
      const pos = await computeAnchoredPosition(virtualEl, inlineRef.current, {
        placement: 'top',
        offset: 8,
        flip: true,
        shift: true,
        padding: 8,
      });
      Object.assign(inlineRef.current.style, {
        position: 'fixed',
        left: `${pos.x}px`,
        top: `${pos.y}px`,
      });
    })();
  }, [isInlineEditing, engine, activeBox, inlineText]);

  if (!visible || !activeBox || !engine) return null;
  if (!isOpen) return null;

  const getSelectedObject = (): AsukaObjectSummary | null => getRenderEngineSelectedObjects(engine)[0] ?? null;

  const handleDelete = () => {
    if (!(permissions as any)?.canDeleteObject || !engine) return;
    const selected = getRenderEngineSelectedObjects(engine);
    for (const object of selected) {
      const id = String(object?.id ?? '').trim();
      if (!id || object?.locked) continue;
      if (String(object?.kind ?? '').toLowerCase() === 'product-3d') {
        void engine.objects.removeElement(id.replace(/^product:/, '')).catch(() => {});
        continue;
      }
      void engine.objects.remove(id).catch(() => {});
    }
  };

  const handleDuplicate = () => {
    if (!(permissions as any)?.canDuplicateObject || !engine) return;
    const selected = getSelectedObject();
    const id = String(selected?.id ?? '').trim();
    if (!id || selected?.locked) return;
    void engine.objects.duplicate(id, { source: 'selection-toolbar' }).catch(() => {});
  };

  const selectedObject = getSelectedObject() as any;
  const selectedObjectId = String(selectedObject?.id ?? '').trim();
  const selectedKind = String(selectedObject?.kind ?? selectedObject?.type ?? '').toLowerCase();
  const selectedEngine = String(selectedObject?.metadata?.engine ?? '').toLowerCase();
  const selectedFixedSlotConstraints = getObjectFixedSlotConstraints(selectedObject);
  const canTransform3DSelection = selectedEngine === 'babylon' && !!selectedObjectId && selectedKind !== 'product-3d' && selectedObject?.locked !== true;
  const updateSelected3D = (patch: Record<string, unknown>) => {
    if (!canTransform3DSelection || !selectedObjectId) return;
    void engine?.objects.updateTransform(selectedObjectId, patch, { source: 'selection-toolbar' }).catch(() => {});
  };

  const Transform3DControls = ({ rotationOnly = false }: { rotationOnly?: boolean } = {}) => {
    if (!canTransform3DSelection) return null;
    const canMoveX = (permissions as any)?.canMoveObject !== false && !selectedFixedSlotConstraints.lockX;
    const canMoveY = (permissions as any)?.canMoveObject !== false && !selectedFixedSlotConstraints.lockY;
    const canResize = (permissions as any)?.canResizeObject !== false;
    const canRotate = (permissions as any)?.canRotateObject !== false && !selectedFixedSlotConstraints.lockRotation;
    const nudge = 0.08;
    const rotate = Math.PI / 18;
    const btnClass = 'ask-theme-btn ask-theme-btn-secondary ask-text-[11px] ask-flex ask-h-6 ask-min-w-6 ask-items-center ask-justify-center ask-rounded-full ask-border ask-px-1.5 ask-py-0.5 ask-transition-colors';
    return (
      <div class="ask-flex ask-items-center ask-gap-1 ask-rounded-full ask-border ask-border-borderSubtle ask-bg-surfaceAlt ask-px-1 ask-py-0.5" aria-label="3D transform controls">
        {!rotationOnly && canMoveX ? (
          <>
            <button type="button" class={btnClass} title="Move left" aria-label="Move left" onClick={() => updateSelected3D({ deltaX: -nudge })}>←</button>
            <button type="button" class={btnClass} title="Move right" aria-label="Move right" onClick={() => updateSelected3D({ deltaX: nudge })}>→</button>
          </>
        ) : null}
        {!rotationOnly && canMoveY ? (
          <>
            <button type="button" class={btnClass} title="Move up" aria-label="Move up" onClick={() => updateSelected3D({ deltaY: nudge })}>↑</button>
            <button type="button" class={btnClass} title="Move down" aria-label="Move down" onClick={() => updateSelected3D({ deltaY: -nudge })}>↓</button>
          </>
        ) : null}
        {!rotationOnly && canResize ? (
          <>
            <button type="button" class={btnClass} title="Scale down" aria-label="Scale down" onClick={() => updateSelected3D({ scaleBy: 0.9 })}>−</button>
            <button type="button" class={btnClass} title="Scale up" aria-label="Scale up" onClick={() => updateSelected3D({ scaleBy: 1.1 })}>+</button>
          </>
        ) : null}
        {canRotate ? (
          <>
            <button type="button" class={btnClass} title="Rotate left" aria-label="Rotate left" onClick={() => updateSelected3D({ rotationBy: -rotate })}>↺</button>
            <button type="button" class={btnClass} title="Rotate right" aria-label="Rotate right" onClick={() => updateSelected3D({ rotationBy: rotate })}>↻</button>
          </>
        ) : null}
        {!rotationOnly ? (
          <button type="button" class={btnClass} title="Reset transform" aria-label="Reset transform" onClick={() => updateSelected3D({ resetTransform: true })}>
            <AsukaIcon name="reset" size={12} />
          </button>
        ) : null}
      </div>
    );
  };

  const RootMenu = () => {
    const visibleItems = items.filter((id) => canOpenSelectionModuleId(permissions as any, id as any) && !!getModule(id as any));
    const transformModuleAvailableFor3D = canTransform3DSelection && visibleItems.includes('transform' as any);
    const showInline3DControls = canTransform3DSelection && !transformModuleAvailableFor3D;
    const showInline3DRotationControls = canTransform3DSelection
      && transformModuleAvailableFor3D
      && (permissions as any)?.canRotateObject !== false
      && !selectedFixedSlotConstraints.lockRotation;
    const canDuplicateSelection = !!selectedObjectId && selectedObject?.locked !== true && (permissions as any)?.canDuplicateObject;
    const showSeparator = visibleItems.length > 0 || showInline3DControls || showInline3DRotationControls || canDuplicateSelection;
    return (
      <div class="ask-flex ask-items-center ask-gap-1.5">
        {visibleItems.map((id) => {
          const def = getModule(id);
          const label = def?.label ?? id;
          const icon = def?.icon;
          return (
            <button
              key={id}
              type="button"
              onClick={(e) => {
                // Opening Studio replaces this toolbar with a modal during the
                // same click. Keep the originating event from reaching the
                // newly mounted overlay backdrop in delegated-event browsers.
                e.preventDefault();
                e.stopPropagation();
                // Studio should open as a fullscreen modal (not inside the selection overlay).
                const rect = floatingRef.current?.getBoundingClientRect?.();
                if (rect) {
                  setSelectionMenuAnchorBox({ left: rect.left, top: rect.top, width: rect.width, height: rect.height });
                }
                if (id === 'studio') {
                  setPendingRestoreSelectionMenu(true);
                  close();
                  openModule('studio' as any);
                  return;
                }
                // Default: open as popover. Hold Shift to open as floating.
                const forceType = (e as any)?.shiftKey ? 'floating' : undefined;
                goTo(id as any, forceType ? { type: forceType } : undefined);
              }}
              class="ask-theme-btn ask-theme-btn-secondary ask-text-[11px] ask-flex ask-items-center ask-gap-1.5 ask-rounded-full ask-border ask-px-2 ask-py-0.5 ask-transition-colors"
            >
              {icon ? <AsukaIcon name={icon} size={12} className="ask-shrink-0" /> : <AsukaIcon name="menu" size={12} className="ask-shrink-0" />}
              <span>{label}</span>
            </button>
          );
        })}

        {showInline3DControls ? <Transform3DControls /> : null}
        {showInline3DRotationControls ? <Transform3DControls rotationOnly /> : null}

        {showSeparator ? <div class="ask-w-px ask-h-4 ask-bg-borderSubtle ask-mx-1" /> : null}

        {canDuplicateSelection ? <button
          type="button"
          onClick={handleDuplicate}
          class="ask-theme-btn ask-theme-btn-secondary ask-text-[11px] ask-flex ask-items-center ask-gap-1 ask-rounded-full ask-border ask-px-2 ask-py-0.5 ask-transition-colors"
          title={t('ui.duplicate')}
          aria-label={t('ui.duplicate')}
        >
          <AsukaIcon name="copy" size={12} />
        </button> : null}
        {(permissions as any)?.canDeleteObject ? <button
          type="button"
          onClick={handleDelete}
          class="ask-selection-toolbar__danger ask-theme-btn ask-theme-btn-secondary ask-text-[11px] ask-flex ask-items-center ask-gap-1 ask-rounded-full ask-border ask-px-2 ask-py-0.5 ask-transition-colors"
          title={t('common.delete')}
          aria-label={t('common.delete')}
        >
          <AsukaIcon name="trash" size={12} />
        </button> : null}
      </div>
    );
  };

  // In module view, render directly (do not define an inline component), so the module
  // stays mounted across SelectionToolbar re-renders (prevents unmount/remount).
  if (activeModuleId) {
    const ModuleComponent = activeDef?.render;
    const title = activeDef?.label ?? activeModuleId ?? 'Module';
    const icon = activeDef?.icon;

    return (
      <>
        {type === 'popover' ? (
          <div
            ref={floatingRef}
            class="ask-pointer-events-none ask-fixed ask-h-px ask-w-px ask-opacity-0"
            style={{ left: '0px', top: '0px' }}
            aria-hidden="true"
            data-asuka="selection-menu-anchor"
          />
        ) : null}
        <OverlayHost
          type={type === 'popover' ? 'popover' : 'floating'}
          title={title}
          icon={icon}
          canBack={true}
          onBack={back}
          onClose={close}
          anchorBox={type === 'popover' ? (liveSelectionModuleAnchorBox ?? selectionMenuAnchorBox) : null}
          popoverAnchorMode={type === 'popover' ? 'replace-anchor' : 'anchored'}
          zClass="ask-z-50"
          compactHeader
          attrs={{ 'data-asuka': 'selection-menu' }}
        >
          <div class="ask-selection-toolbar__module-shell ask-w-full ask-min-w-0">
            {ModuleComponent ? (
              <ModuleComponent context={context} />
            ) : (
              <div class="ask-p-3 ask-text-[12px] ask-text-textMuted">
                {t('ui.module_not_found')} <b>{String(activeModuleId)}</b>
              </div>
            )}
          </div>
          <InlineEditor />
        </OverlayHost>
      </>
    );
  }

  // Root toolbar (capsule) is still a small floating element anchored to the selection.
  return (
    <>
      <div
      ref={floatingRef}
      class="ask-theme-panel-block ask-z-50 ask-fixed ask-flex ask-items-center ask-gap-1.5 ask-rounded-full ask-shadow-soft ask-px-2 ask-py-1"
      style={{ left: '0px', top: '0px' }}
      role="toolbar"
      aria-label={t('ui.selection_menu')}
    >
      <RootMenu />
      </div>
      <InlineEditor />
    </>
  );
};
