"use client";

import { Button } from "@mdxui/primitives/button";
import { Card } from "@mdxui/primitives/card";
import { cn } from "@mdxui/primitives/lib/utils";
import {
	Select,
	SelectContent,
	SelectItem,
	SelectTrigger,
	SelectValue,
} from "@mdxui/primitives/select";
import { Check, Copy } from "lucide-react";
import type React from "react";
import { useCallback, useEffect, useState } from "react";
import {
	type BundledLanguage,
	type BundledTheme,
	createHighlighter,
	type Highlighter,
} from "shiki";

export interface CodeEditorProps {
	/** Initial code content */
	initialValue?: string;
	/** Programming language */
	language?: BundledLanguage;
	/** Callback when content changes */
	onChange?: (value: string) => void;
	/** Placeholder text */
	placeholder?: string;
	/** Height of the editor */
	height?: string | number;
	/** Additional CSS class */
	className?: string;
	/** Whether the editor is read-only */
	readOnly?: boolean;
	/** Show line numbers */
	showLineNumbers?: boolean;
	/** Theme for syntax highlighting */
	theme?: BundledTheme;
	/** Enable language selector */
	showLanguageSelector?: boolean;
	/** Available languages for selector */
	availableLanguages?: BundledLanguage[];
}

const COMMON_LANGUAGES: BundledLanguage[] = [
	"typescript",
	"javascript",
	"tsx",
	"jsx",
	"json",
	"html",
	"css",
	"python",
	"rust",
	"go",
	"java",
	"markdown",
];

export const CodeEditor: React.FC<CodeEditorProps> = ({
	initialValue = "",
	language = "typescript",
	onChange,
	placeholder = "// Start coding...",
	height = 400,
	className,
	readOnly = false,
	showLineNumbers = true,
	theme = "github-dark",
	showLanguageSelector = false,
	availableLanguages = COMMON_LANGUAGES,
}) => {
	const [content, setContent] = useState(initialValue);
	const [currentLanguage, setCurrentLanguage] = useState(language);
	const [highlighter, setHighlighter] = useState<Highlighter | null>(null);
	const [copied, setCopied] = useState(false);

	useEffect(() => {
		let mounted = true;

		createHighlighter({
			themes: [theme],
			langs: availableLanguages,
		}).then((h) => {
			if (mounted) {
				setHighlighter(h);
			}
		});

		return () => {
			mounted = false;
		};
	}, [theme, availableLanguages]);

	const handleChange = useCallback(
		(value: string) => {
			setContent(value);
			onChange?.(value);
		},
		[onChange],
	);

	const handleCopy = useCallback(async () => {
		try {
			await navigator.clipboard.writeText(content);
			setCopied(true);
			setTimeout(() => setCopied(false), 2000);
		} catch (err) {
			console.error("Failed to copy:", err);
		}
	}, [content]);

	const highlightedCode = highlighter
		? highlighter.codeToHtml(content || placeholder, {
				lang: currentLanguage,
				theme,
			})
		: "";

	return (
		<Card className={cn("overflow-hidden", className)}>
			<div className="flex items-center justify-between border-b border-border p-2">
				<div className="flex items-center gap-2">
					{showLanguageSelector && (
						<Select
							value={currentLanguage}
							onValueChange={(val) =>
								setCurrentLanguage(val as BundledLanguage)
							}
						>
							<SelectTrigger className="w-40">
								<SelectValue />
							</SelectTrigger>
							<SelectContent>
								{availableLanguages.map((lang) => (
									<SelectItem key={lang} value={lang}>
										{lang}
									</SelectItem>
								))}
							</SelectContent>
						</Select>
					)}
					{!showLanguageSelector && (
						<span className="text-sm text-muted-foreground">
							{currentLanguage}
						</span>
					)}
				</div>
				<Button
					variant="ghost"
					size="sm"
					onClick={handleCopy}
					title="Copy code"
				>
					{copied ? (
						<Check className="h-4 w-4 text-green-500" />
					) : (
						<Copy className="h-4 w-4" />
					)}
				</Button>
			</div>

			<div className="relative">
				<textarea
					className={cn(
						"absolute inset-0 z-10 w-full resize-none p-4 font-mono text-sm caret-foreground outline-none",
						"bg-transparent text-transparent selection:bg-primary/20",
						showLineNumbers && "pl-14",
						readOnly && "cursor-not-allowed",
					)}
					value={content}
					onChange={(e) => handleChange(e.target.value)}
					placeholder={placeholder}
					readOnly={readOnly}
					spellCheck={false}
					style={{
						height: typeof height === "number" ? `${height}px` : height,
					}}
				/>

				<div
					className={cn(
						"pointer-events-none overflow-auto",
						showLineNumbers && "flex",
					)}
					style={{
						height: typeof height === "number" ? `${height}px` : height,
					}}
				>
					{showLineNumbers && (
						<div className="w-14 select-none border-r border-border bg-muted/30 py-4 text-right">
							{content.split("\n").map((_, i) => (
								<div key={i.toString()} className="px-2 text-xs text-muted-foreground">
									{i + 1}
								</div>
							))}
						</div>
					)}
					<div
						className="min-w-0 flex-1"
						// biome-ignore lint/security/noDangerouslySetInnerHtml: we need to set the inner HTML for the code highlighting
						dangerouslySetInnerHTML={{ __html: highlightedCode }}
					/>
				</div>
			</div>
		</Card>
	);
};
