"use client";

import { evaluate } from "@mdx-js/mdx";
import { MDXProvider, useMDXComponents } from "@mdx-js/react";
import type { MDXComponents } from "mdx/types";
import { useEffect, useState, type ComponentType, type ReactNode } from "react";
import * as runtime from "react/jsx-runtime";
import rehypeHighlight from "rehype-highlight";
import remarkGfm from "remark-gfm";
import { mdxComponents } from "~/components/mdx-components";

type MdxModule = {
  default: ComponentType<{ components?: MDXComponents }>;
};

type ClientMdxProps = {
  source: string;
  components?: MDXComponents;
  format?: "md" | "mdx";
  fallback?: ReactNode;
  errorFallback?: (error: Error) => ReactNode;
};

export function ClientMdx({
  source,
  components = mdxComponents,
  format = "md",
  fallback = null,
  errorFallback,
}: ClientMdxProps) {
  const [Content, setContent] = useState<MdxModule["default"] | null>(null);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    let cancelled = false;
    const trimmed = source.trim();

    if (!trimmed) {
      setContent(null);
      setError(null);
      return;
    }

    void evaluate(trimmed, {
      ...runtime,
      baseUrl: import.meta.url,
      format,
      useMDXComponents,
      rehypePlugins: [rehypeHighlight],
      remarkPlugins: [remarkGfm],
    })
      .then((mod) => {
        if (cancelled) return;
        setContent(() => (mod as MdxModule).default);
        setError(null);
      })
      .catch((nextError: unknown) => {
        if (cancelled) return;
        setContent(null);
        setError(nextError instanceof Error ? nextError : new Error("Failed to render MDX"));
      });

    return () => {
      cancelled = true;
    };
  }, [format, source]);

  if (error) {
    return errorFallback ? errorFallback(error) : <p>{source}</p>;
  }

  if (!Content) return <>{fallback}</>;

  return (
    <MDXProvider components={components}>
      <Content components={components} />
    </MDXProvider>
  );
}
