import { useMemo, useState } from "react";
import {
	Dialog,
	DialogContent,
	DialogHeader,
	DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import {
	Select,
	SelectContent,
	SelectItem,
	SelectTrigger,
	SelectValue,
} from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { ChevronDown, ChevronUp, Pencil, Plus, Trash2 } from "lucide-react";
import {
	useDeleteWidgetInstance,
	useSaveWidgetInstance,
	useWidgetInstances,
} from "../hooks/useWidgets";
import { useServiceInstances } from "../hooks/useServices";
import { useTasks } from "../hooks/useSettings";
import type { WidgetInstance, WidgetInstanceInput } from "../types";
import {
	BUILTIN_WIDGETS,
	SERVICE_REGISTRY,
	type ServiceWidgetBinding,
} from "../integrations/registry";

interface Props {
	open: boolean;
	onClose: () => void;
}

interface Draft {
	id?: string;
	serviceId: string | null;
	widgetKind: string;
	title: string;
	config: Record<string, unknown>;
	enabled: boolean;
	sortOrder: number;
}

function Field({
	label,
	htmlFor,
	helper,
	children,
}: {
	label: string;
	htmlFor: string;
	helper?: string;
	children: React.ReactNode;
}) {
	return (
		<div className="flex flex-col gap-1.5">
			<Label htmlFor={htmlFor}>{label}</Label>
			{children}
			{helper ? (
				<p className="text-xs text-muted-foreground">{helper}</p>
			) : null}
		</div>
	);
}

function bindingLabel(serviceId: string | null, widgetKind: string): string {
	if (serviceId === null)
		return BUILTIN_WIDGETS[widgetKind]?.name ?? widgetKind;
	return widgetKind;
}

function WidgetConfigEditor({
	binding,
	isTaskOutput,
	config,
	onChange,
	tasks,
}: {
	binding: ServiceWidgetBinding | undefined;
	isTaskOutput: boolean;
	config: Record<string, unknown>;
	onChange: (config: Record<string, unknown>) => void;
	tasks: { id: string; name: string; enabled: boolean }[];
}) {
	// SSH task output gets a dedicated task picker; everything else gets a
	// generic text field per top-level schema property.
	if (isTaskOutput) {
		return (
			<Field label="Saved task" htmlFor="widget-task-id">
				<Select
					value={String(config.task_id ?? "")}
					onValueChange={(v) => onChange({ ...config, task_id: v })}
				>
					<SelectTrigger id="widget-task-id">
						<SelectValue placeholder="Select a task" />
					</SelectTrigger>
					<SelectContent>
						{tasks
							.filter((t) => t.enabled)
							.map((t) => (
								<SelectItem key={t.id} value={t.id}>
									{t.name}
								</SelectItem>
							))}
					</SelectContent>
				</Select>
			</Field>
		);
	}

	const properties = binding
		? Object.entries(
				(
					binding.configSchema as
						| { properties?: Record<string, unknown> }
						| undefined
				)?.properties ?? {},
			)
		: [];

	if (properties.length === 0) return null;

	return (
		<div className="flex flex-col gap-3">
			{properties.map(([key, schema]) => {
				const isNumber =
					(schema as { type?: string }).type === "integer" ||
					(schema as { type?: string }).type === "number";
				return (
					<Field
						key={key}
						label={key}
						htmlFor={`widget-cfg-${key}`}
						helper={(schema as { description?: string }).description}
					>
						<Input
							id={`widget-cfg-${key}`}
							type={isNumber ? "number" : "text"}
							value={String(config[key] ?? "")}
							onChange={(e) =>
								onChange({
									...config,
									[key]: isNumber
										? e.target.value === ""
											? undefined
											: Number(e.target.value)
										: e.target.value,
								})
							}
						/>
					</Field>
				);
			})}
		</div>
	);
}

export function WidgetConfigDialog({ open, onClose }: Props) {
	const { data: instances = [] } = useWidgetInstances();
	const { data: services = [] } = useServiceInstances();
	const { data: tasks = [] } = useTasks();
	const saveWidget = useSaveWidgetInstance();
	const deleteWidget = useDeleteWidgetInstance();

	const [draft, setDraft] = useState<Draft | null>(null);

	const sortedInstances = useMemo(
		() =>
			[...instances].sort(
				(a, b) => a.sort_order - b.sort_order || a.created_at - b.created_at,
			),
		[instances],
	);

	function startAddBuiltIn(kind: string) {
		const binding = BUILTIN_WIDGETS[kind];
		setDraft({
			serviceId: null,
			widgetKind: kind,
			title: binding?.name ?? kind,
			config: { ...(binding?.defaultConfig ?? {}) },
			enabled: true,
			sortOrder: 0,
		});
	}

	function startAddService(serviceId: string, kind: string) {
		const binding = SERVICE_REGISTRY[
			services.find((s) => s.id === serviceId)?.service_type ?? ""
		]?.widgets.find((w) => w.kind === kind);
		setDraft({
			serviceId,
			widgetKind: kind,
			title: binding?.name ?? kind,
			config: { ...(binding?.defaultConfig ?? {}) },
			enabled: true,
			sortOrder: 0,
		});
	}

	function startEdit(instance: WidgetInstance) {
		setDraft({
			id: instance.id,
			serviceId: instance.service_id,
			widgetKind: instance.widget_kind,
			title: instance.title,
			config: instance.config,
			enabled: instance.enabled,
			sortOrder: instance.sort_order,
		});
	}

	function reset() {
		setDraft(null);
	}

	async function saveDraft() {
		if (!draft) return;
		const input: WidgetInstanceInput = {
			id: draft.id ?? null,
			service_id: draft.serviceId,
			widget_kind: draft.widgetKind,
			title: draft.title,
			config: draft.config,
			enabled: draft.enabled,
			sort_order: draft.sortOrder,
		};
		await saveWidget.mutateAsync(input);
		reset();
	}

	async function toggleEnabled(instance: WidgetInstance) {
		await saveWidget.mutateAsync({
			id: instance.id,
			service_id: instance.service_id,
			widget_kind: instance.widget_kind,
			title: instance.title,
			config: instance.config,
			enabled: !instance.enabled,
			sort_order: instance.sort_order,
		});
	}

	async function moveInstance(index: number, direction: -1 | 1) {
		const targetIndex = index + direction;
		if (targetIndex < 0 || targetIndex >= sortedInstances.length) return;
		const a = sortedInstances[index];
		const b = sortedInstances[targetIndex];
		await Promise.all([
			saveWidget.mutateAsync({ ...a, sort_order: b.sort_order }),
			saveWidget.mutateAsync({ ...b, sort_order: a.sort_order }),
		]);
	}

	async function removeInstance(instance: WidgetInstance) {
		await deleteWidget.mutateAsync(instance.id);
	}

	function handleClose(next: boolean) {
		if (!next) {
			reset();
			onClose();
		}
	}

	const draftBinding = draft
		? draft.serviceId
			? SERVICE_REGISTRY[
					services.find((s) => s.id === draft.serviceId)?.service_type ?? ""
				]?.widgets.find((w) => w.kind === draft.widgetKind)
			: BUILTIN_WIDGETS[draft.widgetKind]
		: undefined;
	const isTaskOutput =
		draft?.serviceId !== null &&
		services.find((s) => s.id === draft?.serviceId)?.service_type ===
			"ssh_tasks";

	return (
		<Dialog open={open} onOpenChange={handleClose}>
			<DialogContent className="sm:max-w-2xl">
				<DialogHeader>
					<DialogTitle>
						{draft
							? draft.id
								? "Edit widget"
								: "Add widget"
							: "Dashboard widgets"}
					</DialogTitle>
				</DialogHeader>

				{draft ? (
					<div className="flex flex-col gap-4">
						<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
							<Field label="Title" htmlFor="widget-title">
								<Input
									id="widget-title"
									value={draft.title}
									onChange={(e) =>
										setDraft({ ...draft, title: e.target.value })
									}
								/>
							</Field>
							<Field label="Sort order" htmlFor="widget-sort-order">
								<Input
									id="widget-sort-order"
									type="number"
									value={String(draft.sortOrder)}
									onChange={(e) =>
										setDraft({
											...draft,
											sortOrder:
												e.target.value === "" ? 0 : Number(e.target.value),
										})
									}
								/>
							</Field>
						</div>
						<div className="flex items-center gap-2">
							<Switch
								id="widget-enabled"
								checked={draft.enabled}
								onCheckedChange={(checked) =>
									setDraft({ ...draft, enabled: checked })
								}
							/>
							<Label htmlFor="widget-enabled">Enabled</Label>
						</div>
						<WidgetConfigEditor
							binding={draftBinding}
							isTaskOutput={!!isTaskOutput}
							config={draft.config}
							onChange={(config) => setDraft({ ...draft, config })}
							tasks={tasks}
						/>
						<div className="flex justify-end gap-2">
							<Button variant="outline" onClick={reset}>
								Back
							</Button>
							<Button onClick={saveDraft} disabled={saveWidget.isPending}>
								Save widget
							</Button>
						</div>
					</div>
				) : (
					<div className="flex flex-col gap-4">
						{sortedInstances.length === 0 ? (
							<Alert>
								<AlertDescription>
									No widgets yet. Add one below.
								</AlertDescription>
							</Alert>
						) : (
							<div className="flex flex-col gap-2">
								{sortedInstances.map((instance, index) => {
									const serviceName = instance.service_id
										? services.find((s) => s.id === instance.service_id)?.name
										: "Built-in";
									return (
										<div
											key={instance.id}
											className="flex items-center gap-2 rounded border p-2"
										>
											<div className="flex flex-1 flex-col gap-1">
												<div className="flex items-center gap-2">
													<span className="font-medium">{instance.title}</span>
													<Badge variant="outline">
														{bindingLabel(
															instance.service_id,
															instance.widget_kind,
														)}
													</Badge>
													{serviceName ? (
														<span className="text-xs text-muted-foreground">
															{serviceName}
														</span>
													) : null}
													{!instance.enabled ? (
														<Badge variant="secondary">disabled</Badge>
													) : null}
												</div>
											</div>
											<div className="flex items-center gap-1">
												<Button
													variant="ghost"
													size="icon"
													className="h-8 w-8"
													disabled={index === 0}
													onClick={() => moveInstance(index, -1)}
												>
													<ChevronUp className="h-4 w-4" />
												</Button>
												<Button
													variant="ghost"
													size="icon"
													className="h-8 w-8"
													disabled={index === sortedInstances.length - 1}
													onClick={() => moveInstance(index, 1)}
												>
													<ChevronDown className="h-4 w-4" />
												</Button>
												<Switch
													checked={instance.enabled}
													onCheckedChange={() => toggleEnabled(instance)}
													aria-label={`Toggle ${instance.title}`}
												/>
												<Button
													variant="ghost"
													size="icon"
													className="h-8 w-8"
													onClick={() => startEdit(instance)}
												>
													<Pencil className="h-4 w-4" />
												</Button>
												<Button
													variant="ghost"
													size="icon"
													className="h-8 w-8 text-destructive"
													onClick={() => removeInstance(instance)}
												>
													<Trash2 className="h-4 w-4" />
												</Button>
											</div>
										</div>
									);
								})}
							</div>
						)}

						<div className="flex flex-col gap-2">
							<p className="text-sm font-medium">Add widget</p>
							<div className="flex flex-wrap gap-2">
								{Object.values(BUILTIN_WIDGETS).map((b) => (
									<Button
										key={b.kind}
										variant="outline"
										size="sm"
										onClick={() => startAddBuiltIn(b.kind)}
									>
										<Plus className="mr-1 h-3 w-3" />
										{b.name}
									</Button>
								))}
								{services
									.filter((s) => s.enabled)
									.flatMap((s) =>
										(SERVICE_REGISTRY[s.service_type]?.widgets ?? []).map(
											(w) => (
												<Button
													key={`${s.id}:${w.kind}`}
													variant="outline"
													size="sm"
													onClick={() => startAddService(s.id, w.kind)}
												>
													<Plus className="mr-1 h-3 w-3" />
													{w.name} · {s.name}
												</Button>
											),
										),
									)}
							</div>
							<p className="text-xs text-muted-foreground">
								Configure services on their service pages to unlock more
								widgets.
							</p>
						</div>
					</div>
				)}
			</DialogContent>
		</Dialog>
	);
}
