'use client';

import { useState } from 'react';
import {
  DndContext,
  KeyboardSensor,
  PointerSensor,
  closestCenter,
  useSensor,
  useSensors,
  type DragEndEvent,
} from '@dnd-kit/core';
import {
  SortableContext,
  arrayMove,
  sortableKeyboardCoordinates,
  useSortable,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { GripVertical, LayoutGrid } from 'lucide-react';

import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '@/components/ui/dialog';
import { Switch } from '@/components/ui/switch';
import { cn } from '@/lib/utils';
import { useDashboardWidgets } from '@/hooks/use-dashboard-widgets';
import type { DashboardWidget, WidgetGroup } from './widget-registry';

// The dashboard renders widgets in three independent visual regions at
// xl (1280+): charts (main column), rails (right aside), feed (full-
// width). Below xl, all three regions stack into one visual column -
// from the rep's eye it reads as a single ordered list, so the modal
// flattens its sortable in that tier. At xl it splits into three
// region-scoped sortables to match the actual side-by-side layout.
const GROUP_LABELS: Record<WidgetGroup, string> = {
  chart: 'Charts',
  rail: 'Side rail',
  feed: 'Activity',
};
const GROUP_ORDER: readonly WidgetGroup[] = ['chart', 'rail', 'feed'];

/**
 * Combined visibility + reorder picker for the dashboard header.
 *
 * The dashboard renders widgets in three independent visual regions -
 * Charts (main column), Side rail (right aside), Activity (full-width
 * feed). A drag across regions can't change the visual outcome, so the
 * modal exposes one sortable list per region instead of a single flat
 * list that silently fails on cross-region moves. Toggling a widget off
 * moves it to the "Hidden" section; toggling on appends it to the
 * bottom of its native region.
 *
 * Both visibility toggles and order changes commit optimistically via
 * `useDashboardWidgets` so the dashboard reflows in the background and
 * the rep can keep editing.
 */
export function CustomizeWidgetsMenu() {
  const [open, setOpen] = useState(false);
  const {
    allWidgets,
    visibleWidgets,
    visibility,
    isXlLayout,
    setVisible,
    setAll,
    setOrder,
    resetToDefaults,
    isSaving,
  } = useDashboardWidgets();

  const visibleCount = Object.values(visibility).filter(Boolean).length;
  const allVisible = visibleCount === allWidgets.length;
  const allHidden = visibleCount === 0;
  const matchesDefaults = allWidgets.every((w) => (visibility[w.id] ?? false) === w.defaultVisible);

  // Hidden = everything not currently rendered. Sorted by registry order
  // so it reads predictably (newly-added widgets appear at the bottom
  // until the rep explicitly enables them).
  const hidden = allWidgets.filter((w) => !visibility[w.id]);

  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
    useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
  );

  // Visible widgets split per region. Empty regions render nothing so
  // we don't show an "On dashboard / Side rail (0)" tease.
  const visibleByGroup: Record<WidgetGroup, DashboardWidget[]> = {
    chart: visibleWidgets.filter((w) => w.group === 'chart'),
    rail: visibleWidgets.filter((w) => w.group === 'rail'),
    feed: visibleWidgets.filter((w) => w.group === 'feed'),
  };

  // A drag inside group X only moves widgets within that group. Rebuild
  // the flat order by walking `visibleWidgets` in its current sequence
  // and replacing each group-X slot with the next id from the reordered
  // group list. This preserves the relative position of every other
  // widget - only the dragged group's internal order changes.
  function reorderGroup(group: WidgetGroup, oldIndex: number, newIndex: number) {
    const groupIds = visibleByGroup[group].map((w) => w.id);
    if (
      oldIndex < 0 ||
      newIndex < 0 ||
      oldIndex >= groupIds.length ||
      newIndex >= groupIds.length
    ) {
      return;
    }
    const reordered = arrayMove(groupIds, oldIndex, newIndex);
    let cursor = 0;
    const nextOrder = visibleWidgets.map((w) =>
      w.group === group ? (reordered[cursor++] ?? w.id) : w.id,
    );
    setOrder(nextOrder);
  }

  function makeDragEndHandler(group: WidgetGroup) {
    return (event: DragEndEvent) => {
      const { active, over } = event;
      if (!over || active.id === over.id) return;
      const ids = visibleByGroup[group].map((w) => w.id);
      const oldIndex = ids.indexOf(String(active.id));
      const newIndex = ids.indexOf(String(over.id));
      if (oldIndex === -1 || newIndex === -1) return;
      reorderGroup(group, oldIndex, newIndex);
    };
  }

  // Flat reorder used by the stacked layout (< xl). One SortableContext
  // over every visible widget; drops persist via setOrder, which the
  // hook routes to the mobile order field.
  function onFlatDragEnd(event: DragEndEvent) {
    const { active, over } = event;
    if (!over || active.id === over.id) return;
    const ids = visibleWidgets.map((w) => w.id);
    const oldIndex = ids.indexOf(String(active.id));
    const newIndex = ids.indexOf(String(over.id));
    if (oldIndex === -1 || newIndex === -1) return;
    setOrder(arrayMove(ids, oldIndex, newIndex));
  }

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger asChild>
        <Button variant="outline" size="sm" className="gap-1.5">
          <LayoutGrid className="h-4 w-4" aria-hidden />
          Customize
        </Button>
      </DialogTrigger>
      <DialogContent className="sm:max-w-2xl">
        <DialogHeader>
          <DialogTitle>Customize dashboard</DialogTitle>
          <DialogDescription>
            {isXlLayout
              ? 'Editing the desktop layout - drag a widget to reorder it within its region.'
              : 'Editing the stacked layout for this device - drag a widget to reorder. Your desktop arrangement is saved separately.'}{' '}
            Toggle the switch to show or hide. Hidden widgets leave no empty space - the layout
            reflows to fill the available width.
          </DialogDescription>
        </DialogHeader>

        {/* Toggle + reorder list. Capped at ~60vh with internal scroll so
            the modal doesn't push the action footer off-screen. The
            layout matches what the rep is actually seeing: at xl the
            dashboard renders charts | rails | feed as three independent
            slots, so the picker exposes three region-scoped sortables.
            Below xl everything stacks into one column visually, so the
            picker collapses to a single flat sortable that reorders
            across the whole list. */}
        <div className="max-h-[60vh] -mx-2 overflow-y-auto px-2">
          {isXlLayout ? (
            GROUP_ORDER.map((group) => {
              const widgets = visibleByGroup[group];
              if (widgets.length === 0) return null;
              return (
                <Section key={group} title={`${GROUP_LABELS[group]} (${widgets.length})`}>
                  <DndContext
                    sensors={sensors}
                    collisionDetection={closestCenter}
                    onDragEnd={makeDragEndHandler(group)}
                  >
                    <SortableContext
                      items={widgets.map((w) => w.id)}
                      strategy={verticalListSortingStrategy}
                    >
                      <ul className="space-y-1">
                        {widgets.map((w, idx) => (
                          <SortableVisibleRow
                            key={w.id}
                            widget={w}
                            position={idx + 1}
                            disabled={isSaving}
                            onToggle={(checked) => setVisible(w.id, checked)}
                          />
                        ))}
                      </ul>
                    </SortableContext>
                  </DndContext>
                </Section>
              );
            })
          ) : visibleWidgets.length > 0 ? (
            <Section title={`On dashboard (${visibleWidgets.length})`}>
              <DndContext
                sensors={sensors}
                collisionDetection={closestCenter}
                onDragEnd={onFlatDragEnd}
              >
                <SortableContext
                  items={visibleWidgets.map((w) => w.id)}
                  strategy={verticalListSortingStrategy}
                >
                  <ul className="space-y-1">
                    {visibleWidgets.map((w, idx) => (
                      <SortableVisibleRow
                        key={w.id}
                        widget={w}
                        position={idx + 1}
                        disabled={isSaving}
                        onToggle={(checked) => setVisible(w.id, checked)}
                      />
                    ))}
                  </ul>
                </SortableContext>
              </DndContext>
            </Section>
          ) : null}

          {hidden.length > 0 ? (
            <Section title={`Hidden (${hidden.length})`}>
              <ul className="space-y-1">
                {hidden.map((w) => (
                  <HiddenRow
                    key={w.id}
                    widget={w}
                    disabled={isSaving}
                    onToggle={(checked) => setVisible(w.id, checked)}
                  />
                ))}
              </ul>
            </Section>
          ) : null}
        </div>

        <DialogFooter className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between sm:gap-2">
          <span className="text-xs text-muted-foreground sm:order-first">
            {visibleCount} of {allWidgets.length} visible
          </span>
          <div className="flex flex-wrap items-center gap-2 sm:flex-nowrap">
            <Button
              variant="ghost"
              size="sm"
              disabled={matchesDefaults || isSaving}
              onClick={resetToDefaults}
            >
              Reset to defaults
            </Button>
            <Button
              variant="outline"
              size="sm"
              disabled={allHidden || isSaving}
              onClick={() => setAll(false)}
            >
              Hide all
            </Button>
            <Button
              variant="outline"
              size="sm"
              disabled={allVisible || isSaving}
              onClick={() => setAll(true)}
            >
              Show all
            </Button>
            <Button size="sm" onClick={() => setOpen(false)} className="w-full sm:w-auto">
              Done
            </Button>
          </div>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function Section({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <div className="py-2 first:pt-1">
      <div className="px-1 pb-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
        {title}
      </div>
      {children}
    </div>
  );
}

function SortableVisibleRow({
  widget,
  position,
  disabled,
  onToggle,
}: {
  widget: DashboardWidget;
  position: number;
  disabled: boolean;
  onToggle: (checked: boolean) => void;
}) {
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
    id: widget.id,
  });
  return (
    <li
      ref={setNodeRef}
      style={{ transform: CSS.Transform.toString(transform), transition }}
      className={cn(
        'flex items-start gap-2 rounded-md border bg-background px-2 py-2.5',
        isDragging && 'opacity-60 shadow-md',
      )}
    >
      <button
        type="button"
        className="mt-0.5 inline-flex h-7 w-7 shrink-0 cursor-grab items-center justify-center rounded text-muted-foreground hover:bg-accent active:cursor-grabbing"
        aria-label={`Drag handle for ${widget.label}`}
        {...attributes}
        {...listeners}
      >
        <GripVertical className="h-4 w-4" aria-hidden />
      </button>
      <span className="mt-1 w-5 shrink-0 text-right text-xs tabular-nums text-muted-foreground">
        {position}
      </span>
      <div className="min-w-0 flex-1">
        <div className="text-sm font-medium text-foreground">{widget.label}</div>
        <p className="text-xs text-muted-foreground">{widget.description}</p>
      </div>
      <Switch
        aria-label={`Show ${widget.label}`}
        checked
        disabled={disabled}
        onCheckedChange={onToggle}
        className="mt-0.5 shrink-0"
      />
    </li>
  );
}

function HiddenRow({
  widget,
  disabled,
  onToggle,
}: {
  widget: DashboardWidget;
  disabled: boolean;
  onToggle: (checked: boolean) => void;
}) {
  return (
    <li className="flex items-start gap-3 rounded-md px-3 py-2.5 hover:bg-accent/40">
      <div className="min-w-0 flex-1">
        <div className="text-sm font-medium text-foreground">{widget.label}</div>
        <p className="text-xs text-muted-foreground">{widget.description}</p>
      </div>
      <Switch
        aria-label={`Show ${widget.label}`}
        checked={false}
        disabled={disabled}
        onCheckedChange={onToggle}
        className="mt-0.5 shrink-0"
      />
    </li>
  );
}
