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

import { FlowUIContext } from "@/lib/graph/flowContext";
import { useConnectionPathRoleFromStore } from "@/app/canvas/useCanvasConnectionPathFromStore";
import { cn } from "@/lib/utils";
import { NodeRunStatusOverlay } from "./NodeRunStatusOverlay";

/** Default min size for resizable nodes (used by NodeResizer). */
export const RESIZE_MIN_WIDTH = 120;
export const RESIZE_MIN_HEIGHT = 80;

export type BaseNodeProps = ComponentProps<"div"> & {
  /** When true, shows resize handles on corners/edges (via NodeResizer). 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 };
  /** When true, node is selected (from React Flow NodeProps). Used for visible selected state. */
  selected?: boolean;
  /** Optional min/max for NodeResizer. Omit to use RESIZE_MIN_WIDTH / RESIZE_MIN_HEIGHT. */
  resizeConstraints?: { minWidth?: number; minHeight?: number; maxWidth?: number; maxHeight?: number };
};

export function BaseNode({
  className,
  style,
  dimensions,
  resizable,
  nodeId,
  handles,
  children,
  selected,
  resizeConstraints,
  ...props
}: BaseNodeProps) {
  const flowUIContext = useContext(FlowUIContext);
  const isFullscreenInstance = Boolean(nodeId && flowUIContext?.fullscreenNodeId === nodeId);
  const connectionPathRole = useConnectionPathRoleFromStore(nodeId);
  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;

  const minW = resizeConstraints?.minWidth ?? RESIZE_MIN_WIDTH;
  const minH = resizeConstraints?.minHeight ?? RESIZE_MIN_HEIGHT;

  return (
    <div
      className={cn(
        "bg-card text-card-foreground relative rounded-md border transition-[border-color,box-shadow] duration-200",
        "hover:ring-1",
        selected && "border-primary/50 shadow-[0_0_0_2px_hsl(var(--primary)_/_0.15)] dark:border-primary/35 dark:shadow-[0_0_0_2px_hsl(var(--primary)_/_0.1)]",
        connectionPathRole === "trigger" && "connection-path-trigger",
        connectionPathRole === "on-path" && "connection-path-on-path",
        className,
      )}
      data-selected={selected}
      data-path-role={connectionPathRole ?? undefined}
      style={appliedStyle}
      tabIndex={selected ? 0 : -1}
      {...props}
    >
      <div
        className={cn(
          "min-h-0 flex-1 flex flex-col overflow-hidden",
          !selected && "pointer-events-none",
        )}
        style={{ contain: "layout" }}
      >
        {children}
      </div>
      {resizable && nodeId && !isFullscreenInstance && (
        <NodeResizer
          nodeId={nodeId}
          isVisible={selected}
          minWidth={minW}
          minHeight={minH}
          maxWidth={resizeConstraints?.maxWidth}
          maxHeight={resizeConstraints?.maxHeight}
          color="transparent"
          handleClassName="base-node-resize-handle nodrag nopan"
        />
      )}
      {!isFullscreenInstance && (
        <div className={cn(!selected && "pointer-events-none")}>{handles}</div>
      )}
      {nodeId && <NodeRunStatusOverlay nodeId={nodeId} />}
    </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,
  right,
  className,
  onHeaderDoubleClick,
}: {
  icon: ReactNode;
  title: ReactNode;
  /** Optional right-side content (e.g. type selector). */
  right?: ReactNode;
  className?: string;
  /** When provided, double-click on the header triggers this (e.g. fullscreen). Header gets cursor-pointer and nodrag. */
  onHeaderDoubleClick?: () => void;
}) {
  return (
    <header
      role={onHeaderDoubleClick ? 'button' : undefined}
      tabIndex={onHeaderDoubleClick ? 0 : undefined}
      onDoubleClick={onHeaderDoubleClick}
      onKeyDown={
        onHeaderDoubleClick
          ? (e) => {
            if (e.key === 'Enter' || e.key === ' ') {
              e.preventDefault();
              onHeaderDoubleClick();
            }
          }
          : undefined
      }
      className={cn(
        "shrink-0 mx-0 my-0 flex flex-row items-center justify-between gap-2 px-3 py-2",
        onHeaderDoubleClick && "cursor-pointer",
        className,
      )}
      title={onHeaderDoubleClick ? 'Double-click for full screen' : undefined}
    >
      {icon}
      <h3
        data-slot="base-node-title"
        className="user-select-none flex-1 font-mono min-w-0 truncate"
      >
        {title}
      </h3>
      {right != null ? <div className="shrink-0 nodrag nopan">{right}</div> : null}
    </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", 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 border-border/70 pt-1 px-1 pb-1",
        className,
      )}
      {...props}
    />
  );
}
