import { useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
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 {
	Dialog,
	DialogContent,
	DialogHeader,
	DialogTitle,
} from "@/components/ui/dialog";
import { ExternalLink, Plus, Trash2 } from "lucide-react";
import {
	useDeleteServiceInstance,
	useSaveServiceInstance,
	useServiceInstances,
} from "../hooks/useServices";
import { useServiceTypes } from "../hooks/useServices";
import type {
	SecretFieldInfo,
	ServiceInstance,
	ServiceInstanceInput,
	ServiceTypeInfo,
} from "../types";
import { SectionCard } from "../components/SectionCard";
import { ConfirmDialog } from "../components/ConfirmDialog";
import { DialogFooter } from "../components/DialogFooter";
import { getServiceBinding } from "../integrations/registry";

interface CreateDraft {
	serviceType: string;
	name: string;
	config: Record<string, unknown>;
	secrets: Record<string, string>;
	enabled: boolean;
}

function emptyDraft(serviceType: string): CreateDraft {
	return { serviceType, name: "", config: {}, secrets: {}, enabled: true };
}

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 ServiceConfigFields({
	type,
	config,
	onChange,
}: {
	type: ServiceTypeInfo;
	config: Record<string, unknown>;
	onChange: (config: Record<string, unknown>) => void;
}) {
	const properties =
		(
			type.config_schema as {
				properties?: Record<string, { type?: string; description?: string }>;
			}
		).properties ?? {};
	return (
		<div className="flex flex-col gap-3">
			{Object.entries(properties).map(([key, schema]) => {
				const isNumber = schema.type === "integer" || schema.type === "number";
				return (
					<Field
						key={key}
						label={key}
						htmlFor={`cfg-${key}`}
						helper={schema.description}
					>
						<Input
							id={`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>
	);
}

function ServiceSecretFields({
	fields,
	secrets,
	onChange,
}: {
	fields: SecretFieldInfo[];
	secrets: Record<string, string>;
	onChange: (secrets: Record<string, string>) => void;
}) {
	if (fields.length === 0) return null;
	return (
		<div className="flex flex-col gap-3">
			{fields.map((field) => (
				<Field
					key={field.key}
					label={field.label}
					htmlFor={`secret-${field.key}`}
					helper={field.helper ?? (field.required ? "Required" : undefined)}
				>
					<Input
						id={`secret-${field.key}`}
						type="password"
						value={secrets[field.key] ?? ""}
						onChange={(e) =>
							onChange({ ...secrets, [field.key]: e.target.value })
						}
					/>
				</Field>
			))}
		</div>
	);
}

function CreateServiceDialog({
	open,
	onClose,
}: {
	open: boolean;
	onClose: () => void;
}) {
	const { data: types = [] } = useServiceTypes();
	const saveService = useSaveServiceInstance();
	const [draft, setDraft] = useState<CreateDraft | null>(null);

	function reset() {
		setDraft(null);
	}

	async function save() {
		if (!draft) return;
		if (!draft.name.trim()) return;
		const input: ServiceInstanceInput = {
			service_type: draft.serviceType,
			name: draft.name.trim(),
			config: draft.config,
			secrets: draft.secrets,
			enabled: draft.enabled,
		};
		await saveService.mutateAsync(input);
		reset();
		onClose();
	}

	const selectedType = types.find((t) => t.service_type === draft?.serviceType);

	return (
		<Dialog
			open={open}
			onOpenChange={(next) => {
				if (!next) {
					reset();
					onClose();
				}
			}}
		>
			<DialogContent className="sm:max-w-lg">
				<DialogHeader>
					<DialogTitle>New service</DialogTitle>
				</DialogHeader>
				<div className="flex flex-col gap-4">
					{!draft ? (
						<div className="flex flex-col gap-2">
							{types.map((t) => (
								<Button
									key={t.service_type}
									variant="outline"
									onClick={() => setDraft(emptyDraft(t.service_type))}
								>
									<Plus className="mr-1 h-3 w-3" />
									{t.name}
								</Button>
							))}
						</div>
					) : (
						<>
							<p className="text-sm text-muted-foreground">
								{selectedType?.description}
							</p>
							<Field label="Name" htmlFor="service-name">
								<Input
									id="service-name"
									value={draft.name}
									onChange={(e) => setDraft({ ...draft, name: e.target.value })}
								/>
							</Field>
							{selectedType ? (
								<ServiceConfigFields
									type={selectedType}
									config={draft.config}
									onChange={(config) => setDraft({ ...draft, config })}
								/>
							) : null}
							{selectedType ? (
								<ServiceSecretFields
									fields={selectedType.secret_fields}
									secrets={draft.secrets}
									onChange={(secrets) => setDraft({ ...draft, secrets })}
								/>
							) : null}
							<div className="flex items-center gap-2">
								<Switch
									id="service-enabled"
									checked={draft.enabled}
									onCheckedChange={(checked) =>
										setDraft({ ...draft, enabled: checked })
									}
								/>
								<Label htmlFor="service-enabled">Enabled</Label>
							</div>
						</>
					)}
				</div>
				{draft ? (
					<DialogFooter
						onCancel={reset}
						onConfirm={save}
						confirmLabel="Create service"
						confirmDisabled={!draft.name.trim() || saveService.isPending}
					/>
				) : null}
			</DialogContent>
		</Dialog>
	);
}

export function ServicesPage() {
	const navigate = useNavigate();
	const { data: services = [] } = useServiceInstances();
	const { data: types = [] } = useServiceTypes();
	const deleteService = useDeleteServiceInstance();
	const [createOpen, setCreateOpen] = useState(false);
	const [deleteId, setDeleteId] = useState<string | null>(null);

	const grouped = useMemo(() => {
		const map = new Map<string, ServiceInstance[]>();
		for (const s of services) {
			const list = map.get(s.service_type) ?? [];
			list.push(s);
			map.set(s.service_type, list);
		}
		return [...map.entries()].sort((a, b) => a[0].localeCompare(b[0]));
	}, [services]);

	const typeName = (t: string) =>
		types.find((x) => x.service_type === t)?.name ??
		getServiceBinding(t)?.name ??
		t;

	return (
		<div className="flex flex-col gap-4">
			<SectionCard
				title="Services"
				description="External services the app talks to. Configure URLs and API keys here; they are encrypted at rest."
				action={
					<Button variant="outline" onClick={() => setCreateOpen(true)}>
						<Plus className="mr-1 h-3 w-3" />
						Add service
					</Button>
				}
			>
				{services.length === 0 ? (
					<Alert>
						<AlertDescription>
							No services yet. Add a Grafana, Prometheus, Jellyfin, Nextcloud,
							or SSH task runner.
						</AlertDescription>
					</Alert>
				) : (
					<div className="flex flex-col gap-4">
						{grouped.map(([serviceType, instances]) => (
							<div key={serviceType} className="flex flex-col gap-2">
								<div className="text-sm font-medium">
									{typeName(serviceType)}
								</div>
								<div className="flex flex-col gap-2">
									{instances.map((s) => (
										<div
											key={s.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">{s.name}</span>
													<Badge variant="outline">{s.service_type}</Badge>
													{!s.enabled ? (
														<Badge variant="secondary">disabled</Badge>
													) : null}
													{Object.entries(s.secrets_set).some(([, v]) => v) ? (
														<Badge variant="outline">secrets set</Badge>
													) : null}
												</div>
											</div>
											<div className="flex items-center gap-1">
												<Button
													variant="ghost"
													size="sm"
													onClick={() =>
														navigate(`/services/${s.service_type}/${s.id}`)
													}
												>
													Open <ExternalLink className="ml-1 h-3 w-3" />
												</Button>
												<Button
													variant="ghost"
													size="icon"
													className="h-8 w-8 text-destructive"
													onClick={() => setDeleteId(s.id)}
												>
													<Trash2 className="h-4 w-4" />
												</Button>
											</div>
										</div>
									))}
								</div>
							</div>
						))}
					</div>
				)}
			</SectionCard>

			<CreateServiceDialog
				open={createOpen}
				onClose={() => setCreateOpen(false)}
			/>
			<ConfirmDialog
				open={Boolean(deleteId)}
				title="Delete service?"
				message="This removes the service and any widgets that reference it. This cannot be undone."
				confirmLabel="Delete"
				onCancel={() => setDeleteId(null)}
				onConfirm={() => {
					if (deleteId) deleteService.mutate(deleteId);
					setDeleteId(null);
				}}
			/>
		</div>
	);
}
