/**
 * Nav orientation transition — ported from INVYONE Design System
 * (`ui_kits/app/app.jsx` → animatedNavOrientationChange).
 *
 * Clones the outgoing nav (sidebar OR topnav) as a ghost fixed over its
 * original rect, slides it along its own axis + fades, then applies the
 * state change. The new nav fades in via its own CSS enter animation.
 *
 * No 3D, no fold — just clean motion matching the axis change.
 */
export type NavOrientation = "vertical" | "horizontal";

export function animatedNavOrientationChange(
  next: NavOrientation,
  apply: (next: NavOrientation) => void,
): void {
  const root = document.documentElement;
  root.setAttribute("data-nav-anim", next);

  // Grab the current outgoing nav. invyone uses .v5-side / .v5-topnav.
  const oldAside = document.querySelector<HTMLElement>("aside.v5-side");
  const oldTopnav = document.querySelector<HTMLElement>(".v5-topnav");
  const oldNav = oldAside || oldTopnav;

  if (oldNav) {
    const rect = oldNav.getBoundingClientRect();
    const ghost = oldNav.cloneNode(true) as HTMLElement;
    ghost.classList.add("v5-nav-exit-ghost");
    const fromOrient: NavOrientation = next === "horizontal" ? "vertical" : "horizontal";
    Object.assign(ghost.style, {
      position: "fixed",
      left: `${rect.left}px`,
      top: `${rect.top}px`,
      width: `${rect.width}px`,
      height: `${rect.height}px`,
      margin: "0",
      zIndex: "60",
      pointerEvents: "none",
      willChange: "transform, opacity",
    });
    // Kill child animations on the ghost so it stays visually static while sliding.
    ghost.querySelectorAll<HTMLElement>("*").forEach((el) => {
      el.style.animation = "none";
      el.style.transition = "none";
    });
    document.body.appendChild(ghost);

    // Drive via setInterval because React.flushSync in the same tick can swallow rAF.
    const DURATION = 320;
    const startTs = performance.now();
    // sidebar (vertical) slides left; topnav (horizontal) slides up.
    const deltaAxis: "X" | "Y" = fromOrient === "vertical" ? "X" : "Y";
    const deltaMax = fromOrient === "vertical" ? -28 : -14;
    const ease = (t: number) => (t >= 1 ? 1 : 1 - Math.pow(1 - t, 2.2));
    const iv = setInterval(() => {
      if (!ghost.isConnected) {
        clearInterval(iv);
        return;
      }
      const t = Math.min(1, (performance.now() - startTs) / DURATION);
      const e = ease(t);
      ghost.style.transform = `translate${deltaAxis}(${deltaMax * e}px)`;
      ghost.style.opacity = String(1 - e);
      if (t >= 1) {
        clearInterval(iv);
        ghost.remove();
      }
    }, 16);
    // Safety net — if the ghost hangs around, nuke it.
    setTimeout(() => {
      clearInterval(iv);
      if (ghost.isConnected) ghost.remove();
    }, 900);
  }

  // Apply the state change. New nav mounts and plays its own enter animation.
  apply(next);

  // Clean up the root attribute after the "enter" window.
  setTimeout(() => {
    if (root.getAttribute("data-nav-anim") === next) {
      root.removeAttribute("data-nav-anim");
    }
  }, 520);
}
