import type { ComponentProps, ReactNode } from "react";
import { NodeResizeControl } from "@xyflow/react";

import { cn } from "@/lib/utils";

export type BaseNodeProps = ComponentProps<"div"> & {
  /** When true, shows a bottom-right resize handle. Requires nodeId when used inside React Flow. */
  resizable?: boolean;
  /** Node id for the resize control (required when resizable is true). */
  nodeId?: string;
  /** Connection handles (InputHandle, OutputHandle). Rendered outside the overflow layer so they stay visible. */
  handles?: ReactNode;
  /** When provided, node uses this size (e.g. from React Flow width/height). Enables correct resize behavior. */
  dimensions?: { width: number; height: number };
};

export function BaseNode({
  className,
  style,
  dimensions,
  resizable,
  nodeId,
  handles,
  children,
  ...props
}: BaseNodeProps) {
  const hasSize =
    dimensions &&
    dimensions.width > 0 &&
    dimensions.height > 0;
  // Don't set overflow: hidden on the root — handles are positioned at left/right -10px
  // and would be clipped. The inner content wrapper has overflow-hidden for scrolling.
  const appliedStyle = hasSize
    ? {
      ...style,
      width: dimensions.width,
      height: dimensions.height,
      display: "flex" as const,
      flexDirection: "column" as const,
      contain: "layout" as const,
    }
    : style
      ? { ...style, display: "flex", flexDirection: "column" as const }
      : undefined;

  return (
    <div
      className={cn(
        "bg-card text-card-foreground relative rounded-md border",
        "hover:ring-1",
        // React Flow displays node elements inside of a `NodeWrapper` component,
        // which compiles down to a div with the class `react-flow__node`.
        // When a node is selected, the class `selected` is added to the
        // `react-flow__node` element. This allows us to style the node when it
        // is selected, using Tailwind's `&` selector.
        "[.react-flow\\_\\_node.selected_&]:border-muted-foreground",
        "[.react-flow\\_\\_node.selected_&]:shadow-lg",
        className,
      )}
      style={appliedStyle}
      tabIndex={0}
      {...props}
    >
      <div
        className="min-h-0 flex-1 flex flex-col overflow-hidden"
        style={{ contain: "layout" }}
      >
        {children}
      </div>
      {resizable && nodeId && (
        <NodeResizeControl
          nodeId={nodeId}
          position="bottom-right"
          minWidth={120}
          minHeight={80}
          className={cn(
            "!border-0 !bg-transparent !rounded-none !p-0",
            "absolute bottom-0 right-0 w-8 h-8 cursor-se-resize select-none",
            "nodrag nopan touch-none",
          )}
          style={{ margin: 0, touchAction: "none", userSelect: "none" }}
        >
        </NodeResizeControl>
      )}
      {handles}
    </div>
  );
}

/**
 * A container for a consistent header layout intended to be used inside the
 * `<BaseNode />` component.
 */
export function BaseNodeHeader({
  className,
  ...props
}: ComponentProps<"header">) {
  return (
    <header
      {...props}
      className={cn(
        "shrink-0 mx-0 my-0 -mb-1 flex flex-row items-center justify-between gap-2 px-3 py-2",
        // Remove or modify these classes if you modify the padding in the
        // `<BaseNode />` component.
        className,
      )}
    />
  );
}

/**
 * Standard node header: icon on the left, title taking the rest. Use for all node types.
 * Inlined so it does not depend on BaseNodeHeader/BaseNodeHeaderTitle (avoids reference errors with HMR).
 */
export function BaseNodeHeaderRow({
  icon,
  title,
  className,
}: {
  icon: ReactNode;
  title: ReactNode;
  className?: string;
}) {
  return (
    <header
      className={cn(
        "shrink-0 mx-0 my-0 flex flex-row items-center justify-between gap-2 px-3 py-2",
        className,
      )}
    >
      {icon}
      <h3
        data-slot="base-node-title"
        className="user-select-none flex-1 font-mono"
      >
        {title}
      </h3>
    </header>
  );
}

/**
 * Single-line muted text for the footer. Use for status or hints in all node types.
 */
export function BaseNodeFooterText({
  className,
  ...props
}: ComponentProps<"div">) {
  return (
    <div
      className={cn("w-full text-xs text-muted-foreground", className)}
      data-slot="base-node-footer-text"
      {...props}
    />
  );
}

export function BaseNodeContent({
  className,
  ...props
}: ComponentProps<"div">) {
  return (
    <div
      data-slot="base-node-content"
      className={cn("min-h-0 flex-1 flex flex-col overflow-auto", className)}
      {...props}
    />
  );
}

export function BaseNodeFooter({ className, ...props }: ComponentProps<"div">) {
  return (
    <div
      data-slot="base-node-footer"
      className={cn(
        "shrink-0 flex flex-col items-center gap-y-2 border-t p-1",
        className,
      )}
      {...props}
    />
  );
}
