import { KeyRound, ListChecks, RefreshCw, Save, Trash2, Video, Wand2 } from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { useI18n } from "@/contexts/I18nContext";
import type {
	GuideAiProvider,
	GuideAiSettings,
	GuideLanguage,
	GuideOcrProfile,
	GuideSession,
	GuideSnapshot,
} from "@/guide/contracts";
import { captureGuideSnapshots } from "@/guide/snapshot/extractGuideSnapshots";

interface GuidePanelProps {
	recordingId: number | string | null;
	videoPath: string | null;
	videoSourcePath: string | null;
	currentTimeMs: number;
	onAttachToVideo?: (session: GuideSession) => void;
}

type BusyAction = "load" | "generate" | "attach";

interface GuideProgressState {
	label: string;
	current: number;
	total: number;
	detail?: string;
}

const COPY = {
	en: {
		title: "Guide",
		noRecording: "Record with Guide Mode to create a step guide.",
		noSession: "No guide session yet.",
		generateGuide: "Generate guide",
		generating: "Generating...",
		prepare: "Prepare",
		snapshots: "Snapshots",
		ocr: "OCR",
		draft: "Draft",
		deepseek: "DeepSeek",
		local: "Local",
		exportMd: "MD",
		exportHtml: "HTML",
		events: "events",
		shots: "shots",
		text: "text",
		steps: "steps",
		captureStep: "Capture step",
		captureLabel: "Manual capture",
		settings: "Settings",
		guideSettings: "Guide settings",
		apiKey: "API key env",
		apiKeyPlaceholder: "DEEPSEEK_API_KEY",
		baseUrl: "Base URL",
		model: "Model",
		ocrProfile: "OCR profile",
		ocrLanguage: "OCR languages",
		ocrFast: "Fast Latin",
		ocrVietnamese: "Vietnamese Enhanced",
		ocrHybrid: "Hybrid Vi + Latin",
		saveSettings: "Save",
		clearKey: "Reset env",
		settingsSaved: "Guide settings saved.",
		keyMissing: "Set a DeepSeek API key environment variable before generating with DeepSeek.",
		keyConfigured: "Env ready",
		keyNotConfigured: "Env value missing",
		ready: "Guide artifacts are ready.",
		noEvents: "No click events were captured for this guide.",
		ocrUnavailable: "Local OCR service is unavailable. You can still create a local draft.",
		exported: "Guide exported",
		attachToVideo: "Attach to video",
		attachedToVideo: "Guide steps attached to the video timeline.",
		noDraft: "Generate a guide draft before attaching steps to the video.",
		progressPreparing: "Preparing events",
		progressSnapshots: "Capturing snapshots",
		progressOcr: "Running OCR",
		progressDraft: "Writing draft",
		progressExport: "Exporting files",
	},
	vi: {
		title: "Hướng dẫn",
		noRecording: "Hãy quay bằng Guide Mode để tạo hướng dẫn từng bước.",
		noSession: "Chưa có guide session.",
		generateGuide: "Tạo hướng dẫn",
		generating: "Đang tạo...",
		prepare: "Chuẩn bị",
		snapshots: "Ảnh",
		ocr: "OCR",
		draft: "Draft",
		deepseek: "DeepSeek",
		local: "Local",
		exportMd: "MD",
		exportHtml: "HTML",
		events: "events",
		shots: "ảnh",
		text: "text",
		steps: "steps",
		captureStep: "Chụp bước",
		captureLabel: "Chụp thủ công",
		settings: "Cài đặt",
		guideSettings: "Guide settings",
		apiKey: "API key env",
		apiKeyPlaceholder: "DEEPSEEK_API_KEY",
		baseUrl: "Base URL",
		model: "Model",
		ocrProfile: "OCR profile",
		ocrLanguage: "OCR languages",
		ocrFast: "Fast Latin",
		ocrVietnamese: "Vietnamese Enhanced",
		ocrHybrid: "Hybrid Vi + Latin",
		saveSettings: "Lưu",
		clearKey: "Reset env",
		settingsSaved: "Da luu cai dat guide.",
		keyMissing: "Hãy set biến môi trường DeepSeek API key trước khi tạo draft bằng DeepSeek.",
		keyConfigured: "Env ready",
		keyNotConfigured: "Chưa thấy giá trị env",
		ready: "Đã sẵn sàng tài liệu hướng dẫn.",
		noEvents: "Chưa ghi nhận click event nào cho guide này.",
		ocrUnavailable: "OCR local chưa chạy. Vẫn có thể tạo draft local.",
		exported: "Đã export hướng dẫn",
		attachToVideo: "Gắn vào video",
		attachedToVideo: "Đã gắn các bước guide vào timeline video.",
		noDraft: "Hãy tạo draft guide trước khi gắn vào video.",
		progressPreparing: "Đang chuẩn bị events",
		progressSnapshots: "Đang chụp ảnh",
		progressOcr: "Đang OCR",
		progressDraft: "Đang tạo draft",
		progressExport: "Đang export file",
	},
} as const;

function getPendingOcrSnapshots(session: GuideSession): GuideSnapshot[] {
	const ocrCompletedSnapshotIds = new Set(session.ocrBlocks.map((block) => block.snapshotId));
	return session.snapshots.filter(
		(snapshot) => !snapshot.ocrCompletedAt && !ocrCompletedSnapshotIds.has(snapshot.id),
	);
}

function getProgressPercent(progress: GuideProgressState | null): number {
	if (!progress) {
		return 0;
	}
	if (progress.total <= 0) {
		return 100;
	}
	const percent = Math.round((progress.current / progress.total) * 100);
	return Math.min(100, Math.max(progress.current > 0 ? 8 : 4, percent));
}

export function GuidePanel({
	recordingId,
	videoPath,
	videoSourcePath,
	onAttachToVideo,
}: GuidePanelProps) {
	const { locale } = useI18n();
	const copy = useMemo(() => (locale.startsWith("vi") ? COPY.vi : COPY.en), [locale]);
	const guideLanguage: GuideLanguage = locale.startsWith("vi") ? "vi" : "en";
	const [session, setSession] = useState<GuideSession | null>(null);
	const [provider, setProvider] = useState<GuideAiProvider>("local");
	const [busyAction, setBusyAction] = useState<BusyAction | null>(null);
	const [aiSettings, setAiSettings] = useState<GuideAiSettings | null>(null);
	const [settingsOpen, setSettingsOpen] = useState(false);
	const [settingsBusy, setSettingsBusy] = useState(false);
	const [deepSeekApiKeyEnvName, setDeepSeekApiKeyEnvName] = useState("DEEPSEEK_API_KEY");
	const [deepSeekBaseUrl, setDeepSeekBaseUrl] = useState("https://api.deepseek.com");
	const [deepSeekModel, setDeepSeekModel] = useState("deepseek-chat");
	const [ocrProfile, setOcrProfile] = useState<GuideOcrProfile>("vietnamese");
	const [ocrLanguage, setOcrLanguage] = useState("vi,en");
	const [message, setMessage] = useState<string | null>(null);
	const [progress, setProgress] = useState<GuideProgressState | null>(null);

	const isBusy = busyAction !== null;
	const progressPercent = getProgressPercent(progress);
	const canUseGuide = Boolean(recordingId && videoSourcePath && window.electronAPI?.guide);
	const generatedSteps = session?.generatedGuide?.steps ?? [];
	const statusLabel = useMemo(() => {
		if (!session) {
			return copy.noSession;
		}
		return [
			`${session.events.length} ${copy.events}`,
			`${session.snapshots.length} ${copy.shots}`,
			`${session.ocrBlocks.length} ${copy.text}`,
			`${generatedSteps.length} ${copy.steps}`,
		].join(" / ");
	}, [copy, generatedSteps.length, session]);

	const loadAiSettings = useCallback(async () => {
		if (!window.electronAPI?.guide?.getAiSettings) {
			return;
		}
		const result = await window.electronAPI.guide.getAiSettings();
		if (!result.success) {
			setMessage(result.error);
			return;
		}
		setAiSettings(result.data);
		setDeepSeekBaseUrl(result.data.deepseek.baseUrl);
		setDeepSeekModel(result.data.deepseek.model);
		setDeepSeekApiKeyEnvName(result.data.deepseek.apiKeyEnvName);
		setOcrProfile(result.data.ocr.profile);
		setOcrLanguage(result.data.ocr.language);
	}, []);

	useEffect(() => {
		void loadAiSettings();
	}, [loadAiSettings]);

	const loadSession = useCallback(async () => {
		if (!recordingId || !window.electronAPI?.guide) {
			setSession(null);
			setBusyAction(null);
			return;
		}

		setBusyAction("load");
		const result = await window.electronAPI.guide.readSession(recordingId);
		setBusyAction(null);
		if (result.success) {
			setSession(result.data);
			setMessage(null);
			return;
		}
		if (result.code === "guide-session-not-found") {
			setSession(null);
			setMessage(null);
			return;
		}
		setMessage(result.error);
	}, [recordingId]);

	useEffect(() => {
		let cancelled = false;
		async function load() {
			if (!recordingId || !window.electronAPI?.guide) {
				setSession(null);
				setBusyAction(null);
				return;
			}
			setBusyAction("load");
			const result = await window.electronAPI.guide.readSession(recordingId);
			if (cancelled) {
				return;
			}
			setBusyAction(null);
			if (result.success) {
				setSession(result.data);
				setMessage(null);
			} else if (result.code === "guide-session-not-found") {
				setSession(null);
				setMessage(null);
			} else {
				setMessage(result.error);
			}
		}
		load();
		return () => {
			cancelled = true;
		};
	}, [recordingId]);

	const ensureEventsSession = useCallback(async (): Promise<GuideSession> => {
		if (!recordingId || !videoSourcePath) {
			throw new Error(copy.noRecording);
		}

		let current = session;
		const readResult = await window.electronAPI.guide.readSession(recordingId);
		if (readResult.success) {
			current = readResult.data;
		} else if (readResult.code === "guide-session-not-found") {
			current = null;
		} else if (!current) {
			throw new Error(readResult.error);
		}

		if (!current) {
			const startResult = await window.electronAPI.guide.startSession(recordingId);
			if (!startResult.success) {
				throw new Error(startResult.error);
			}
			current = startResult.data;
		}

		if (current.status === "recording" || current.videoPath !== videoSourcePath) {
			const finalizeResult = await window.electronAPI.guide.finalizeEvents({
				recordingId,
				videoPath: videoSourcePath,
			});
			if (!finalizeResult.success) {
				throw new Error(finalizeResult.error);
			}
			current = finalizeResult.data;
		}

		setSession(current);
		return current;
	}, [copy.noRecording, recordingId, session, videoSourcePath]);

	const runAction = useCallback(
		async (action: BusyAction, task: () => Promise<void>) => {
			if (!canUseGuide) {
				setMessage(copy.noRecording);
				return;
			}
			setBusyAction(action);
			setMessage(null);
			setProgress(null);
			try {
				await task();
			} catch (error) {
				const text = error instanceof Error ? error.message : String(error);
				setMessage(text);
				toast.error(text);
			} finally {
				setBusyAction(null);
			}
		},
		[canUseGuide, copy.noRecording],
	);

	const handleProviderChange = useCallback(
		(nextProvider: GuideAiProvider) => {
			setProvider(nextProvider);
			if (nextProvider === "deepseek" && !aiSettings?.deepseek.hasApiKey) {
				setSettingsOpen(true);
				setMessage(copy.keyMissing);
			}
		},
		[aiSettings?.deepseek.hasApiKey, copy.keyMissing],
	);

	const handleSaveAiSettings = useCallback(async () => {
		if (!window.electronAPI?.guide?.saveAiSettings) {
			return;
		}
		setSettingsBusy(true);
		setMessage(null);
		try {
			const result = await window.electronAPI.guide.saveAiSettings({
				deepseekApiKeyEnvName: deepSeekApiKeyEnvName,
				baseUrl: deepSeekBaseUrl,
				model: deepSeekModel,
				ocrProfile,
				ocrLanguage,
			});
			if (!result.success) {
				throw new Error(result.error);
			}
			setAiSettings(result.data);
			setDeepSeekApiKeyEnvName(result.data.deepseek.apiKeyEnvName);
			setDeepSeekBaseUrl(result.data.deepseek.baseUrl);
			setDeepSeekModel(result.data.deepseek.model);
			setOcrProfile(result.data.ocr.profile);
			setOcrLanguage(result.data.ocr.language);
			toast.success(copy.settingsSaved);
		} catch (error) {
			const text = error instanceof Error ? error.message : String(error);
			setMessage(text);
			toast.error(text);
		} finally {
			setSettingsBusy(false);
		}
	}, [
		copy.settingsSaved,
		deepSeekApiKeyEnvName,
		deepSeekBaseUrl,
		deepSeekModel,
		ocrLanguage,
		ocrProfile,
	]);

	const handleClearDeepSeekKey = useCallback(async () => {
		if (!window.electronAPI?.guide?.saveAiSettings) {
			return;
		}
		setSettingsBusy(true);
		setMessage(null);
		try {
			const result = await window.electronAPI.guide.saveAiSettings({
				clearDeepseekApiKeyEnvName: true,
				baseUrl: deepSeekBaseUrl,
				model: deepSeekModel,
				ocrProfile,
				ocrLanguage,
			});
			if (!result.success) {
				throw new Error(result.error);
			}
			setAiSettings(result.data);
			setDeepSeekApiKeyEnvName(result.data.deepseek.apiKeyEnvName);
			setOcrProfile(result.data.ocr.profile);
			setOcrLanguage(result.data.ocr.language);
			toast.success(copy.settingsSaved);
		} catch (error) {
			const text = error instanceof Error ? error.message : String(error);
			setMessage(text);
			toast.error(text);
		} finally {
			setSettingsBusy(false);
		}
	}, [copy.settingsSaved, deepSeekBaseUrl, deepSeekModel, ocrLanguage, ocrProfile]);

	const handleGenerateGuide = useCallback(() => {
		void runAction("generate", async () => {
			if (provider === "deepseek" && !aiSettings?.deepseek.hasApiKey) {
				setSettingsOpen(true);
				throw new Error(copy.keyMissing);
			}
			if (!videoPath) {
				throw new Error("Video URL is not available.");
			}
			setProgress({
				label: copy.progressPreparing,
				current: 0,
				total: 1,
				detail: "0/1",
			});
			let current = await ensureEventsSession();
			setProgress({
				label: copy.progressPreparing,
				current: 1,
				total: 1,
				detail: "1/1",
			});
			if (current.events.length === 0) {
				throw new Error(copy.noEvents);
			}
			const snapshotEventIds = new Set(current.snapshots.map((snapshot) => snapshot.eventId));
			const pendingSnapshotTotal = current.events.filter(
				(event) => !snapshotEventIds.has(event.id),
			).length;
			if (pendingSnapshotTotal > 0) {
				setProgress({
					label: copy.progressSnapshots,
					current: 0,
					total: pendingSnapshotTotal,
					detail: `0/${pendingSnapshotTotal}`,
				});
				current = await captureGuideSnapshots({
					session: current,
					videoUrl: videoPath,
					maxWidth: 1280,
					onProgress: ({ completed, total }) => {
						setProgress({
							label: copy.progressSnapshots,
							current: completed,
							total,
							detail: `${completed}/${total}`,
						});
					},
				});
				setSession(current);
			}
			const pendingOcrSnapshots = getPendingOcrSnapshots(current);
			for (const [index, snapshot] of pendingOcrSnapshots.entries()) {
				setProgress({
					label: copy.progressOcr,
					current: index,
					total: pendingOcrSnapshots.length,
					detail: `${index + 1}/${pendingOcrSnapshots.length}`,
				});
				const ocrResult = await window.electronAPI.guide.runOcr({
					recordingId: current.recordingId,
					snapshotIds: [snapshot.id],
				});
				if (!ocrResult.success) {
					if (ocrResult.code === "guide-ocr-unavailable") {
						toast.warning(copy.ocrUnavailable);
					}
					throw new Error(ocrResult.error);
				}
				current = ocrResult.data;
				setSession(current);
				setProgress({
					label: copy.progressOcr,
					current: index + 1,
					total: pendingOcrSnapshots.length,
					detail: `${index + 1}/${pendingOcrSnapshots.length}`,
				});
			}
			setProgress({
				label: copy.progressDraft,
				current: 0,
				total: 1,
				detail: "0/1",
			});
			const result = await window.electronAPI.guide.generateDraft({
				recordingId: current.recordingId,
				language: guideLanguage,
				provider,
			});
			if (!result.success) {
				throw new Error(result.error);
			}
			current = result.data;
			setSession(current);
			setProgress({
				label: copy.progressDraft,
				current: 1,
				total: 1,
				detail: "1/1",
			});
			setProgress({
				label: copy.progressExport,
				current: 0,
				total: 2,
				detail: "0/2",
			});
			const markdownResult = await window.electronAPI.guide.exportMarkdown({
				recordingId: current.recordingId,
			});
			if (!markdownResult.success) {
				throw new Error(markdownResult.error);
			}
			setProgress({
				label: copy.progressExport,
				current: 1,
				total: 2,
				detail: "1/2",
			});
			const htmlResult = await window.electronAPI.guide.exportHtml({
				recordingId: current.recordingId,
			});
			if (!htmlResult.success) {
				throw new Error(htmlResult.error);
			}
			setProgress({
				label: copy.progressExport,
				current: 2,
				total: 2,
				detail: "2/2",
			});
			const revealResult = await window.electronAPI.revealInFolder(htmlResult.data.path);
			if (!revealResult.success) {
				toast.warning(revealResult.error ?? "Unable to open guide folder.");
			}
			setSession(htmlResult.data.session);
			toast.success(copy.exported, {
				description: `${markdownResult.data.path}\n${htmlResult.data.path}`,
			});
		});
	}, [
		aiSettings?.deepseek.hasApiKey,
		copy.exported,
		copy.keyMissing,
		copy.noEvents,
		copy.ocrUnavailable,
		copy.progressDraft,
		copy.progressExport,
		copy.progressOcr,
		copy.progressPreparing,
		copy.progressSnapshots,
		ensureEventsSession,
		guideLanguage,
		provider,
		runAction,
		videoPath,
	]);

	const handleAttachToVideo = useCallback(() => {
		if (!session?.generatedGuide || session.generatedGuide.steps.length === 0) {
			setMessage(copy.noDraft);
			toast.error(copy.noDraft);
			return;
		}
		if (!onAttachToVideo) {
			return;
		}
		setBusyAction("attach");
		try {
			onAttachToVideo(session);
			setMessage(null);
			toast.success(copy.attachedToVideo);
		} finally {
			setBusyAction(null);
		}
	}, [copy.attachedToVideo, copy.noDraft, onAttachToVideo, session]);

	return (
		<section className="editor-inspector-shell flex max-h-[320px] min-h-[246px] shrink-0 flex-col overflow-hidden rounded-[18px] border border-white/[0.075] bg-[#090a0c]">
			<div className="flex items-center justify-between border-b border-white/[0.07] px-3 py-2">
				<div className="flex min-w-0 items-center gap-2">
					<ListChecks className="h-4 w-4 shrink-0 text-[#34B27B]" />
					<span className="truncate text-sm font-semibold text-slate-100">{copy.title}</span>
				</div>
				<button
					type="button"
					title="Reload guide session"
					disabled={isBusy || !recordingId}
					onClick={loadSession}
					className="flex h-7 w-7 items-center justify-center rounded-lg border border-transparent text-slate-500 transition-all hover:border-white/10 hover:bg-white/[0.06] hover:text-slate-200 disabled:cursor-not-allowed disabled:opacity-40"
				>
					<RefreshCw className={`h-3.5 w-3.5 ${busyAction === "load" ? "animate-spin" : ""}`} />
				</button>
			</div>

			<div className="min-h-0 flex-1 overflow-y-auto p-3 custom-scrollbar">
				<p className="mb-2 text-[11px] leading-4 text-slate-400">
					{canUseGuide ? statusLabel : copy.noRecording}
				</p>
				{message && <p className="mb-2 text-[11px] leading-4 text-amber-300">{message}</p>}
				{progress && (
					<div className="mb-2 rounded-md border border-white/[0.07] bg-white/[0.035] px-2 py-1.5">
						<div className="mb-1 flex items-center justify-between gap-2 text-[10px] leading-4">
							<span className="min-w-0 truncate font-semibold text-slate-200">
								{progress.label}
							</span>
							<span className="shrink-0 text-slate-500">
								{progress.detail ?? `${progress.current}/${progress.total}`}
							</span>
						</div>
						<div className="h-1.5 overflow-hidden rounded-full bg-white/[0.06]">
							<div
								className="h-full rounded-full bg-[#34B27B] transition-all duration-200"
								style={{ width: `${progressPercent}%` }}
							/>
						</div>
					</div>
				)}

				<div className="mb-2 flex items-center gap-1.5">
					<select
						value={provider}
						onChange={(event) => handleProviderChange(event.target.value as GuideAiProvider)}
						className="h-8 flex-1 rounded-md border border-white/[0.08] bg-white/[0.04] px-2 text-[11px] font-medium text-slate-200 outline-none"
						disabled={isBusy}
					>
						<option value="local">{copy.local}</option>
						<option value="deepseek">{copy.deepseek}</option>
					</select>
					<Button
						type="button"
						size="sm"
						title={copy.settings}
						disabled={isBusy}
						onClick={() => setSettingsOpen((current) => !current)}
						className="h-8 rounded-md border border-white/[0.08] bg-white/[0.04] px-2 text-[11px] text-slate-200 hover:bg-white/[0.08]"
					>
						<KeyRound className="h-3.5 w-3.5" />
					</Button>
				</div>

				<button
					type="button"
					disabled={!canUseGuide || isBusy}
					onClick={handleGenerateGuide}
					className="mb-2 flex h-10 w-full items-center justify-center gap-2 rounded-md border border-[#34B27B]/35 bg-[#34B27B]/20 px-3 text-sm font-semibold text-[#B9F5D2] transition-all hover:border-[#34B27B]/55 hover:bg-[#34B27B]/28 disabled:cursor-not-allowed disabled:opacity-40"
				>
					<Wand2
						className={`h-4 w-4 shrink-0 ${busyAction === "generate" ? "animate-pulse" : ""}`}
					/>
					<span className="truncate">
						{busyAction === "generate" ? copy.generating : copy.generateGuide}
					</span>
				</button>

				<button
					type="button"
					disabled={!generatedSteps.length || isBusy || !onAttachToVideo}
					onClick={handleAttachToVideo}
					className="mb-2 flex h-9 w-full items-center justify-center gap-2 rounded-md border border-sky-400/25 bg-sky-400/10 px-3 text-xs font-semibold text-sky-100 transition-all hover:border-sky-300/45 hover:bg-sky-400/18 disabled:cursor-not-allowed disabled:opacity-40"
				>
					<Video className="h-3.5 w-3.5 shrink-0" />
					<span className="truncate">{copy.attachToVideo}</span>
				</button>

				{settingsOpen && (
					<div className="mb-2 space-y-2 rounded-md border border-white/[0.07] bg-white/[0.035] p-2">
						<div className="flex items-center justify-between gap-2">
							<div className="min-w-0">
								<div className="truncate text-[11px] font-semibold text-slate-100">
									{copy.guideSettings}
								</div>
								<div className="truncate text-[10px] text-slate-500">
									{aiSettings?.deepseek.hasApiKey
										? `${copy.keyConfigured}: ${aiSettings.deepseek.apiKeyEnvName}`
										: `${copy.keyNotConfigured}: ${
												aiSettings?.deepseek.apiKeyEnvName ?? "DEEPSEEK_API_KEY"
											}`}
								</div>
							</div>
							<span className="shrink-0 rounded border border-white/[0.08] px-1.5 py-0.5 text-[10px] text-slate-500">
								{aiSettings?.deepseek.storage ?? "none"}
							</span>
						</div>

						<div className="grid grid-cols-2 gap-1.5">
							<label className="block min-w-0 text-[10px] font-medium text-slate-400">
								{copy.ocrProfile}
								<select
									value={ocrProfile}
									onChange={(event) => setOcrProfile(event.target.value as GuideOcrProfile)}
									disabled={settingsBusy}
									className="mt-1 h-8 w-full rounded-md border border-white/[0.08] bg-black/20 px-2 text-[11px] text-slate-100 outline-none"
								>
									<option value="vietnamese">{copy.ocrVietnamese}</option>
									<option value="hybrid">{copy.ocrHybrid}</option>
									<option value="fast">{copy.ocrFast}</option>
								</select>
							</label>
							<label className="block min-w-0 text-[10px] font-medium text-slate-400">
								{copy.ocrLanguage}
								<input
									type="text"
									value={ocrLanguage}
									onChange={(event) => setOcrLanguage(event.target.value)}
									placeholder="vi,en"
									disabled={settingsBusy}
									className="mt-1 h-8 w-full rounded-md border border-white/[0.08] bg-black/20 px-2 text-[11px] text-slate-100 outline-none placeholder:text-slate-600"
								/>
							</label>
						</div>

						<label className="block text-[10px] font-medium text-slate-400">
							{copy.apiKey}
							<input
								type="text"
								value={deepSeekApiKeyEnvName}
								onChange={(event) => setDeepSeekApiKeyEnvName(event.target.value)}
								placeholder={copy.apiKeyPlaceholder}
								disabled={settingsBusy}
								className="mt-1 h-8 w-full rounded-md border border-white/[0.08] bg-black/20 px-2 text-[11px] text-slate-100 outline-none placeholder:text-slate-600"
							/>
						</label>

						<div className="grid grid-cols-2 gap-1.5">
							<label className="block min-w-0 text-[10px] font-medium text-slate-400">
								{copy.baseUrl}
								<input
									type="text"
									value={deepSeekBaseUrl}
									onChange={(event) => setDeepSeekBaseUrl(event.target.value)}
									disabled={settingsBusy}
									className="mt-1 h-8 w-full rounded-md border border-white/[0.08] bg-black/20 px-2 text-[11px] text-slate-100 outline-none"
								/>
							</label>
							<label className="block min-w-0 text-[10px] font-medium text-slate-400">
								{copy.model}
								<input
									type="text"
									value={deepSeekModel}
									onChange={(event) => setDeepSeekModel(event.target.value)}
									disabled={settingsBusy}
									className="mt-1 h-8 w-full rounded-md border border-white/[0.08] bg-black/20 px-2 text-[11px] text-slate-100 outline-none"
								/>
							</label>
						</div>

						<div className="flex items-center gap-1.5">
							<Button
								type="button"
								size="sm"
								disabled={settingsBusy}
								onClick={handleSaveAiSettings}
								className="h-8 rounded-md border border-white/[0.08] bg-[#34B27B]/15 px-2 text-[11px] text-[#9BE7BF] hover:bg-[#34B27B]/25"
							>
								<Save className="h-3.5 w-3.5" />
								{copy.saveSettings}
							</Button>
							<Button
								type="button"
								size="sm"
								disabled={settingsBusy || !aiSettings?.deepseek.hasApiKey}
								onClick={handleClearDeepSeekKey}
								className="h-8 rounded-md border border-white/[0.08] bg-white/[0.04] px-2 text-[11px] text-slate-200 hover:bg-white/[0.08]"
							>
								<Trash2 className="h-3.5 w-3.5" />
								{copy.clearKey}
							</Button>
						</div>
					</div>
				)}

				<div className="space-y-1.5">
					{generatedSteps.slice(0, 4).map((step) => (
						<div
							key={step.id}
							className="rounded-md border border-white/[0.06] bg-white/[0.035] px-2 py-1.5"
						>
							<div className="truncate text-[11px] font-semibold text-slate-100">
								{step.order}. {step.title}
							</div>
							<p className="line-clamp-2 text-[10px] leading-4 text-slate-400">
								{step.instruction}
							</p>
						</div>
					))}
				</div>
			</div>
		</section>
	);
}
