import { useState, useEffect, useCallback, useRef } from "react";
import { Icon } from "./icon";
import { extractErrorMessage } from "../utils/errors";
import {
	compileToolDefinition,
	type ToolDefinitionManifest,
} from "../api/tool_definitions";

interface PackageEntry {
	name: string;
}

interface MountEntry {
	name: string;
	target: string;
	source_type: string;
	writable: boolean;
	owner: string;
	mode: string;
	file_mode: string;
	readonly: boolean;
	git_mount_ref: string;
}

interface ManifestEditorProps {
	manifest: Record<string, unknown> | null;
	baseDefinitions: ToolDefinitionManifest[];
	onChange: (manifest: Record<string, unknown>) => void;
	definitionId?: string | null;
}

export const ManifestEditor = ({
	manifest,
	baseDefinitions,
	onChange,
	definitionId,
}: ManifestEditorProps) => {
	const [baseImage, setBaseImage] = useState("");
	const [baseDefinitionId, setBaseDefinitionId] = useState("");
	const [aptPackages, setAptPackages] = useState<PackageEntry[]>([]);
	const [npmPackages, setNpmPackages] = useState<PackageEntry[]>([]);
	const [pipPackages, setPipPackages] = useState<PackageEntry[]>([]);
	const [nodeVersion, setNodeVersion] = useState("");
	const [userName, setUserName] = useState("user");
	const [userUid, setUserUid] = useState("1000");
	const [userGid, setUserGid] = useState("1000");
	const [envVars, setEnvVars] = useState<{ key: string; value: string }[]>([]);
	const [buildScripts, setBuildScripts] = useState<string[]>([""]);
	const [startupScripts, setStartupScripts] = useState<string[]>([""]);
	const [mounts, setMounts] = useState<MountEntry[]>([]);
	const [command, setCommand] = useState<string[]>([""]);
	const [workingDir, setWorkingDir] = useState("/workspace");
	const [stdinOpen, setStdinOpen] = useState(true);
	const [tty, setTty] = useState(true);

	const [preview, setPreview] = useState<{
		dockerfile: string;
		compose: string;
		entrypoint: string;
	} | null>(null);
	const [previewLoading, setPreviewLoading] = useState(false);
	const [previewError, setPreviewError] = useState<string | null>(null);

	// Load manifest into form
	useEffect(() => {
		if (!manifest) return;

		const pkgs = (manifest.packages as Record<string, unknown>) || {};
		setBaseImage((manifest.base_image as string) || "");
		setBaseDefinitionId((manifest.base_definition_id as string) || "");
		setAptPackages(((pkgs.apt as string[]) || []).map((p) => ({ name: p })));
		setNpmPackages(
			((pkgs.npm_global as string[]) || []).map((p) => ({ name: p })),
		);
		setPipPackages(((pkgs.pip as string[]) || []).map((p) => ({ name: p })));
		setNodeVersion((pkgs.node as Record<string, string>)?.version || "");

		const user = (manifest.user as Record<string, unknown>) || {};
		setUserName((user.name as string) || "user");
		setUserUid(String(user.uid || "1000"));
		setUserGid(String(user.gid || "1000"));

		const env = (manifest.env as Record<string, string>) || {};
		setEnvVars(Object.entries(env).map(([key, value]) => ({ key, value })));

		const scripts = (manifest.scripts as Record<string, string[]>) || {};
		setBuildScripts((scripts.build || []).length > 0 ? scripts.build : [""]);
		setStartupScripts(
			(scripts.startup || []).length > 0 ? scripts.startup : [""],
		);

		const mts = (manifest.mounts as MountEntry[]) || [];
		setMounts(mts);

		const runtime = (manifest.runtime as Record<string, unknown>) || {};
		setCommand((runtime.command as string[]) || [""]);
		setWorkingDir((runtime.working_dir as string) || "/workspace");
		setStdinOpen((runtime.stdin_open as boolean) ?? true);
		setTty((runtime.tty as boolean) ?? true);
	}, [manifest]);

	// Build manifest from form state
	const buildManifest = useCallback((): Record<string, unknown> => {
		const packages: Record<string, unknown> = {};
		const apt = aptPackages.map((p) => p.name).filter(Boolean);
		if (apt.length) packages.apt = apt;
		const npm = npmPackages.map((p) => p.name).filter(Boolean);
		if (npm.length) packages.npm_global = npm;
		const pip = pipPackages.map((p) => p.name).filter(Boolean);
		if (pip.length) packages.pip = pip;
		if (nodeVersion) packages.node = { version: nodeVersion };

		const env: Record<string, string> = {};
		envVars.forEach(({ key, value }) => {
			if (key) env[key] = value;
		});

		const scripts: Record<string, string[]> = {};
		const build = buildScripts.filter(Boolean);
		if (build.length) scripts.build = build;
		const startup = startupScripts.filter(Boolean);
		if (startup.length) scripts.startup = startup;

		const mts = mounts.filter((m) => m.name && m.target);

		const result: Record<string, unknown> = {
			packages,
			user: {
				name: userName,
				uid: parseInt(userUid) || 1000,
				gid: parseInt(userGid) || 1000,
				create_home: true,
				shell: "/bin/bash",
			},
			env,
			scripts,
			mounts: mts,
			runtime: {
				command:
					command.filter(Boolean).length > 0
						? command.filter(Boolean)
						: ["/bin/bash"],
				stdin_open: stdinOpen,
				tty: tty,
				working_dir: workingDir,
			},
		};

		if (baseImage) result.base_image = baseImage;
		if (baseDefinitionId) result.base_definition_id = baseDefinitionId;

		return result;
	}, [
		aptPackages,
		npmPackages,
		pipPackages,
		nodeVersion,
		userName,
		userUid,
		userGid,
		envVars,
		buildScripts,
		startupScripts,
		mounts,
		command,
		workingDir,
		stdinOpen,
		tty,
		baseImage,
		baseDefinitionId,
	]);

	// Notify parent of changes — only when built manifest actually differs
	// from what we last sent, to avoid feedback loops with the manifest prop.
	const lastSentRef = useRef<string>("");
	const onChangeRef = useRef(onChange);
	onChangeRef.current = onChange;

	useEffect(() => {
		const m = buildManifest();
		const serialized = JSON.stringify(m);
		if (serialized !== lastSentRef.current) {
			lastSentRef.current = serialized;
			onChangeRef.current(m);
		}
	}, [buildManifest]);

	const handlePreview = async () => {
		if (!definitionId) {
			setPreviewError("Save the tool definition first to preview");
			return;
		}
		setPreviewLoading(true);
		setPreviewError(null);
		try {
			const result = await compileToolDefinition(definitionId);
			setPreview({
				dockerfile: result.dockerfile,
				compose: result.compose,
				entrypoint: result.entrypoint,
			});
		} catch (err) {
			setPreviewError(extractErrorMessage(err));
		} finally {
			setPreviewLoading(false);
		}
	};

	const addAptPackage = () => setAptPackages([...aptPackages, { name: "" }]);
	const removeAptPackage = (idx: number) =>
		setAptPackages(aptPackages.filter((_, i) => i !== idx));
	const updateAptPackage = (idx: number, name: string) => {
		const copy = [...aptPackages];
		copy[idx] = { name };
		setAptPackages(copy);
	};

	const addNpmPackage = () => setNpmPackages([...npmPackages, { name: "" }]);
	const removeNpmPackage = (idx: number) =>
		setNpmPackages(npmPackages.filter((_, i) => i !== idx));
	const updateNpmPackage = (idx: number, name: string) => {
		const copy = [...npmPackages];
		copy[idx] = { name };
		setNpmPackages(copy);
	};

	const addPipPackage = () => setPipPackages([...pipPackages, { name: "" }]);
	const removePipPackage = (idx: number) =>
		setPipPackages(pipPackages.filter((_, i) => i !== idx));
	const updatePipPackage = (idx: number, name: string) => {
		const copy = [...pipPackages];
		copy[idx] = { name };
		setPipPackages(copy);
	};

	const addEnvVar = () => setEnvVars([...envVars, { key: "", value: "" }]);
	const removeEnvVar = (idx: number) =>
		setEnvVars(envVars.filter((_, i) => i !== idx));
	const updateEnvVar = (idx: number, field: "key" | "value", val: string) => {
		const copy = [...envVars];
		copy[idx] = { ...copy[idx], [field]: val };
		setEnvVars(copy);
	};

	const addBuildScript = () => setBuildScripts([...buildScripts, ""]);
	const removeBuildScript = (idx: number) =>
		setBuildScripts(buildScripts.filter((_, i) => i !== idx));
	const updateBuildScript = (idx: number, val: string) => {
		const copy = [...buildScripts];
		copy[idx] = val;
		setBuildScripts(copy);
	};

	const addStartupScript = () => setStartupScripts([...startupScripts, ""]);
	const removeStartupScript = (idx: number) =>
		setStartupScripts(startupScripts.filter((_, i) => i !== idx));
	const updateStartupScript = (idx: number, val: string) => {
		const copy = [...startupScripts];
		copy[idx] = val;
		setStartupScripts(copy);
	};

	const addMount = () =>
		setMounts([
			...mounts,
			{
				name: "",
				target: "",
				source_type: "repo",
				writable: true,
				owner: "",
				mode: "",
				file_mode: "",
				readonly: false,
				git_mount_ref: "",
			},
		]);
	const removeMount = (idx: number) =>
		setMounts(mounts.filter((_, i) => i !== idx));
	const updateMount = (idx: number, field: keyof MountEntry, val: unknown) => {
		const copy = [...mounts];
		copy[idx] = { ...copy[idx], [field]: val };
		setMounts(copy);
	};

	return (
		<div className="stack" style={{ gap: "1.5rem" }}>
			{/* Base Image */}
			<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
				<h4 style={{ margin: 0 }}>Base Image</h4>
				<div className="row" style={{ gap: "1rem" }}>
					<div className="form-group" style={{ flex: 1 }}>
						<label>Base Definition</label>
						<select
							value={baseDefinitionId}
							onChange={(e) => {
								setBaseDefinitionId(e.target.value);
								setBaseImage("");
							}}
							className="form-input"
						>
							<option value="">Custom image...</option>
							{baseDefinitions.map((b) => (
								<option key={b.id} value={b.id}>
									{b.display_name} ({b.version})
								</option>
							))}
						</select>
					</div>
					<div className="form-group" style={{ flex: 1 }}>
						<label>Custom Base Image</label>
						<input
							type="text"
							value={baseImage}
							onChange={(e) => {
								setBaseImage(e.target.value);
								setBaseDefinitionId("");
							}}
							placeholder="e.g., ubuntu:24.04"
							className="form-input"
							disabled={!!baseDefinitionId}
						/>
					</div>
				</div>
			</div>

			{/* Packages */}
			<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
				<h4 style={{ margin: 0 }}>Packages</h4>

				<div className="form-group">
					<label>Node.js Version</label>
					<input
						type="text"
						value={nodeVersion}
						onChange={(e) => setNodeVersion(e.target.value)}
						placeholder="e.g., 20"
						className="form-input"
						style={{ width: "120px" }}
					/>
				</div>

				<div>
					<label>APT Packages</label>
					<div className="stack" style={{ gap: "0.5rem", marginTop: "0.5rem" }}>
						{aptPackages.map((pkg, idx) => (
							<div key={idx} className="row" style={{ gap: "0.5rem" }}>
								<input
									type="text"
									value={pkg.name}
									onChange={(e) => updateAptPackage(idx, e.target.value)}
									placeholder="e.g., neovim"
									className="form-input"
								/>
								<button
									type="button"
									onClick={() => removeAptPackage(idx)}
									className="button-icon"
									title="Remove"
								>
									<Icon name="delete" size="sm" />
								</button>
							</div>
						))}
						<button
							type="button"
							onClick={addAptPackage}
							className="button-secondary"
							style={{ width: "fit-content" }}
						>
							<Icon name="add" size="sm" /> Add APT Package
						</button>
					</div>
				</div>

				<div>
					<label>NPM Global Packages</label>
					<div className="stack" style={{ gap: "0.5rem", marginTop: "0.5rem" }}>
						{npmPackages.map((pkg, idx) => (
							<div key={idx} className="row" style={{ gap: "0.5rem" }}>
								<input
									type="text"
									value={pkg.name}
									onChange={(e) => updateNpmPackage(idx, e.target.value)}
									placeholder="e.g., @scope/pkg"
									className="form-input"
								/>
								<button
									type="button"
									onClick={() => removeNpmPackage(idx)}
									className="button-icon"
									title="Remove"
								>
									<Icon name="delete" size="sm" />
								</button>
							</div>
						))}
						<button
							type="button"
							onClick={addNpmPackage}
							className="button-secondary"
							style={{ width: "fit-content" }}
						>
							<Icon name="add" size="sm" /> Add NPM Package
						</button>
					</div>
				</div>

				<div>
					<label>Pip Packages</label>
					<div className="stack" style={{ gap: "0.5rem", marginTop: "0.5rem" }}>
						{pipPackages.map((pkg, idx) => (
							<div key={idx} className="row" style={{ gap: "0.5rem" }}>
								<input
									type="text"
									value={pkg.name}
									onChange={(e) => updatePipPackage(idx, e.target.value)}
									placeholder="e.g., requests"
									className="form-input"
								/>
								<button
									type="button"
									onClick={() => removePipPackage(idx)}
									className="button-icon"
									title="Remove"
								>
									<Icon name="delete" size="sm" />
								</button>
							</div>
						))}
						<button
							type="button"
							onClick={addPipPackage}
							className="button-secondary"
							style={{ width: "fit-content" }}
						>
							<Icon name="add" size="sm" /> Add Pip Package
						</button>
					</div>
				</div>
			</div>

			{/* Runtime User */}
			<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
				<h4 style={{ margin: 0 }}>Runtime User</h4>
				<div className="row" style={{ gap: "1rem" }}>
					<div className="form-group" style={{ flex: 1 }}>
						<label>User Name</label>
						<input
							type="text"
							value={userName}
							onChange={(e) => setUserName(e.target.value)}
							className="form-input"
						/>
					</div>
					<div className="form-group" style={{ flex: 1 }}>
						<label>UID</label>
						<input
							type="number"
							value={userUid}
							onChange={(e) => setUserUid(e.target.value)}
							className="form-input"
						/>
					</div>
					<div className="form-group" style={{ flex: 1 }}>
						<label>GID</label>
						<input
							type="number"
							value={userGid}
							onChange={(e) => setUserGid(e.target.value)}
							className="form-input"
						/>
					</div>
				</div>
			</div>

			{/* Environment */}
			<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
				<h4 style={{ margin: 0 }}>Environment Variables</h4>
				<div className="stack" style={{ gap: "0.5rem" }}>
					{envVars.map((ev, idx) => (
						<div key={idx} className="row" style={{ gap: "0.5rem" }}>
							<input
								type="text"
								value={ev.key}
								onChange={(e) => updateEnvVar(idx, "key", e.target.value)}
								placeholder="KEY"
								className="form-input"
							/>
							<input
								type="text"
								value={ev.value}
								onChange={(e) => updateEnvVar(idx, "value", e.target.value)}
								placeholder="value"
								className="form-input"
							/>
							<button
								type="button"
								onClick={() => removeEnvVar(idx)}
								className="button-icon"
								title="Remove"
							>
								<Icon name="delete" size="sm" />
							</button>
						</div>
					))}
					<button
						type="button"
						onClick={addEnvVar}
						className="button-secondary"
						style={{ width: "fit-content" }}
					>
						<Icon name="add" size="sm" /> Add Env Var
					</button>
				</div>
			</div>

			{/* Build Scripts */}
			<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
				<h4 style={{ margin: 0 }}>Build Scripts (run during docker build)</h4>
				<div className="stack" style={{ gap: "0.5rem" }}>
					{buildScripts.map((script, idx) => (
						<div
							key={idx}
							className="row"
							style={{ gap: "0.5rem", alignItems: "flex-start" }}
						>
							<textarea
								value={script}
								onChange={(e) => updateBuildScript(idx, e.target.value)}
								placeholder="git config --global user.email 'dev@example.com'"
								className="form-input"
								rows={2}
								style={{
									fontFamily: "monospace",
									fontSize: "0.8125rem",
									flex: 1,
								}}
							/>
							<button
								type="button"
								onClick={() => removeBuildScript(idx)}
								className="button-icon"
								title="Remove"
							>
								<Icon name="delete" size="sm" />
							</button>
						</div>
					))}
					<button
						type="button"
						onClick={addBuildScript}
						className="button-secondary"
						style={{ width: "fit-content" }}
					>
						<Icon name="add" size="sm" /> Add Build Script
					</button>
				</div>
			</div>

			{/* Startup Scripts */}
			<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
				<h4 style={{ margin: 0 }}>
					Startup Scripts (run when container starts)
				</h4>
				<div className="stack" style={{ gap: "0.5rem" }}>
					{startupScripts.map((script, idx) => (
						<div
							key={idx}
							className="row"
							style={{ gap: "0.5rem", alignItems: "flex-start" }}
						>
							<textarea
								value={script}
								onChange={(e) => updateStartupScript(idx, e.target.value)}
								placeholder="chown -R user:user /workspace"
								className="form-input"
								rows={2}
								style={{
									fontFamily: "monospace",
									fontSize: "0.8125rem",
									flex: 1,
								}}
							/>
							<button
								type="button"
								onClick={() => removeStartupScript(idx)}
								className="button-icon"
								title="Remove"
							>
								<Icon name="delete" size="sm" />
							</button>
						</div>
					))}
					<button
						type="button"
						onClick={addStartupScript}
						className="button-secondary"
						style={{ width: "fit-content" }}
					>
						<Icon name="add" size="sm" /> Add Startup Script
					</button>
				</div>
			</div>

			{/* Mounts */}
			<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
				<h4 style={{ margin: 0 }}>Mount Schema</h4>
				<div className="stack" style={{ gap: "1rem" }}>
					{mounts.map((mount, idx) => (
						<div
							key={idx}
							className="stack"
							style={{
								gap: "0.5rem",
								padding: "0.75rem",
								border: "1px solid var(--border)",
								borderRadius: "0.375rem",
							}}
						>
							<div className="row" style={{ gap: "0.5rem" }}>
								<input
									type="text"
									value={mount.name}
									onChange={(e) => updateMount(idx, "name", e.target.value)}
									placeholder="Name (e.g., workspace)"
									className="form-input"
								/>
								<input
									type="text"
									value={mount.target}
									onChange={(e) => updateMount(idx, "target", e.target.value)}
									placeholder="Target (e.g., /workspace)"
									className="form-input"
								/>
								<select
									value={mount.source_type}
									onChange={(e) =>
										updateMount(idx, "source_type", e.target.value)
									}
									className="form-input"
								>
									<option value="repo">Repository</option>
									<option value="ssh_key">SSH Key</option>
									<option value="instance">Instance</option>
									<option value="git_mount">Git Mount</option>
									<option value="host_path">Host Path</option>
								</select>
								<button
									type="button"
									onClick={() => removeMount(idx)}
									className="button-icon"
									title="Remove"
								>
									<Icon name="delete" size="sm" />
								</button>
							</div>
							<div className="row" style={{ gap: "0.5rem" }}>
								<label
									style={{
										display: "flex",
										alignItems: "center",
										gap: "0.25rem",
										fontSize: "0.875rem",
									}}
								>
									<input
										type="checkbox"
										checked={mount.writable}
										onChange={(e) =>
											updateMount(idx, "writable", e.target.checked)
										}
									/>
									Writable
								</label>
								<label
									style={{
										display: "flex",
										alignItems: "center",
										gap: "0.25rem",
										fontSize: "0.875rem",
									}}
								>
									<input
										type="checkbox"
										checked={mount.readonly}
										onChange={(e) =>
											updateMount(idx, "readonly", e.target.checked)
										}
									/>
									Read-only
								</label>
								<input
									type="text"
									value={mount.owner}
									onChange={(e) => updateMount(idx, "owner", e.target.value)}
									placeholder="Owner (e.g., user)"
									className="form-input"
									style={{ width: "120px" }}
								/>
								<input
									type="text"
									value={mount.mode}
									onChange={(e) => updateMount(idx, "mode", e.target.value)}
									placeholder="Mode (e.g., 0755)"
									className="form-input"
									style={{ width: "100px" }}
								/>
								<input
									type="text"
									value={mount.file_mode}
									onChange={(e) =>
										updateMount(idx, "file_mode", e.target.value)
									}
									placeholder="File mode (e.g., 0644)"
									className="form-input"
									style={{ width: "120px" }}
								/>
								{mount.source_type === "git_mount" && (
									<input
										type="text"
										value={mount.git_mount_ref}
										onChange={(e) =>
											updateMount(idx, "git_mount_ref", e.target.value)
										}
										placeholder="Git mount ref"
										className="form-input"
										style={{ width: "120px" }}
									/>
								)}
							</div>
						</div>
					))}
					<button
						type="button"
						onClick={addMount}
						className="button-secondary"
						style={{ width: "fit-content" }}
					>
						<Icon name="add" size="sm" /> Add Mount
					</button>
				</div>
			</div>

			{/* Runtime */}
			<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
				<h4 style={{ margin: 0 }}>Runtime</h4>
				<div className="row" style={{ gap: "1rem" }}>
					<div className="form-group" style={{ flex: 1 }}>
						<label>Command</label>
						<input
							type="text"
							value={command.join(" ")}
							onChange={(e) => setCommand(e.target.value.split(" "))}
							placeholder="/bin/bash"
							className="form-input"
						/>
					</div>
					<div className="form-group" style={{ flex: 1 }}>
						<label>Working Directory</label>
						<input
							type="text"
							value={workingDir}
							onChange={(e) => setWorkingDir(e.target.value)}
							placeholder="/workspace"
							className="form-input"
						/>
					</div>
				</div>
				<div className="row" style={{ gap: "1rem" }}>
					<label
						style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}
					>
						<input
							type="checkbox"
							checked={stdinOpen}
							onChange={(e) => setStdinOpen(e.target.checked)}
						/>
						stdin_open
					</label>
					<label
						style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}
					>
						<input
							type="checkbox"
							checked={tty}
							onChange={(e) => setTty(e.target.checked)}
						/>
						tty
					</label>
				</div>
			</div>

			{/* Preview */}
			<div className="card stack" style={{ gap: "1rem", padding: "1rem" }}>
				<div
					style={{
						display: "flex",
						justifyContent: "space-between",
						alignItems: "center",
					}}
				>
					<h4 style={{ margin: 0 }}>Live Preview</h4>
					<button
						type="button"
						onClick={handlePreview}
						disabled={previewLoading}
						className="button-secondary"
					>
						<Icon name="refresh" size="sm" />
						{previewLoading ? "Compiling..." : "Preview"}
					</button>
				</div>
				{previewError && <p className="text-error">{previewError}</p>}
				{preview && (
					<div className="stack" style={{ gap: "1rem" }}>
						<div>
							<label style={{ fontWeight: 600, fontSize: "0.875rem" }}>
								Dockerfile
							</label>
							<pre
								style={{
									background: "var(--code-bg, #1e1e1e)",
									color: "var(--code-fg, #d4d4d4)",
									padding: "1rem",
									borderRadius: "0.375rem",
									overflow: "auto",
									fontSize: "0.8125rem",
									maxHeight: "300px",
								}}
							>
								{preview.dockerfile}
							</pre>
						</div>
						<div>
							<label style={{ fontWeight: 600, fontSize: "0.875rem" }}>
								Compose
							</label>
							<pre
								style={{
									background: "var(--code-bg, #1e1e1e)",
									color: "var(--code-fg, #d4d4d4)",
									padding: "1rem",
									borderRadius: "0.375rem",
									overflow: "auto",
									fontSize: "0.8125rem",
									maxHeight: "200px",
								}}
							>
								{preview.compose}
							</pre>
						</div>
					</div>
				)}
			</div>
		</div>
	);
};
