import { ChevronRightIcon } from "@heroicons/react/24/solid";
import { Link } from "@remix-run/react";
import { ClipboardCheckIcon, ClipboardIcon } from "lucide-react";
import React, { type ReactNode, createContext, forwardRef, useContext, useState } from "react";
import { useCopy } from "~/hooks/useCopy";
import { cn } from "~/utils/cn";
import { Popover, PopoverContent, PopoverVerticalEllipseTrigger } from "./Popover";
import { InfoIconTooltip, SimpleTooltip } from "./Tooltip";

const variants = {
  bright: {
    header: "bg-background-bright",
    headerCell: "px-3 py-2.5 pb-3 text-sm",
    cell: "group-hover/table-row:bg-background-hover group-has-[[tabindex='0']:focus]/table-row:bg-background-hover",
    cellSize: "px-3 py-3",
    cellText: "text-xs group-hover/table-row:text-text-bright",
    stickyCell: "bg-background-bright group-hover/table-row:bg-background-hover",
    menuButton:
      "bg-background-bright group-hover/table-row:bg-background-hover group-hover/table-row:ring-border-bright/70 group-has-[[tabindex='0']:focus]/table-row:bg-background-hover",
    menuButtonDivider: "group-hover/table-row:border-border-bright/70",
    rowSelected: "bg-background-hover group-hover/table-row:bg-background-hover",
  },
  "bright/no-hover": {
    header: "bg-transparent",
    headerCell: "px-3 py-2.5 pb-3 text-sm",
    cell: "group-hover/table-row:bg-transparent",
    cellSize: "px-3 py-3",
    cellText: "text-xs",
    stickyCell: "bg-background-bright",
    menuButton: "bg-background-bright",
    menuButtonDivider: "",
    rowSelected: "bg-background-hover",
  },
  dimmed: {
    header: "bg-background-dimmed",
    headerCell: "px-3 py-2.5 pb-3 text-sm",
    cell: "group-hover/table-row:bg-background-bright group-has-[[tabindex='0']:focus]/table-row:bg-background-bright",
    cellSize: "px-3 py-3",
    cellText: "text-xs group-hover/table-row:text-text-bright",
    stickyCell: "group-hover/table-row:bg-background-bright",
    menuButton:
      "bg-background-dimmed group-hover/table-row:bg-background-bright group-hover/table-row:ring-grid-bright group-has-[[tabindex='0']:focus]/table-row:bg-background-bright",
    menuButtonDivider: "group-hover/table-row:border-grid-bright",
    rowSelected: "bg-background-hover group-hover/table-row:bg-background-hover",
  },
  "compact/mono": {
    header: "bg-background-dimmed",
    headerCell: "px-2 py-1.5 text-sm",
    cell: "group-hover/table-row:bg-background-bright group-has-[[tabindex='0']:focus]/table-row:bg-background-bright",
    cellSize: "px-2 py-1.5",
    cellText: "text-xs font-mono group-hover/table-row:text-text-bright",
    stickyCell: "group-hover/table-row:bg-background-bright",
    menuButton:
      "bg-background-dimmed group-hover/table-row:bg-background-bright group-hover/table-row:ring-grid-bright group-has-[[tabindex='0']:focus]/table-row:bg-background-bright",
    menuButtonDivider: "group-hover/table-row:border-grid-bright",
    rowSelected: "bg-background-hover group-hover/table-row:bg-background-hover",
  },
} as const;

export type TableVariant = keyof typeof variants;

type TableProps = {
  containerClassName?: string;
  className?: string;
  children: ReactNode;
  fullWidth?: boolean;
  showTopBorder?: boolean;
  stickyHeader?: boolean;
};

// Add TableContext
const TableContext = createContext<{ variant: TableVariant }>({ variant: "dimmed" });

export const Table = forwardRef<HTMLTableElement, TableProps & { variant?: TableVariant }>(
  (
    {
      className,
      containerClassName,
      children,
      fullWidth,
      variant = "dimmed",
      showTopBorder = true,
      stickyHeader = false,
    },
    ref
  ) => {
    return (
      <TableContext.Provider value={{ variant }}>
        <div
          className={cn(
            "whitespace-nowrap scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control",
            stickyHeader ? "overflow-visible" : "overflow-x-auto",
            showTopBorder && "border-t",
            containerClassName,
            fullWidth && "w-full"
          )}
        >
          <table ref={ref} className={cn("w-full", className)}>
            {children}
          </table>
        </div>
      </TableContext.Provider>
    );
  }
);

type TableHeaderProps = {
  className?: string;
  children: ReactNode;
};

export const TableHeader = forwardRef<HTMLTableSectionElement, TableHeaderProps>(
  ({ className, children }, ref) => {
    const { variant } = useContext(TableContext);
    return (
      <thead
        ref={ref}
        className={cn(
          "safari-only sticky top-0 z-10 after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-grid-bright supports-[(-webkit-hyphens:none)]:after:content-none",
          variants[variant].header,
          className
        )}
      >
        {children}
      </thead>
    );
  }
);

type TableBodyProps = {
  className?: string;
  children?: ReactNode;
  style?: React.CSSProperties;
};

export const TableBody = forwardRef<HTMLTableSectionElement, TableBodyProps>(
  ({ className, children, style }, ref) => {
    return (
      <tbody ref={ref} className={cn("relative overflow-y-auto", className)} style={style}>
        {children}
      </tbody>
    );
  }
);

type TableRowProps = JSX.IntrinsicElements["tr"] & {
  className?: string;
  children: ReactNode;
  disabled?: boolean;
  isSelected?: boolean;
};

export const TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(
  ({ className, disabled, isSelected, children, ...props }, ref) => {
    const { variant } = useContext(TableContext);
    return (
      <tr
        ref={ref}
        {...props}
        className={cn(
          "group/table-row relative w-full outline-hidden after:absolute after:bottom-0 after:left-3 after:right-0 after:h-px after:bg-grid-dimmed",
          isSelected && variants[variant].rowSelected,
          disabled && "opacity-50",
          className
        )}
      >
        {children}
      </tr>
    );
  }
);

type TableCellBasicProps = {
  className?: string;
  alignment?: "left" | "center" | "right";
  children?: ReactNode;
  colSpan?: number;
};

type TableHeaderCellProps = TableCellBasicProps & {
  hiddenLabel?: boolean;
  tooltip?: ReactNode;
  disableTooltipHoverableContent?: boolean;
};

export const TableHeaderCell = forwardRef<HTMLTableCellElement, TableHeaderCellProps>(
  (
    {
      className,
      alignment = "left",
      children,
      colSpan,
      hiddenLabel = false,
      tooltip,
      disableTooltipHoverableContent = false,
    },
    ref
  ) => {
    const { variant } = useContext(TableContext);
    let alignmentClassName = "text-left";
    switch (alignment) {
      case "center":
        alignmentClassName = "text-center";
        break;
      case "right":
        alignmentClassName = "text-right";
        break;
    }

    const [isHovered, setIsHovered] = useState(false);

    return (
      <th
        ref={ref}
        scope="col"
        className={cn(
          "align-middle font-medium text-text-bright",
          variants[variant].headerCell,
          alignmentClassName,
          className
        )}
        colSpan={colSpan}
        tabIndex={-1}
        onMouseEnter={() => setIsHovered(true)}
        onMouseLeave={() => setIsHovered(false)}
      >
        {hiddenLabel ? (
          <span className="sr-only">{children}</span>
        ) : tooltip ? (
          <div
            className={cn("flex items-center gap-1", {
              "justify-center": alignment === "center",
              "justify-end": alignment === "right",
            })}
          >
            {children}
            <InfoIconTooltip
              content={tooltip}
              contentClassName="normal-case tracking-normal"
              enabled={isHovered}
              disableHoverableContent={disableTooltipHoverableContent}
            />
          </div>
        ) : (
          children
        )}
      </th>
    );
  }
);

type TableCellProps = TableCellBasicProps & {
  to?: string;
  onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
  hasAction?: boolean;
  isSticky?: boolean;
  actionClassName?: string;
  rowHoverStyle?: string;
  isSelected?: boolean;
  isTabbableCell?: boolean;
  children?: ReactNode;
  style?: React.CSSProperties;
};

export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
  (
    {
      className,
      actionClassName,
      alignment = "left",
      children,
      colSpan,
      to,
      onClick,
      hasAction = false,
      isSticky = false,
      isSelected,
      isTabbableCell = false,
      style,
    },
    ref
  ) => {
    let alignmentClassName = "text-left";
    switch (alignment) {
      case "center":
        alignmentClassName = "text-center";
        break;
      case "right":
        alignmentClassName = "text-right";
        break;
    }

    const { variant } = useContext(TableContext);
    const flexClasses = cn(
      "flex w-full whitespace-nowrap items-center text-text-dimmed",
      variants[variant].cellSize,
      variants[variant].cellText,
      alignment === "left"
        ? "justify-start text-left"
        : alignment === "center"
          ? "justify-center text-center"
          : "justify-end text-right"
    );

    return (
      <td
        ref={ref}
        className={cn(
          "safari-only text-xs text-text-dimmed has-[[tabindex='0']:focus]:before:absolute has-[[tabindex='0']:focus]:before:-top-px has-[[tabindex='0']:focus]:before:left-0 has-[[tabindex='0']:focus]:before:h-px has-[[tabindex='0']:focus]:before:w-3 has-[[tabindex='0']:focus]:before:bg-grid-dimmed has-[[tabindex='0']:focus]:after:absolute has-[[tabindex='0']:focus]:after:bottom-0 has-[[tabindex='0']:focus]:after:left-0 has-[[tabindex='0']:focus]:after:right-0 has-[[tabindex='0']:focus]:after:h-px has-[[tabindex='0']:focus]:after:bg-grid-dimmed",
          variants[variant].cellText,
          variants[variant].cell,
          to || onClick || hasAction
            ? "cursor-pointer"
            : cn("cursor-default align-middle", variants[variant].cellSize),
          !to && !onClick && alignmentClassName,
          isSticky && "[&:has([data-hidden-buttons])]:w-auto sticky right-0 bg-background-dimmed",
          isSticky && variants[variant].stickyCell,
          isSelected && variants[variant].rowSelected,
          !isSelected &&
            "group-hover/table-row:before:absolute group-hover/table-row:before:left-0 group-hover/table-row:before:-top-px group-hover/table-row:before:h-px group-hover/table-row:before:w-3 group-hover/table-row:before:bg-background-hover group-hover/table-row:after:absolute group-hover/table-row:after:bottom-0 group-hover/table-row:after:left-0 group-hover/table-row:after:h-px group-hover/table-row:after:w-3 group-hover/table-row:after:bg-background-hover group-focus-visible/table-row:bg-background-bright",
          className
        )}
        colSpan={colSpan}
        style={style}
      >
        {to ? (
          <Link
            to={to}
            className={cn("cursor-pointer focus:outline-hidden", flexClasses, actionClassName)}
            tabIndex={isTabbableCell ? 0 : -1}
          >
            {children}
          </Link>
        ) : onClick ? (
          <button
            onClick={onClick}
            className={cn("cursor-pointer focus:outline-hidden", flexClasses, actionClassName)}
            tabIndex={isTabbableCell ? 0 : -1}
          >
            {children}
          </button>
        ) : (
          <>{children}</>
        )}
      </td>
    );
  }
);

type CopyableTableCellProps = TableCellProps & {
  value: string;
};

export const CopyableTableCell = forwardRef<HTMLTableCellElement, CopyableTableCellProps>(
  ({ value, children, className, ...props }, ref) => {
    const [isHovered, setIsHovered] = useState(false);
    const { copy, copied } = useCopy(value);

    return (
      <TableCell ref={ref} className={className} {...props}>
        <div
          className="relative flex items-center"
          onMouseEnter={() => setIsHovered(true)}
          onMouseLeave={() => setIsHovered(false)}
        >
          {children}
          {isHovered && (
            <span
              onClick={(e) => {
                e.stopPropagation();
                e.preventDefault();
                copy();
              }}
              className="absolute -right-2 top-1/2 z-10 flex -translate-y-1/2 cursor-pointer"
            >
              <SimpleTooltip
                button={
                  <span
                    className={cn(
                      "flex size-6 items-center justify-center rounded border border-border-bright bg-background-hover",
                      copied
                        ? "text-green-500"
                        : "text-text-dimmed hover:border-border-bright hover:bg-background-raised hover:text-text-bright"
                    )}
                  >
                    {copied ? (
                      <ClipboardCheckIcon className="size-3.5" />
                    ) : (
                      <ClipboardIcon className="size-3.5" />
                    )}
                  </span>
                }
                content={copied ? "Copied!" : "Copy"}
                disableHoverableContent
              />
            </span>
          )}
        </div>
      </TableCell>
    );
  }
);

export const TableCellChevron = forwardRef<
  HTMLTableCellElement,
  {
    className?: string;
    to?: string;
    children?: ReactNode;
    isSticky?: boolean;
    onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
  }
>(({ className, to, children, isSticky, onClick }, ref) => {
  return (
    <TableCell
      className={className}
      isSticky={isSticky}
      to={to}
      onClick={onClick}
      ref={ref}
      alignment="right"
    >
      {children}
      <ChevronRightIcon className="size-4 text-text-dimmed transition group-hover:text-text-bright" />
    </TableCell>
  );
});

export const TableCellMenu = forwardRef<
  HTMLTableCellElement,
  TableCellProps & {
    className?: string;
    isSticky?: boolean;
    onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
    visibleButtons?: ReactNode;
    hiddenButtons?: ReactNode;
    popoverContent?: ReactNode | ((close: () => void) => ReactNode);
    children?: ReactNode;
    isSelected?: boolean;
  }
>(
  (
    {
      className,
      isSticky,
      onClick,
      visibleButtons,
      hiddenButtons,
      popoverContent,
      children,
      isSelected,
    },
    ref
  ) => {
    const [isOpen, setIsOpen] = useState(false);
    const { variant } = useContext(TableContext);
    const resolvedContent =
      typeof popoverContent === "function"
        ? popoverContent(() => setIsOpen(false))
        : popoverContent;

    return (
      <TableCell
        className={className}
        isSticky={isSticky}
        onClick={onClick}
        ref={ref}
        alignment="right"
        hasAction={true}
        isSelected={isSelected}
      >
        <div className="relative h-full p-1">
          <div
            className={cn(
              "absolute right-0 top-1/2 mr-1 flex -translate-y-1/2 items-center justify-end gap-0.5 rounded-[0.25rem] p-0.5 group-hover/table-row:ring-1",
              variants[variant].menuButton
            )}
          >
            {/* Hidden buttons that show on hover */}
            {hiddenButtons && (
              <div
                data-hidden-buttons
                className={cn(
                  "hidden group-hover/table-row:block",
                  popoverContent && "pr-0.5 group-hover/table-row:border-r",
                  variants[variant].menuButtonDivider
                )}
              >
                <div className={cn("flex items-center gap-x-0.5 divide-x divide-grid-bright")}>
                  {hiddenButtons}
                </div>
              </div>
            )}
            {/* Always visible buttons  */}
            {visibleButtons}
            {/* Always visible popover with ellipsis trigger */}
            {resolvedContent && (
              <Popover open={isOpen} onOpenChange={(open) => setIsOpen(open)}>
                <PopoverVerticalEllipseTrigger
                  isOpen={isOpen}
                  className="duration-0 group-hover/table-row:text-text-bright"
                />
                <PopoverContent
                  className="min-w-40 max-w-80 overflow-y-auto p-0 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control"
                  align="end"
                >
                  {typeof popoverContent === "function" ? (
                    resolvedContent
                  ) : (
                    <div className="flex flex-col gap-1 p-1">{resolvedContent}</div>
                  )}
                </PopoverContent>
              </Popover>
            )}
          </div>
        </div>
      </TableCell>
    );
  }
);

type TableBlankRowProps = {
  className?: string;
  colSpan: number;
  children?: ReactNode;
};

export const TableBlankRow = forwardRef<HTMLTableRowElement, TableBlankRowProps>(
  ({ children, colSpan, className }, ref) => {
    return (
      <tr ref={ref}>
        <td colSpan={colSpan} className={cn("py-6 text-center text-sm", className)}>
          {children}
        </td>
      </tr>
    );
  }
);
