import clsx from "clsx";
import * as React from "react";
import {
  UNSAFE_FrameworkContext as FrameworkContext,
  generatePath,
  NavLink,
  useSearchParams,
  type UNSAFE_AssetsManifest as AssetsManifest,
} from "react-router";
import { composeEventHandlers, useDebugEvents } from "../../utils";
import { useComposedRefs } from "../../utils/mergeRefs";
import { TooltipSymbol } from "../Tooltip";
import * as styles from "./styles.module.css";

export const LinkContext = React.createContext<boolean>(false);
export const ButtonContext = React.createContext<boolean>(false);

export const handleEnterPress = (e) => {
  if (e.key === "Enter") {
    e.target.click();
  }
};

export type RouteRecord = {
  id: string;
  path: string;
  params?: Record<string, any>;
};
export function resolveRoute(to: RouteRecord, context: AssetsManifest) {
  if (to) {
    const routes = context?.routes || globalThis?.__reactRouterManifest?.routes;
    const route = routes?.[to.id];
    if (route) {
      const fallback = `/${route.path}`;
      try {
        const completePath = fullpath(lineage(routes, route)) || fallback;
        return generatePath(completePath, to.params || {});
      } catch (e) {
        console.error(e);
        return fallback;
      }
    }
    return `/${to.path}`;
  }
  return "";
}

function lineage(routes: Record<string, any>, route: any): any[] {
  const result: any[] = [];
  while (route) {
    result.push(route);
    if (!route.parentId) break;
    route = routes[route.parentId];
  }
  result.reverse();
  return result;
}

function fullpath(lineage: any[]) {
  const route = lineage.at(-1);

  // root
  if (lineage.length === 1 && route?.id === "root") return "/";

  // layout
  const isLayout = route && route.index !== true && route.path === undefined;
  if (isLayout) return undefined;

  return (
    "/" +
    lineage
      .map((route) => route.path?.replace(/^\//, "")?.replace(/\/$/, ""))
      .filter((path) => path !== undefined && path !== "")
      .join("/")
  );
}

export function InternalLink({
  className,
  to,
  query = [],
  prefetch = "intent",
  newTab = false,
  preserveSearchParams = false,
  ref,
  variant = "none",
  ...props
}: React.ComponentProps<"a"> & {
  "data-id"?: string;
  prefetch?: PrefetchBehavior;
  newTab?: boolean;
  to: RouteRecord;
  preserveSearchParams?: boolean;
  query: { key: string; value: string }[];
  variant?: "solid" | "subtle" | "outline" | "elevated" | "line" | "none";
}) {
  const [searchParams] = useSearchParams();
  const linkRef = React.useRef<any>(null);
  const framework = React.use(FrameworkContext)!;
  if (!framework) {
    throw new Error(
      "InternalLink requires FrameworkContext to be provided, please ensure you are using it within a Router.",
    );
  }
  const resolved = React.useMemo(() => {
    return resolveRoute(to, framework.manifest);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [to, framework.manifest]);
  const queryStr = React.useMemo(() => {
    const search = new URLSearchParams(
      preserveSearchParams ? searchParams : undefined,
    );
    query?.forEach(({ key, value }) => {
      search.set(key, value);
    });
    return search.size > 0 ? `?${search.toString()}` : "";
  }, [query, searchParams, preserveSearchParams]);

  const linkProps = {
    ...props,
    onClick: composeEventHandlers((e: any) => {
      e?.stopPropagation();
    }, props.onClick),
  };

  if (newTab) {
    linkProps.target = "_blank";
    linkProps.rel = "noopener noreferrer";
  }

  const finalRef = useComposedRefs(linkRef, ref);

  return (
    <NavLink
      ref={finalRef}
      className={({ isActive, isPending }) =>
        clsx(
          styles.link,
          className,
          isActive ? "_active" : null,
          isPending ? "_pending" : null,
        )
      }
      to={resolved + queryStr}
      data-variant={variant}
      data-component="InternalLink$Brevity"
      prefetch={prefetch}
      {...linkProps}
    />
  );
}
InternalLink[TooltipSymbol] = true;

export function ExternalLink({
  className,
  href,
  newTab = false,
  onClick,
  ref,
  ...rest
}: React.ComponentProps<"a"> & { newTab?: boolean; "data-id"?: string }) {
  const isChild = React.useContext(LinkContext);
  const props = useDebugEvents(rest);
  if (newTab) {
    props.target = "_blank";
    props.rel = "noopener noreferrer";
  }
  return (
    <LinkContext.Provider value={true}>
      {isChild ? (
        <span
          ref={ref}
          className={clsx(styles.link, className)}
          role="link"
          tabIndex={0}
          data-href={href}
          onKeyDown={handleEnterPress}
          onClick={(e) => {
            e.stopPropagation();
            e.preventDefault();
            if (onClick) {
              onClick(e as any);
            }
            const href = (e.target as any).dataset.href;
            if (href) {
              window.location.href = href;
            }
          }}
          {...props}
        />
      ) : (
        <a
          ref={ref}
          className={clsx(styles.link, className)}
          href={href}
          onClick={(e) => {
            e.stopPropagation();
            if (onClick) {
              onClick(e);
            }
          }}
          {...props}
        />
      )}
    </LinkContext.Provider>
  );
}
ExternalLink[TooltipSymbol] = true;

export type PrefetchBehavior = "intent" | "render" | "none";
