import { useEffect, useLayoutEffect, useRef } from "react";
import { useLocation } from "wouter";
import { useTranslation } from "react-i18next";
import * as LucideIcons from "lucide-react";
import type { LucideIcon } from "lucide-react";
import {
  useListApps,
  getListAppsQueryKey,
  logAppOpen,
  type App,
} from "@workspace/api-client-react";
import { useDockVisible } from "@/hooks/use-dock-visible";

type IconName = keyof typeof LucideIcons;
function isIconName(x: string): x is IconName {
  return x in LucideIcons;
}
function resolveIcon(name: string): LucideIcon {
  if (isIconName(name)) {
    const Candidate = LucideIcons[name] as unknown;
    if (typeof Candidate === "function" || typeof Candidate === "object") {
      return Candidate as LucideIcon;
    }
  }
  return LucideIcons.Grid2X2;
}

export interface AppDockProps {
  currentSlug: string;
}

export function AppDock({ currentSlug }: AppDockProps) {
  const [, setLocation] = useLocation();
  const { i18n } = useTranslation();
  const lang = i18n.language;
  const { data: apps } = useListApps({ query: { queryKey: getListAppsQueryKey() } });
  const dockRef = useRef<HTMLDivElement | null>(null);
  const [dockEnabled] = useDockVisible();

  // Match Home's source-of-truth contract: same `useListApps` query
  // key and the same `isActive` filter Home applies in its
  // orderedApps effect (home.tsx L365). We don't subscribe to Home's
  // local drag-reorder state here because that state is intentionally
  // a Home-only UX surface; the dock just mirrors the API order, same
  // as Home's `orderedApps ?? apps` fallback on first render.
  const others = (apps ?? []).filter(
    (a) => a.isActive && a.slug !== currentSlug,
  );

  // Hide the dock when:
  //  - the user disabled it in Settings (dockEnabled === false), or
  //  - it would be degenerate (≤1 other app to switch to).
  const visible = dockEnabled && others.length > 1;

  // Publish the dock's measured outer height as `--app-dock-height` on
  // the document root so the global `body { padding-bottom }` rule in
  // index.css can reserve space below page content. Without this, the
  // floating dock (position: fixed) overlaps the last row on long
  // mobile pages (e.g. Meetings, Notes, My Orders).
  //
  // We re-measure on resize and on `others.length` changes so the
  // padding stays in sync as the dock grows/shrinks or the safe-area
  // inset changes (iOS rotation, URL-bar collapse).
  useLayoutEffect(() => {
    if (!visible) return;
    const root = document.documentElement;
    const el = dockRef.current;
    if (!el) return;

    const update = () => {
      // Use getBoundingClientRect so we capture the actual rendered
      // height including the `bottom-2/3` offset (we want the total
      // space the dock occupies from the bottom of the viewport).
      const rect = el.getBoundingClientRect();
      const viewportH = window.innerHeight;
      const occupied = Math.max(0, viewportH - rect.top);
      // A small breathing gap so the last row doesn't kiss the dock.
      root.style.setProperty("--app-dock-height", `${Math.ceil(occupied) + 8}px`);
    };

    update();
    const ro = new ResizeObserver(update);
    ro.observe(el);
    window.addEventListener("resize", update);
    window.addEventListener("orientationchange", update);
    return () => {
      ro.disconnect();
      window.removeEventListener("resize", update);
      window.removeEventListener("orientationchange", update);
      root.style.removeProperty("--app-dock-height");
    };
  }, [visible, others.length]);

  // When the dock is hidden, ensure we clear any stale variable so the
  // page doesn't keep extra padding from a prior render.
  useEffect(() => {
    if (visible) return;
    document.documentElement.style.removeProperty("--app-dock-height");
  }, [visible]);

  if (!visible) return null;

  const openApp = (app: App) => {
    logAppOpen(app.id).catch(() => {});
    const mode = app.openMode ?? "internal";
    if (mode === "external_tab" && app.externalUrl) {
      window.open(app.externalUrl, "_blank", "noopener,noreferrer");
      return;
    }
    if (mode === "external_iframe" && app.externalUrl) {
      setLocation(`/embedded/${app.id}`);
      return;
    }
    setLocation(app.route);
  };

  return (
    <div
      ref={dockRef}
      className="fixed inset-x-0 bottom-2 sm:bottom-3 z-40 flex justify-center print:hidden pointer-events-none"
      style={{ paddingBottom: "env(safe-area-inset-bottom)" }}
      data-testid="app-dock"
    >
      <div className="pointer-events-auto flex items-center gap-3 sm:gap-4 px-3 sm:px-4 py-2 rounded-full bg-white/95 backdrop-blur border border-gray-200 shadow-lg max-w-[min(92vw,640px)] overflow-x-auto no-scrollbar">
        {others.map((app) => {
          const Icon = resolveIcon(app.iconName);
          const label = lang === "ar" ? app.nameAr : app.nameEn;
          return (
            <button
              key={app.id}
              type="button"
              onClick={() => openApp(app)}
              aria-label={label}
              title={label}
              data-testid={`app-dock-${app.slug}`}
              className="shrink-0 w-11 h-11 rounded-full flex items-center justify-center hover:scale-110 transition-transform"
              style={{ backgroundColor: `${app.color}25` }}
            >
              <Icon size={20} color={app.color} />
            </button>
          );
        })}
      </div>
    </div>
  );
}
