'use client';
import { Check, Clipboard } from 'lucide-react';
import {
  type ComponentProps,
  createContext,
  type HTMLAttributes,
  type ReactNode,
  type RefObject,
  useContext,
  useMemo,
  useRef,
} from 'react';
import { cn } from '../lib/cn';
import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button';
import { buttonVariants } from './ui/button';
import {
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger,
} from './tabs.unstyled';
import { mergeRefs } from '../lib/merge-refs';

export interface CodeBlockProps extends ComponentProps<'figure'> {
  /**
   * Icon of code block
   *
   * When passed as a string, it assumes the value is the HTML of icon
   */
  icon?: ReactNode;

  /**
   * Allow to copy code with copy button
   *
   * @defaultValue true
   */
  allowCopy?: boolean;

  /**
   * Keep original background color generated by Shiki or Rehype Code
   *
   * @defaultValue false
   */
  keepBackground?: boolean;

  viewportProps?: HTMLAttributes<HTMLElement>;

  /**
   * show line numbers
   */
  'data-line-numbers'?: boolean;

  /**
   * @defaultValue 1
   */
  'data-line-numbers-start'?: number;

  Actions?: (props: { className?: string; children?: ReactNode }) => ReactNode;
}

const TabsContext = createContext<{
  containerRef: RefObject<HTMLDivElement | null>;
  nested: boolean;
} | null>(null);

export function Pre(props: ComponentProps<'pre'>) {
  return (
    <pre
      {...props}
      className={cn('min-w-full w-max *:flex *:flex-col', props.className)}
    >
      {props.children}
    </pre>
  );
}

export function CodeBlock({
  ref,
  title,
  allowCopy = true,
  keepBackground = false,
  icon,
  viewportProps = {},
  children,
  Actions = (props) => (
    <div {...props} className={cn('empty:hidden', props.className)} />
  ),
  ...props
}: CodeBlockProps) {
  const inTab = useContext(TabsContext) !== null;
  const areaRef = useRef<HTMLDivElement>(null);

  return (
    <figure
      ref={ref}
      dir="ltr"
      {...props}
      className={cn(
        inTab
          ? 'bg-fd-secondary -mx-px -mb-px last:rounded-b-xl'
          : 'my-4 bg-fd-card rounded-xl',
        keepBackground && 'bg-(--shiki-light-bg) dark:bg-(--shiki-dark-bg)',

        'shiki relative border shadow-sm outline-none not-prose overflow-hidden text-sm',
        props.className,
      )}
    >
      {title ? (
        <div className="flex text-fd-muted-foreground items-center gap-2 h-9.5 border-b px-4">
          {typeof icon === 'string' ? (
            <div
              className="[&_svg]:size-3.5"
              dangerouslySetInnerHTML={{
                __html: icon,
              }}
            />
          ) : (
            icon
          )}
          <figcaption className="flex-1 truncate">{title}</figcaption>
          {Actions({
            className: '-me-2',
            children: allowCopy && <CopyButton containerRef={areaRef} />,
          })}
        </div>
      ) : (
        Actions({
          className:
            'absolute top-2 right-2 z-2 backdrop-blur-lg rounded-lg text-fd-muted-foreground',
          children: allowCopy && <CopyButton containerRef={areaRef} />,
        })
      )}
      <div
        ref={areaRef}
        {...viewportProps}
        className={cn(
          'text-[13px] py-3.5 overflow-auto max-h-[600px] fd-scroll-container',
          viewportProps.className,
        )}
        style={
          {
            // space for toolbar
            '--padding-right': !title ? 'calc(var(--spacing) * 8)' : undefined,
            counterSet: props['data-line-numbers']
              ? `line ${Number(props['data-line-numbers-start'] ?? 1) - 1}`
              : undefined,
            ...viewportProps.style,
          } as object
        }
      >
        {children}
      </div>
    </figure>
  );
}

function CopyButton({
  className,
  containerRef,
  ...props
}: ComponentProps<'button'> & {
  containerRef: RefObject<HTMLElement | null>;
}) {
  const [checked, onClick] = useCopyButton(() => {
    const pre = containerRef.current?.getElementsByTagName('pre').item(0);
    if (!pre) return;

    const clone = pre.cloneNode(true) as HTMLElement;
    clone.querySelectorAll('.nd-copy-ignore').forEach((node) => {
      node.replaceWith('\n');
    });

    void navigator.clipboard.writeText(clone.textContent ?? '');
  });

  return (
    <button
      type="button"
      data-checked={checked || undefined}
      className={cn(
        buttonVariants({
          className:
            'hover:text-fd-accent-foreground data-[checked]:text-fd-accent-foreground',
          size: 'icon-xs',
        }),
        className,
      )}
      aria-label={checked ? 'Copied Text' : 'Copy Text'}
      onClick={onClick}
      {...props}
    >
      {checked ? <Check /> : <Clipboard />}
    </button>
  );
}

export function CodeBlockTabs({ ref, ...props }: ComponentProps<typeof Tabs>) {
  const containerRef = useRef<HTMLDivElement>(null);
  const nested = useContext(TabsContext) !== null;

  return (
    <Tabs
      ref={mergeRefs(containerRef, ref)}
      {...props}
      className={cn(
        'bg-fd-card rounded-xl border',
        !nested && 'my-4',
        props.className,
      )}
    >
      <TabsContext.Provider
        value={useMemo(
          () => ({
            containerRef,
            nested,
          }),
          [nested],
        )}
      >
        {props.children}
      </TabsContext.Provider>
    </Tabs>
  );
}

export function CodeBlockTabsList(props: ComponentProps<typeof TabsList>) {
  return (
    <TabsList
      {...props}
      className={cn(
        'flex flex-row px-2 overflow-x-auto text-fd-muted-foreground',
        props.className,
      )}
    >
      {props.children}
    </TabsList>
  );
}

export function CodeBlockTabsTrigger({
  children,
  ...props
}: ComponentProps<typeof TabsTrigger>) {
  return (
    <TabsTrigger
      {...props}
      className={cn(
        'relative group inline-flex text-sm font-medium text-nowrap items-center transition-colors gap-2 px-2 py-1.5 hover:text-fd-accent-foreground data-[state=active]:text-fd-primary [&_svg]:size-3.5',
        props.className,
      )}
    >
      <div className="absolute inset-x-2 bottom-0 h-px group-data-[state=active]:bg-fd-primary" />
      {children}
    </TabsTrigger>
  );
}

// TODO: currently Vite RSC plugin has problem with `asChild` due to children is automatically wrapped in <Fragment />, maybe revisit this in future
export function CodeBlockTab(props: ComponentProps<typeof TabsContent>) {
  return <TabsContent {...props} />;
}
