import type { ReactNode } from "react";

import { CopyOutlined } from "@ant-design/icons";
import { App, Button } from "antd";
import { useCallback, useEffect, useState } from "react";
import { codeToHtml } from "shiki";

import { useIsDark } from "../../../shared/hooks/use-is-dark";

interface CodeBlockProps {
  children: ReactNode;
  className?: string;
  isStreaming: boolean;
}

export function CodeBlock({ children, className: _className, isStreaming }: CodeBlockProps) {
  const { message } = App.useApp();
  const isDark = useIsDark();
  const [highlighted, setHighlighted] = useState<null | string>(null);
  const { codeText, lang } = extractCode(children);

  const handleCopy = useCallback(() => {
    navigator.clipboard.writeText(codeText).then(
      () => message.success("已复制"),
      () => message.error("复制失败"),
    );
  }, [codeText, message]);

  useEffect(() => {
    if (isStreaming || !codeText) return;

    let cancelled = false;
    codeToHtml(codeText, {
      lang,
      theme: isDark ? "github-dark" : "github-light",
    })
      .then((html) => {
        if (!cancelled) setHighlighted(html);
      })
      .catch(() => {
        if (!cancelled) setHighlighted(null);
      });

    return () => {
      cancelled = true;
    };
  }, [codeText, lang, isDark, isStreaming]);

  if (isStreaming) {
    return (
      <pre className="code-block">
        <code>{codeText}</code>
      </pre>
    );
  }

  return (
    <div className="code-block">
      <div className="code-block-header">
        <span className="code-block-lang">{lang}</span>
        <Button className="btn-dimmed" icon={<CopyOutlined />} onClick={handleCopy} size="small" type="text" />
      </div>
      {highlighted ? (
        <div className="code-block-body" dangerouslySetInnerHTML={{ __html: highlighted }} />
      ) : (
        <div className="code-block-body">
          <pre>
            <code>{codeText}</code>
          </pre>
        </div>
      )}
    </div>
  );
}

function extractCode(children: ReactNode): { codeText: string; lang: string } {
  if (children && typeof children === "object" && "props" in children && children.props) {
    const props = children.props as Record<string, unknown>;
    const codeText = typeof props["children"] === "string" ? props["children"] : "";
    const codeClassName = typeof props["className"] === "string" ? props["className"] : "";
    const classes = codeClassName.split(/\s+/);
    const langClass = classes.find((c) => c.startsWith("lang-") || c.startsWith("language-")) ?? "";
    const lang = langClass.replace(/^(language|lang)-/, "") || "text";
    return { codeText, lang };
  }
  return { codeText: typeof children === "string" ? children : "", lang: "text" };
}
