"use client";

import * as React from "react";
import {
  Responsive,
  useContainerWidth,
  verticalCompactor,
  type Layout,
  type LayoutItem,
} from "react-grid-layout";
import { GripVertical } from "lucide-react";

import { cn } from "@/lib/utils";
import {
  DASHBOARD_BREAKPOINTS,
  DASHBOARD_COLS,
  DASHBOARD_DRAG_HANDLE,
  cardKeyFromLayoutId,
  isClubbableCardLayoutId,
  isSectionLayoutId,
  type DashboardBreakpoint,
  type DashboardLayoutItem,
  type DashboardLayouts,
} from "@/app/customer/_lib/dashboard-layout";

import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";

export type DashboardGridItem = {
  id: string;
  content: React.ReactNode;
  /** When set on a section item, dropping a metric card here clubs into this section. */
  clubTargetKey?: string;
};

type DashboardLayoutShellProps = {
  layouts: DashboardLayouts;
  onLayoutsChange: (layouts: DashboardLayouts) => void;
  /** Drop statistic card A onto statistic card B → club (metrics only). */
  onClubCards?: (activeKey: string, overKey: string) => boolean;
  editMode?: boolean;
  items: DashboardGridItem[];
  className?: string;
};

function cloneLayouts(layouts: DashboardLayouts): DashboardLayouts {
  return {
    lg: layouts.lg.map((item) => ({ ...item })),
    md: layouts.md.map((item) => ({ ...item })),
    sm: layouts.sm.map((item) => ({ ...item })),
    xs: layouts.xs.map((item) => ({ ...item })),
    xxs: layouts.xxs.map((item) => ({ ...item })),
  };
}

/** Ignore tiny pointer jitter before a drag starts. */
const DRAG_THRESHOLD_PX = 8;

function eventWantsClub(event: Event | null | undefined): boolean {
  return Boolean(
    event &&
      (event instanceof MouseEvent || event instanceof PointerEvent || event instanceof KeyboardEvent) &&
      event.shiftKey,
  );
}

function toMutableItem(item: LayoutItem): DashboardLayoutItem {
  return {
    i: item.i,
    x: item.x,
    y: item.y,
    w: item.w,
    h: item.h,
    minW: item.minW,
    minH: item.minH,
    maxW: item.maxW,
    maxH: item.maxH,
  };
}

function toMutableLayout(
  layout: Layout | undefined,
  fallback: DashboardLayoutItem[],
): DashboardLayoutItem[] {
  if (!layout) return fallback.map((item) => ({ ...item }));
  return layout.map(toMutableItem);
}

function layoutItemsEqual(a: DashboardLayoutItem[], b: DashboardLayoutItem[]): boolean {
  if (a.length !== b.length) return false;
  const byId = new Map(b.map((item) => [item.i, item]));
  for (const item of a) {
    const other = byId.get(item.i);
    if (!other) return false;
    if (item.x !== other.x || item.y !== other.y || item.w !== other.w || item.h !== other.h) {
      return false;
    }
  }
  return true;
}

function layoutsEqual(a: DashboardLayouts, b: DashboardLayouts): boolean {
  return (Object.keys(DASHBOARD_COLS) as DashboardBreakpoint[]).every((bp) =>
    layoutItemsEqual(a[bp], b[bp]),
  );
}

function mergeBreakpointLayouts(
  allLayouts: Partial<Record<string, Layout>>,
  previous: DashboardLayouts,
): DashboardLayouts {
  return {
    lg: toMutableLayout(allLayouts.lg, previous.lg),
    md: toMutableLayout(allLayouts.md, previous.md),
    sm: toMutableLayout(allLayouts.sm, previous.sm),
    xs: toMutableLayout(allLayouts.xs, previous.xs),
    xxs: toMutableLayout(allLayouts.xxs, previous.xxs),
  };
}

function withBreakpointLayout(
  previous: DashboardLayouts,
  breakpoint: DashboardBreakpoint,
  layout: Layout,
): DashboardLayouts {
  return {
    ...previous,
    [breakpoint]: toMutableLayout(layout, previous[breakpoint]),
  };
}

function resolveClubTarget(
  clientX: number,
  clientY: number,
  activeLayoutId: string,
): string | null {
  if (!isClubbableCardLayoutId(activeLayoutId)) return null;
  const activeKey = cardKeyFromLayoutId(activeLayoutId);
  if (!activeKey) return null;

  const stack = document.elementsFromPoint(clientX, clientY);
  for (const node of stack) {
    if (!(node instanceof HTMLElement)) continue;

    const cardHost = node.closest<HTMLElement>("[data-dashboard-card]");
    if (cardHost && cardHost.dataset.nestedMetric !== "true") {
      const key = cardHost.dataset.dashboardCard ?? "";
      if (key && key !== activeKey && key !== "total") {
        return key;
      }
    }

    const sectionHost = node.closest<HTMLElement>("[data-club-target-key]");
    if (sectionHost) {
      const key = sectionHost.dataset.clubTargetKey ?? "";
      if (key && key !== activeKey && key !== "total") {
        return key;
      }
    }
  }
  return null;
}

export function DashboardLayoutShell({
  layouts,
  onLayoutsChange,
  onClubCards,
  editMode = false,
  items,
  className,
}: DashboardLayoutShellProps) {
  const { width, containerRef, mounted } = useContainerWidth({
    measureBeforeMount: true,
    initialWidth: 1280,
  });

  const layoutsRef = React.useRef(layouts);
  const onLayoutsChangeRef = React.useRef(onLayoutsChange);
  const onClubCardsRef = React.useRef(onClubCards);
  const interactingRef = React.useRef(false);
  const breakpointRef = React.useRef<DashboardBreakpoint>("lg");
  const dragLayoutIdRef = React.useRef<string | null>(null);
  const dragStartLayoutsRef = React.useRef<DashboardLayouts | null>(null);
  const shiftHeldRef = React.useRef(false);
  const [clubHintKey, setClubHintKey] = React.useState<string | null>(null);

  React.useEffect(() => {
    layoutsRef.current = layouts;
  }, [layouts]);

  React.useEffect(() => {
    onLayoutsChangeRef.current = onLayoutsChange;
  }, [onLayoutsChange]);

  React.useEffect(() => {
    onClubCardsRef.current = onClubCards;
  }, [onClubCards]);

  const clearClubHint = React.useCallback(() => {
    setClubHintKey(null);
  }, []);

  const commitLayouts = React.useCallback((next: DashboardLayouts) => {
    if (layoutsEqual(next, layoutsRef.current)) return;
    layoutsRef.current = next;
    onLayoutsChangeRef.current(next);
  }, []);

  const handleLayoutChange = React.useCallback(
    (_current: Layout, allLayouts: Partial<Record<string, Layout>>) => {
      if (!interactingRef.current) return;
      commitLayouts(mergeBreakpointLayouts(allLayouts, layoutsRef.current));
    },
    [commitLayouts],
  );

  const handleBreakpointChange = React.useCallback((bp: string) => {
    if (bp in DASHBOARD_COLS) {
      breakpointRef.current = bp as DashboardBreakpoint;
    }
  }, []);

  const handleDragStart = React.useCallback(
    (
      _layout: Layout,
      _oldItem: LayoutItem | null,
      newItem: LayoutItem | null,
    ) => {
      interactingRef.current = true;
      dragLayoutIdRef.current = newItem?.i ?? null;
      dragStartLayoutsRef.current = cloneLayouts(layoutsRef.current);
      shiftHeldRef.current = false;
      clearClubHint();
    },
    [clearClubHint],
  );

  const handleResizeStart = React.useCallback(
    (
      _layout: Layout,
      _oldItem: LayoutItem | null,
      newItem: LayoutItem | null,
    ) => {
      interactingRef.current = true;
      dragLayoutIdRef.current = newItem?.i ?? null;
      clearClubHint();
    },
    [clearClubHint],
  );

  const handleDrag = React.useCallback(
    (
      _layout: Layout,
      _oldItem: LayoutItem | null,
      newItem: LayoutItem | null,
      _placeholder: LayoutItem | null,
      event: Event,
    ) => {
      const activeId = newItem?.i ?? dragLayoutIdRef.current;
      const wantsClub = eventWantsClub(event);
      shiftHeldRef.current = wantsClub;

      if (!wantsClub || !activeId || !isClubbableCardLayoutId(activeId)) {
        clearClubHint();
        return;
      }
      if (!(event instanceof MouseEvent) && !(event instanceof PointerEvent)) {
        return;
      }
      // Visual hint only — does not freeze or change layout.
      setClubHintKey(resolveClubTarget(event.clientX, event.clientY, activeId));
    },
    [clearClubHint],
  );

  const handleInteractionStop = React.useCallback(
    (
      layout: Layout,
      _oldItem: LayoutItem | null,
      newItem: LayoutItem | null,
      _placeholder: LayoutItem | null,
      event: Event,
    ) => {
      const activeId = newItem?.i ?? dragLayoutIdRef.current;
      const bp = breakpointRef.current;
      let clubbed = false;

      // Club only with Shift+drop onto another statistic card / section.
      const wantsClub = eventWantsClub(event) || shiftHeldRef.current;
      if (
        wantsClub &&
        activeId &&
        isClubbableCardLayoutId(activeId) &&
        onClubCardsRef.current &&
        (event instanceof MouseEvent || event instanceof PointerEvent)
      ) {
        const overKey = resolveClubTarget(event.clientX, event.clientY, activeId);
        const activeKey = cardKeyFromLayoutId(activeId);
        if (activeKey && overKey) {
          if (dragStartLayoutsRef.current) {
            layoutsRef.current = dragStartLayoutsRef.current;
            onLayoutsChangeRef.current(dragStartLayoutsRef.current);
          }
          clubbed = onClubCardsRef.current(activeKey, overKey);
        }
      }

      clearClubHint();
      dragLayoutIdRef.current = null;
      interactingRef.current = false;
      dragStartLayoutsRef.current = null;
      shiftHeldRef.current = false;

      if (clubbed) {
        return;
      }

      commitLayouts(withBreakpointLayout(layoutsRef.current, bp, layout));
    },
    [clearClubHint, commitLayouts],
  );

  const handleResizeStop = React.useCallback(
    (layout: Layout) => {
      clearClubHint();
      dragLayoutIdRef.current = null;
      const bp = breakpointRef.current;
      commitLayouts(withBreakpointLayout(layoutsRef.current, bp, layout));
      interactingRef.current = false;
    },
    [clearClubHint, commitLayouts],
  );

  return (
    <div ref={containerRef} className={cn("w-full min-w-0", className)}>
      {mounted ? (
        <Responsive
          className={cn("dashboard-rgl", editMode && "dashboard-rgl-editing")}
          width={width}
          layouts={layouts}
          breakpoints={DASHBOARD_BREAKPOINTS}
          cols={DASHBOARD_COLS}
          rowHeight={40}
          margin={[8, 8] as const}
          containerPadding={[0, 0] as const}
          compactor={verticalCompactor}
          dragConfig={{
            enabled: editMode,
            handle: `.${DASHBOARD_DRAG_HANDLE}`,
            threshold: DRAG_THRESHOLD_PX,
          }}
          resizeConfig={{
            enabled: editMode,
            handles: ["se"],
          }}
          onLayoutChange={handleLayoutChange}
          onBreakpointChange={handleBreakpointChange}
          onDragStart={handleDragStart}
          onResizeStart={handleResizeStart}
          onDrag={handleDrag}
          onDragStop={handleInteractionStop}
          onResizeStop={handleResizeStop}
        >
          {items.map((item) => {
            const cardKey = cardKeyFromLayoutId(item.id);
            const isSection = isSectionLayoutId(item.id);
            const sectionClubKey = isSection ? item.clubTargetKey : undefined;
            const isClubHint = Boolean(
              clubHintKey &&
                ((cardKey && clubHintKey === cardKey) ||
                  (sectionClubKey && clubHintKey === sectionClubKey)),
            );

            return (
              <div
                key={item.id}
                className={cn(
                  "group relative flex h-full min-h-0 flex-col overflow-hidden rounded-xl border border-border/70 bg-card shadow-sm",
                  editMode && "ring-1 ring-primary/30 ring-offset-1 ring-offset-background",
                  isClubHint && "ring-2 ring-primary ring-offset-2",
                  isSection && "bg-gradient-to-b from-muted/30 to-card",
                )}
                data-dashboard-card={cardKey ?? undefined}
                data-dashboard-section={isSection ? item.id.slice("section:".length) : undefined}
                data-club-target-key={sectionClubKey}
              >
                {editMode ? (
                  <button
                    type="button"
                    className={cn(
                      DASHBOARD_DRAG_HANDLE,
                      "absolute top-1.5 left-1.5 z-20 inline-flex size-7 cursor-grab touch-none items-center justify-center rounded-md border bg-background/95 text-muted-foreground shadow-sm",
                      "opacity-100 hover:text-foreground active:cursor-grabbing",
                    )}
                    aria-label="Drag to move"
                    title={
                      cardKey && cardKey !== "total"
                        ? "Drag to move. Hold Shift and drop on another statistic card to club"
                        : "Drag to move"
                    }
                  >
                    <GripVertical className="size-3.5" />
                  </button>
                ) : null}
                {isClubHint ? (
                  <div className="pointer-events-none absolute inset-0 z-30 flex items-center justify-center rounded-[inherit] bg-background/80 text-xs font-semibold text-primary">
                    Shift + drop to club
                  </div>
                ) : null}
                <div className="min-h-0 flex-1 overflow-hidden [&>*]:h-full [&>*]:border-0 [&>*]:shadow-none">
                  {item.content}
                </div>
              </div>
            );
          })}
        </Responsive>
      ) : (
        <div className="min-h-[200px] w-full" />
      )}
      <style>{`
        .dashboard-rgl .react-resizable-handle {
          z-index: 30;
          width: 16px;
          height: 16px;
          opacity: 0.55;
        }
        .dashboard-rgl .react-grid-item:hover .react-resizable-handle,
        .dashboard-rgl-editing .react-resizable-handle {
          opacity: 1;
        }
        .dashboard-rgl .react-resizable-handle-se {
          bottom: 4px;
          right: 4px;
        }
        .dashboard-rgl .react-resizable-handle::after {
          right: 3px;
          bottom: 3px;
          width: 8px;
          height: 8px;
          border-right-width: 2px;
          border-bottom-width: 2px;
          border-color: hsl(var(--primary) / 0.75);
        }
        .dashboard-rgl .react-grid-item.react-grid-placeholder {
          background: hsl(var(--primary) / 0.12);
          border-radius: 0.75rem;
        }
      `}</style>
    </div>
  );
}

export type { DashboardBreakpoint };
