/**
 * WidgetGrid - Draggable grid layout for widgets
 * Uses react-grid-layout for drag-and-drop functionality
 */

/* eslint-disable @typescript-eslint/no-non-null-assertion */

import { useCallback, useMemo, useRef, useState, useEffect } from "react";
import GridLayout from "react-grid-layout";
import type { Layout, LayoutItem } from "react-grid-layout";
import type { WidgetPlacement } from "@mosaic/shared";
import { getWidgetByName } from "./WidgetRegistry";
import { BaseWidget } from "./BaseWidget";
import "react-grid-layout/css/styles.css";

// Simple classnames utility
function cn(...classes: (string | undefined | null | false)[]): string {
  return classes.filter(Boolean).join(" ");
}

export interface WidgetGridProps {
  layout: WidgetPlacement[];
  onLayoutChange: (layout: WidgetPlacement[]) => void;
  onRemoveWidget?: (widgetId: string) => void;
  onEditWidget?: (widgetId: string) => void;
  isEditing?: boolean;
  className?: string;
}

export function WidgetGrid({
  layout,
  onLayoutChange,
  onRemoveWidget,
  onEditWidget,
  isEditing = false,
  className,
}: WidgetGridProps): React.JSX.Element {
  // Measure container width for responsive grid
  const containerRef = useRef<HTMLDivElement>(null);
  const [containerWidth, setContainerWidth] = useState(1200);

  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;

    const observer = new ResizeObserver((entries): void => {
      const entry = entries[0];
      if (entry) {
        setContainerWidth(entry.contentRect.width);
      }
    });
    observer.observe(el);

    // Set initial width
    setContainerWidth(el.clientWidth);

    return (): void => {
      observer.disconnect();
    };
  }, []);

  // Convert WidgetPlacement to react-grid-layout Layout format
  const gridLayout: Layout = useMemo(
    () =>
      layout.map((item): LayoutItem => {
        const layoutItem: LayoutItem = {
          i: item.i,
          x: item.x,
          y: item.y,
          w: item.w,
          h: item.h,
          static: !isEditing || (item.static ?? false),
          isDraggable: isEditing && item.isDraggable !== false,
          isResizable: isEditing && item.isResizable !== false,
        };

        if (item.minW !== undefined) layoutItem.minW = item.minW;
        if (item.maxW !== undefined) layoutItem.maxW = item.maxW;
        if (item.minH !== undefined) layoutItem.minH = item.minH;
        if (item.maxH !== undefined) layoutItem.maxH = item.maxH;

        return layoutItem;
      }),
    [layout, isEditing]
  );

  const handleLayoutChange = useCallback(
    (newLayout: Layout) => {
      const updatedLayout: WidgetPlacement[] = newLayout.map((item): WidgetPlacement => {
        const placement: WidgetPlacement = {
          i: item.i,
          x: item.x,
          y: item.y,
          w: item.w,
          h: item.h,
        };

        if (item.minW !== undefined) placement.minW = item.minW;
        if (item.maxW !== undefined) placement.maxW = item.maxW;
        if (item.minH !== undefined) placement.minH = item.minH;
        if (item.maxH !== undefined) placement.maxH = item.maxH;
        if (item.static !== undefined) placement.static = item.static;
        if (item.isDraggable !== undefined) placement.isDraggable = item.isDraggable;
        if (item.isResizable !== undefined) placement.isResizable = item.isResizable;

        return placement;
      });
      onLayoutChange(updatedLayout);
    },
    [onLayoutChange]
  );

  const handleRemoveWidget = useCallback(
    (widgetId: string) => {
      if (onRemoveWidget) {
        onRemoveWidget(widgetId);
      }
    },
    [onRemoveWidget]
  );

  // Empty state
  if (layout.length === 0) {
    return (
      <div
        ref={containerRef}
        className="flex items-center justify-center h-full min-h-[400px]"
        style={{
          background: "var(--surface-2)",
          borderRadius: "var(--r-lg)",
          border: "2px dashed var(--border)",
        }}
      >
        <div className="text-center">
          <p className="text-lg font-medium" style={{ color: "var(--muted)" }}>
            No widgets yet
          </p>
          <p className="text-sm mt-1" style={{ color: "var(--muted)", opacity: 0.7 }}>
            Add widgets to customize your dashboard
          </p>
        </div>
      </div>
    );
  }

  return (
    <div ref={containerRef} className={cn("widget-grid-container", className)}>
      <GridLayout
        className="layout"
        layout={gridLayout}
        onLayoutChange={handleLayoutChange}
        width={containerWidth}
        gridConfig={{
          cols: 12,
          rowHeight: 100,
        }}
        dragConfig={{
          enabled: isEditing,
        }}
        resizeConfig={{
          enabled: isEditing,
        }}
        data-testid="grid-layout"
      >
        {layout.map((item) => {
          // Extract widget type from widget ID (format: "WidgetType-uuid")
          const widgetType = item.i.split("-")[0]!;
          const widgetDef = getWidgetByName(widgetType);

          if (!widgetDef) {
            return (
              <div key={item.i} data-testid={`widget-${item.i}`}>
                <BaseWidget id={item.i} title="Unknown Widget" error="Widget not found">
                  <div />
                </BaseWidget>
              </div>
            );
          }

          const WidgetComponent = widgetDef.component;

          return (
            <div key={item.i} data-testid={`widget-${item.i}`}>
              <BaseWidget
                id={item.i}
                title={widgetDef.displayName}
                description={widgetDef.description}
                {...(isEditing &&
                  onEditWidget && {
                    onEdit: (): void => {
                      onEditWidget(item.i);
                    },
                  })}
                {...(isEditing &&
                  onRemoveWidget && {
                    onRemove: (): void => {
                      handleRemoveWidget(item.i);
                    },
                  })}
              >
                <WidgetComponent id={item.i} />
              </BaseWidget>
            </div>
          );
        })}
      </GridLayout>
    </div>
  );
}
