"use client";

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

import { useEffect, useState, useTransition, type ReactNode } from "react";
import { useRouter } from "next/navigation";
import { GridLayout } from "react-grid-layout";
import type { Layout } from "react-grid-layout";
import { GripVertical, Settings2, Trash2, RotateCcw, Plus, LayoutGrid } from "lucide-react";
import type { DashboardLayout, WidgetPlacement, PresetId } from "@/lib/dashboard";
import { computePresetLayoutFromMetas, widgetContentKey } from "@/lib/dashboard";
import type { SerializedWidgetMeta } from "@/modules/_core/registry";
import { saveDashboardLayout, resetDashboardLayout } from "@/app/d/actions";
import { Button } from "@/components/ui/button";
import { WidgetPicker } from "./widget-picker";

function placementKey(placement: WidgetPlacement, index: number) {
  return `${placement.widgetId}::${index}`;
}

export function DashboardEditor({
  dashboard,
  layout: initialLayout,
  widgetMetas,
  widgetContents,
}: {
  dashboard: { id: string; name: string; slug: string };
  layout: DashboardLayout;
  widgetMetas: SerializedWidgetMeta[];
  widgetContents: Record<string, ReactNode>;
}) {
  const router = useRouter();
  const [isPending, startTransition] = useTransition();
  const [placements, setPlacements] = useState<WidgetPlacement[]>(initialLayout.widgets);
  const [isDirty, setIsDirty] = useState(false);
  const [pickerOpen, setPickerOpen] = useState(false);
  const [configuringIndex, setConfiguringIndex] = useState<number | null>(null);
  const [containerWidth, setContainerWidth] = useState(1200);
  const [presetMenuOpen, setPresetMenuOpen] = useState(false);

  useEffect(() => {
    function measure() {
      const el = document.getElementById("dashboard-editor-grid");
      if (el) setContainerWidth(el.offsetWidth);
    }
    measure();
    window.addEventListener("resize", measure);
    return () => window.removeEventListener("resize", measure);
  }, []);

  useEffect(() => {
    function onKey(e: KeyboardEvent) {
      if (e.key === "Escape") handleCancel();
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [dashboard.slug]);

  function handleCancel() {
    router.push(`/d/${dashboard.slug}`);
  }

  function handleSave() {
    startTransition(async () => {
      await saveDashboardLayout(dashboard.id, { version: 1, widgets: placements });
      router.push(`/d/${dashboard.slug}`);
    });
  }

  function handleReset() {
    startTransition(async () => {
      await resetDashboardLayout(dashboard.id);
      router.push(`/d/${dashboard.slug}`);
    });
  }

  function handleLayoutChange(items: Layout) {
    setPlacements((current) =>
      current.map((p, i) => {
        const key = placementKey(p, i);
        const item = items.find((it) => it.i === key);
        if (!item) return p;
        return { ...p, x: item.x, y: item.y, w: item.w, h: item.h };
      }),
    );
    setIsDirty(true);
  }

  function removeWidget(index: number) {
    setPlacements((current) => current.filter((_, i) => i !== index));
    setIsDirty(true);
  }

  function addWidget(widgetId: string, config: unknown) {
    const meta = widgetMetas.find((m) => m.id === widgetId);
    if (!meta) return;
    const maxY = placements.reduce((m, p) => Math.max(m, p.y + p.h), 0);
    setPlacements((current) => [
      ...current,
      { widgetId, config, x: 0, y: maxY, w: meta.defaultSize.w, h: meta.defaultSize.h },
    ]);
    setIsDirty(true);
    setPickerOpen(false);
  }

  function updateConfig(index: number, config: unknown) {
    setPlacements((current) => current.map((p, i) => (i === index ? { ...p, config } : p)));
    setIsDirty(true);
    setConfiguringIndex(null);
  }

  function applyPreset(preset: PresetId) {
    const next = computePresetLayoutFromMetas(preset, widgetMetas);
    setPlacements(next.widgets);
    setIsDirty(true);
    setPresetMenuOpen(false);
  }

  const gridItems: Layout = placements.map((p, i) => ({
    i: placementKey(p, i),
    x: p.x,
    y: p.y,
    w: p.w,
    h: p.h,
    minW: widgetMetas.find((m) => m.id === p.widgetId)?.minSize?.w ?? 2,
    minH: widgetMetas.find((m) => m.id === p.widgetId)?.minSize?.h ?? 1,
    maxW: widgetMetas.find((m) => m.id === p.widgetId)?.maxSize?.w ?? 12,
  }));

  return (
    <div>
      <div className="mb-6 flex items-center justify-between gap-4 flex-wrap">
        <h1 className="serif text-[26px] tracking-tight">{dashboard.name}</h1>
        <div className="flex items-center gap-2 flex-wrap relative">
          <div className="relative">
            <Button
              variant="outline"
              size="sm"
              onClick={() => setPresetMenuOpen((o) => !o)}
              disabled={isPending}
            >
              <LayoutGrid data-icon="inline-start" />
              Preset
            </Button>
            {presetMenuOpen && (
              <div
                className="absolute right-0 top-9 z-50 min-w-[180px] rounded-md border-[0.5px] bg-card shadow-[var(--shadow-pop)]"
                style={{ borderColor: "var(--hair-2)" }}
                onMouseLeave={() => setPresetMenuOpen(false)}
              >
                <button
                  type="button"
                  onClick={() => applyPreset("classic")}
                  className="block w-full text-left px-3 py-2 text-sm hover:bg-[var(--shade)]"
                >
                  Classic — main + side rail
                </button>
                <button
                  type="button"
                  onClick={() => applyPreset("split")}
                  className="block w-full text-left px-3 py-2 text-sm hover:bg-[var(--shade)]"
                >
                  Split — two even columns
                </button>
                <button
                  type="button"
                  onClick={() => applyPreset("glance")}
                  className="block w-full text-left px-3 py-2 text-sm hover:bg-[var(--shade)]"
                >
                  Glance — single column
                </button>
              </div>
            )}
          </div>
          <Button variant="outline" size="sm" onClick={handleReset} disabled={isPending}>
            <RotateCcw data-icon="inline-start" />
            Reset
          </Button>
          <Button
            variant="outline"
            size="sm"
            onClick={() => setPickerOpen(true)}
            disabled={isPending}
          >
            <Plus data-icon="inline-start" />
            Add widget
          </Button>
          <Button variant="outline" size="sm" onClick={handleCancel} disabled={isPending}>
            Cancel
          </Button>
          <Button size="sm" onClick={handleSave} disabled={!isDirty || isPending}>
            {isPending ? "Saving…" : "Save"}
          </Button>
        </div>
      </div>

      <p className="mb-4 text-sm text-muted-foreground">
        Drag to reorder · resize from the bottom-right corner · ESC to cancel
      </p>

      <div id="dashboard-editor-grid">
        <GridLayout
          layout={gridItems}
          width={containerWidth}
          gridConfig={{
            cols: 12,
            rowHeight: 60,
            margin: [16, 16] as [number, number],
            containerPadding: [0, 0] as [number, number],
          }}
          dragConfig={{ handle: ".drag-handle" }}
          onLayoutChange={handleLayoutChange}
        >
          {placements.map((placement, i) => {
            const meta = widgetMetas.find((m) => m.id === placement.widgetId);
            const content = widgetContents[widgetContentKey(placement)];
            return (
              <div
                key={placementKey(placement, i)}
                className="rounded-lg border bg-card text-card-foreground flex flex-col overflow-hidden"
              >
                <div className="drag-handle flex items-center gap-2 px-3 py-2 bg-muted/40 cursor-grab active:cursor-grabbing select-none border-b">
                  <GripVertical className="size-4 text-muted-foreground shrink-0" />
                  <span className="text-sm font-medium truncate flex-1">
                    {meta?.title ?? placement.widgetId}
                  </span>
                  <button
                    type="button"
                    onClick={() => setConfiguringIndex(i)}
                    className="text-muted-foreground hover:text-foreground transition-colors p-0.5"
                    aria-label="Configure widget"
                  >
                    <Settings2 className="size-4" />
                  </button>
                  <button
                    type="button"
                    onClick={() => removeWidget(i)}
                    className="text-muted-foreground hover:text-destructive transition-colors p-0.5"
                    aria-label="Remove widget"
                  >
                    <Trash2 className="size-4" />
                  </button>
                </div>
                <div className="flex-1 min-h-0 overflow-auto p-4">
                  {content ?? (
                    <p className="text-xs text-muted-foreground text-center">
                      {meta?.description ?? "Preview available after save"}
                    </p>
                  )}
                </div>
              </div>
            );
          })}
        </GridLayout>
      </div>

      {placements.length === 0 && (
        <div className="flex flex-col items-center justify-center py-24 gap-4 text-center">
          <p className="text-muted-foreground">No widgets yet.</p>
          <Button onClick={() => setPickerOpen(true)}>
            <Plus className="size-4 mr-1" />
            Add widget
          </Button>
        </div>
      )}

      {pickerOpen && (
        <WidgetPicker
          onClose={() => setPickerOpen(false)}
          widgetMetas={widgetMetas}
          onAdd={addWidget}
        />
      )}

      {configuringIndex !== null && (
        <WidgetPicker
          onClose={() => setConfiguringIndex(null)}
          widgetMetas={widgetMetas}
          onAdd={(_, config) => updateConfig(configuringIndex, config)}
          initialWidgetId={placements[configuringIndex]?.widgetId}
          initialConfig={placements[configuringIndex]?.config}
        />
      )}
    </div>
  );
}
