import { memo, useMemo, useState, useCallback } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { Check, Copy, Code2, PanelRightOpen } from "lucide-react";
import { cn } from "@/lib/utils";

interface MarkdownRendererProps {
  content: string;
  className?: string;
  /** Called when user clicks to open a code block in a side panel. */
  onOpenCodePanel?: (payload: {
    code: string;
    language: string;
    lineCount: number;
    title?: string;
  }) => void;
}

/** Extract plain text from react-markdown children (string or array). */
function extractText(children: React.ReactNode): string {
  if (typeof children === "string") return children;
  if (Array.isArray(children)) {
    return children.map((c) => (typeof c === "string" ? c : "")).join("");
  }
  return "";
}

/**
 * Code block wrapped in Artifact-style container.
 */
const ArtifactCodeBlock = memo(function ArtifactCodeBlock({
  language,
  children,
  onOpen,
}: {
  language: string;
  children: string;
  onOpen?: () => void;
}) {
  const [copied, setCopied] = useState(false);
  const lines = useMemo(
    () => children.replace(/\n$/, "").split("\n"),
    [children],
  );

  const handleCopy = useCallback(() => {
    navigator.clipboard
      .writeText(children)
      .then(() => {
        setCopied(true);
        setTimeout(() => setCopied(false), 2000);
      })
      .catch(() => {});
  }, [children]);

  return (
    <div
      className={cn(
        "not-prose my-4 overflow-hidden rounded-xl border bg-card shadow-sm transition-colors",
        onOpen
          ? "cursor-pointer border-border/60 hover:border-primary/40"
          : "border-border",
      )}
      onClick={onOpen}
      role={onOpen ? "button" : undefined}
      tabIndex={onOpen ? 0 : undefined}
      onKeyDown={
        onOpen
          ? (e: React.KeyboardEvent) => {
              if (e.key === "Enter" || e.key === " ") {
                e.preventDefault();
                onOpen();
              }
            }
          : undefined
      }
      aria-label={onOpen ? `Open ${language || "code"} in side panel` : undefined}
    >
      {/* Header */}
      <div className="flex items-center justify-between border-b border-border/60 bg-muted/40 px-4 py-2.5">
        <div className="flex items-center gap-2">
          <span className="grid size-6 place-items-center rounded-md bg-muted">
            <Code2 className="size-3.5 text-muted-foreground" />
          </span>
          <span className="text-xs font-medium text-foreground">
            {language || "code"}
          </span>
          <span className="hidden sm:inline text-[11px] text-muted-foreground/50">
            {lines.length} line{lines.length !== 1 ? "s" : ""}
          </span>
        </div>
        <div className="flex items-center gap-1">
          {onOpen && (
            <span className="inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs text-muted-foreground">
              <PanelRightOpen className="size-3" />
              Open in panel
            </span>
          )}
          {!onOpen && (
            <button
              className={cn(
                "inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium transition-colors",
                copied
                  ? "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400"
                  : "text-muted-foreground hover:bg-muted hover:text-foreground",
              )}
              onClick={(e) => {
                e.stopPropagation();
                handleCopy();
              }}
              type="button"
            >
              {copied ? (
                <>
                  <Check className="size-3" />
                  Copied
                </>
              ) : (
                <>
                  <Copy className="size-3" />
                  Copy
                </>
              )}
            </button>
          )}
        </div>
      </div>

      {/* Content with line numbers — only when no side panel (inline mode) */}
      {!onOpen && (
        <div className="grid min-w-max grid-cols-[auto_1fr]">
          <div
            aria-hidden="true"
            className="select-none border-r border-border/40 bg-muted/20 px-3 py-3 text-right font-mono text-xs tabular-nums leading-[1.65] text-muted-foreground/40"
          >
            {lines.map((_, i) => (
              <div key={i}>{i + 1}</div>
            ))}
          </div>
          <pre className="m-0 overflow-x-auto px-4 py-3 font-mono text-sm leading-[1.65] text-foreground/90">
            <code className={cn(language && `language-${language}`)}>
              {children.replace(/\n$/, "")}
            </code>
          </pre>
        </div>
      )}

      {/* Compact summary when panel mode */}
      {onOpen && (
        <div className="px-4 py-2.5">
          <p className="text-xs text-muted-foreground/60">
            {lines[0]?.slice(0, 80)}{(lines[0]?.length ?? 0) > 80 ? "…" : ""}
          </p>
        </div>
      )}
    </div>
  );
});

export const MarkdownRenderer = memo(function MarkdownRenderer({
  content,
  className = "",
  onOpenCodePanel,
}: MarkdownRendererProps) {
  const components = useMemo(
    () => ({
      code({
        className: codeClassName,
        children,
        ...props
      }: {
        className?: string;
        children: React.ReactNode;
        node?: unknown;
      }) {
        const match = /language-(\w+)/.exec(codeClassName || "");
        const codeContent = extractText(children).replace(/\n$/, "");
        const isInline = !match && !codeContent.includes("\n");

        if (isInline) {
          return (
            <code
              className="rounded bg-muted px-1.5 py-0.5 font-mono text-[0.9em] text-foreground/90"
              {...props}
            >
              {extractText(children)}
            </code>
          );
        }

        return (
          <ArtifactCodeBlock
            language={match?.[1] || ""}
            onOpen={
              onOpenCodePanel
                ? () =>
                    onOpenCodePanel({
                      code: codeContent,
                      language: match?.[1] || "text",
                      lineCount: codeContent.split("\n").length,
                    })
                : undefined
            }
          >
            {codeContent}
          </ArtifactCodeBlock>
        );
      },
      pre({ children }: { children: React.ReactNode }) {
        return <>{children}</>;
      },
    }),
    [onOpenCodePanel],
  );

  return (
    <div
      className={cn(
        "prose prose-sm dark:prose-invert max-w-none",
        className,
      )}
    >
      <ReactMarkdown
        components={components as any}
        remarkPlugins={[remarkGfm]}
      >
        {content}
      </ReactMarkdown>
    </div>
  );
});

export default MarkdownRenderer;
