"use client";

import { useState, useCallback, useEffect, useRef } from "react";
import {
  type DashboardLayoutConfig,
  type DashboardWidgetType,
} from "@capakraken/shared/types";
import {
  createDashboardWidget,
  createDefaultDashboardLayout,
  getNextDashboardWidgetY,
  normalizeDashboardLayout,
} from "@capakraken/shared/schemas";
import { trpc } from "~/lib/trpc/client.js";

const STORAGE_KEY = "capakraken_dashboard_v1";

function generateWidgetId() {
  return `widget-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
}

function loadFromStorage(): DashboardLayoutConfig | null {
  if (typeof window === "undefined") return null;
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (!raw) return null;
    return normalizeDashboardLayout(JSON.parse(raw));
  } catch {
    return null;
  }
}

function saveToStorage(config: DashboardLayoutConfig) {
  if (typeof window === "undefined") return;
  try {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
  } catch {}
}

export function useDashboardLayout() {
  const [config, setConfig] = useState<DashboardLayoutConfig>(() => loadFromStorage() ?? createDefaultDashboardLayout());
  const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const { data: dbData } = trpc.user.getDashboardLayout.useQuery(undefined, {
    staleTime: 30_000,
  }) as { data: { layout: DashboardLayoutConfig | null; updatedAt: unknown } | null | undefined };

  const saveMutation = trpc.user.saveDashboardLayout.useMutation();

  // Sync from DB on load (DB wins if it has data)
  useEffect(() => {
    if (dbData?.layout) {
      const dbConfig = normalizeDashboardLayout(dbData.layout);
      setConfig(dbConfig);
      saveToStorage(dbConfig);
    }
  }, [dbData]);

  const persist = useCallback((nextConfig: DashboardLayoutConfig) => {
    const newConfig = normalizeDashboardLayout(nextConfig);
    saveToStorage(newConfig);
    if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);
    saveTimeoutRef.current = setTimeout(() => {
      saveMutation.mutate({ layout: newConfig });
    }, 2000);
  }, [saveMutation]);

  const addWidget = useCallback((type: DashboardWidgetType) => {
    setConfig((prev) => {
      const newConfig = {
        ...prev,
        widgets: [
          ...prev.widgets,
          createDashboardWidget(type, {
            id: generateWidgetId(),
            x: 0,
            y: getNextDashboardWidgetY(prev.widgets),
          }),
        ],
      };
      persist(newConfig);
      return newConfig;
    });
  }, [persist]);

  const removeWidget = useCallback((id: string) => {
    setConfig((prev) => {
      const newConfig = { ...prev, widgets: prev.widgets.filter((w) => w.id !== id) };
      persist(newConfig);
      return newConfig;
    });
  }, [persist]);

  const updateWidgetConfig = useCallback((id: string, configUpdate: Record<string, unknown>) => {
    setConfig((prev) => {
      const newConfig = {
        ...prev,
        widgets: prev.widgets.map((w) =>
          w.id === id ? { ...w, config: { ...w.config, ...configUpdate } } : w,
        ),
      };
      persist(newConfig);
      return newConfig;
    });
  }, [persist]);

  const onLayoutChange = useCallback(
    (layout: { i: string; x: number; y: number; w: number; h: number }[]) => {
      setConfig((prev) => {
        const layoutMap = new Map(layout.map((item) => [item.i, item]));
        const previousWidgetMap = new Map(prev.widgets.map((widget) => [widget.id, widget]));
        const updatedWidgets = prev.widgets.map((w) => {
          const item = layoutMap.get(w.id);
          if (!item) return w;
          return { ...w, x: item.x, y: item.y, w: item.w, h: item.h };
        });

        // Only persist when the user actually moved/resized something.
        // react-grid-layout fires onLayoutChange on mount too — we skip that
        // to avoid overwriting saved positions with compacted coordinates.
        const changed = updatedWidgets.some((w) => {
          const orig = previousWidgetMap.get(w.id);
          return orig && (w.x !== orig.x || w.y !== orig.y || w.w !== orig.w || w.h !== orig.h);
        });

        const newConfig = { ...prev, widgets: updatedWidgets };
        if (changed) persist(newConfig);
        return newConfig;
      });
    },
    [persist],
  );

  const resetLayout = useCallback(() => {
    const defaultConfig = createDefaultDashboardLayout();
    setConfig(defaultConfig);
    persist(defaultConfig);
  }, [persist]);

  return {
    config,
    addWidget,
    removeWidget,
    updateWidgetConfig,
    onLayoutChange,
    resetLayout,
  };
}
