import { cva, type VariantProps } from "class-variance-authority";
import { forwardRef } from "react";
import { cn } from "@/lib/utils";

/**
 * Code Block component for displaying code snippets and inline code
 *
 * @example
 * ```tsx
 * <CodeBlock>const greeting = "Hello, world!";</CodeBlock>
 *
 * <CodeBlock variant="inline">const x = 10;</CodeBlock>
 *
 * <CodeBlock language="python" showLineNumbers>
 *   def hello_world():
 *       print("Hello, world!")
 * </CodeBlock>
 * ```
 */

const codeBlockVariants = cva("font-mono text-sm rounded-md", {
	variants: {
		variant: {
			default: "bg-muted text-foreground p-4 overflow-x-auto",
			inline: "bg-muted px-1.5 py-0.5 text-primary-foreground",
			terminal: "bg-black text-white p-4",
		},
		language: {
			none: "",
			js: "",
			ts: "",
			jsx: "",
			tsx: "",
			html: "",
			css: "",
			python: "",
			ruby: "",
			java: "",
			csharp: "",
			go: "",
			rust: "",
			php: "",
			swift: "",
			shell: "",
			sql: "",
			markdown: "",
			json: "",
			yaml: "",
		},
	},
	defaultVariants: {
		variant: "default",
		language: "none",
	},
});

export interface CodeBlockProps
	extends React.HTMLAttributes<HTMLPreElement>,
		VariantProps<typeof codeBlockVariants> {
	/**
	 * Whether to show line numbers
	 */
	showLineNumbers?: boolean;
	/**
	 * Whether this is a single line of code (uses inline styling)
	 */
	inline?: boolean;
	/**
	 * Optional title for the code block
	 */
	title?: string;
}

const CodeBlock = forwardRef<HTMLPreElement, CodeBlockProps>(
	(
		{
			className,
			variant = "default",
			language,
			showLineNumbers = false,
			inline = false,
			title,
			children,
			...props
		},
		ref,
	) => {
		// If inline is true, override variant to "inline"
		const actualVariant = inline ? "inline" : variant;

		// If inline, render as inline code element
		if (inline) {
			return (
				<code
					className={cn(
						codeBlockVariants({ variant: actualVariant, language, className }),
					)}
					{...props}
				>
					{children}
				</code>
			);
		}

		// Process code content for line numbers if needed
		let codeContent = children;
		if (showLineNumbers && typeof children === "string") {
			const lines = children.toString().trim().split("\n");
			const lineCount = lines.length;
			const lineNumberWidth = lineCount.toString().length;

			codeContent = (
				<div className="flex">
					<div className="flex-none pr-4 text-muted-foreground select-none border-r border-muted-foreground/20 mr-4">
						{lines.map((_, idx) => (
							<div key={idx} className="text-right">
								{(idx + 1).toString().padStart(lineNumberWidth, " ")}
							</div>
						))}
					</div>
					<div className="flex-1">
						{lines.map((line, idx) => (
							<div key={idx}>{line || " "}</div>
						))}
					</div>
				</div>
			);
		}

		return (
			<div className="relative my-6 rounded-md overflow-hidden">
				{title && (
					<div className="bg-muted-foreground/10 text-muted-foreground px-4 py-1.5 text-sm font-medium border-b border-border">
						{title}
					</div>
				)}
				<pre
					className={cn(
						codeBlockVariants({ variant: actualVariant, language, className }),
						showLineNumbers && "pl-0",
					)}
					ref={ref}
					{...props}
				>
					{language && (
						<div className="absolute right-2 top-2 bg-muted-foreground/20 text-muted-foreground px-2 py-0.5 rounded text-xs uppercase">
							{language}
						</div>
					)}
					{codeContent}
				</pre>
			</div>
		);
	},
);

CodeBlock.displayName = "CodeBlock";

export { CodeBlock, codeBlockVariants };
