import { useMemo, useState } from "react";
import { useParams, 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 { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
	Select,
	SelectContent,
	SelectItem,
	SelectTrigger,
	SelectValue,
} from "@/components/ui/select";
import {
	useDeleteServiceInstance,
	useSaveServiceInstance,
	useServiceInstances,
	useServiceTypes,
} from "../hooks/useServices";
import { useIsMobile } from "../hooks/useIsMobile";
import { SheetForm } from "@/components/ui/sheet-form";
import type {
	ServiceInstance,
	ServiceInstanceInput,
	ServiceTypeInfo,
} from "../types";
import { SectionCard } from "../components/SectionCard";
import { ConfirmDialog } from "../components/ConfirmDialog";
import { getServiceBinding } from "../integrations/registry";
import {
	OVERVIEW_TAB,
	serviceContentTabs,
	type ContentTab,
} from "./service-tabs";

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>
	);
}

export function ServicePage() {
	const { serviceType = "", serviceId = "" } = useParams<{
		serviceType: string;
		serviceId: string;
	}>();
	const { data: services = [] } = useServiceInstances(serviceType || undefined);
	const { data: types = [] } = useServiceTypes();
	const navigate = useNavigate();
	const saveService = useSaveServiceInstance();
	const deleteService = useDeleteServiceInstance();

	const instance = useMemo(
		() => services.find((s) => s.id === serviceId),
		[services, serviceId],
	);
	const binding = getServiceBinding(serviceType);
	const typeInfo = useMemo(
		() => types.find((t) => t.service_type === serviceType),
		[types, serviceType],
	);
	const contentTabs = useMemo(
		() => serviceContentTabs(serviceType),
		[serviceType],
	);
	const siblings = useMemo(
		() => services.filter((s) => s.service_type === serviceType),
		[services, serviceType],
	);
	// R3.1: switcher trigger keys off ENABLED siblings (not total).
	const enabledSiblings = useMemo(
		() => siblings.filter((s) => s.enabled),
		[siblings],
	);
	const showSwitcher = enabledSiblings.length > 1;

	const [name, setName] = useState("");
	const [enabled, setEnabled] = useState(true);
	const [draftConfig, setDraftConfig] = useState<Record<string, unknown>>({});
	const [draftSecrets, setDraftSecrets] = useState<Record<string, string>>({});
	const [deleteOpen, setDeleteOpen] = useState(false);
	const [hydrated, setHydrated] = useState(false);
	const isMobile = useIsMobile();
	const [sheetOpen, setSheetOpen] = useState(true);

	if (instance && !hydrated) {
		setName(instance.name);
		setEnabled(instance.enabled);
		setDraftConfig({ ...instance.config });
		setDraftSecrets({});
		setHydrated(true);
	}

	if (!binding) {
		return (
			<Alert>
				<AlertDescription>Unknown service type: {serviceType}</AlertDescription>
			</Alert>
		);
	}

	if (!instance) {
		return (
			<Alert>
				<AlertDescription>Service not found.</AlertDescription>
			</Alert>
		);
	}

	function buildInput(): ServiceInstanceInput {
		// R2.3/R10.1: collect typed secret drafts. Empty values mean "keep the
		// existing value" so they are filtered out before sending.
		const onlyChangedSecrets = Object.fromEntries(
			Object.entries(draftSecrets).filter(([, v]) => v !== ""),
		);
		return {
			id: instance!.id,
			service_type: instance!.service_type,
			name,
			config: draftConfig,
			secrets: onlyChangedSecrets,
			enabled,
		};
	}

	async function save() {
		await saveService.mutateAsync(buildInput());
		// Clear secret drafts after a successful save so the inputs reset to
		// "leave blank to keep" state.
		setDraftSecrets({});
	}

	const allTabs: ContentTab[] = [OVERVIEW_TAB, ...contentTabs];

	// The config + widgets body, shared between desktop tabs and mobile SheetForm.
	const widgetsContent =
		binding.widgets.length > 0 ? (
			<div className="flex flex-col gap-2">
				{binding.widgets.map((w) => (
					<div
						key={w.kind}
						className="flex items-center justify-between rounded border p-2"
					>
						<div>
							<div className="font-medium">{w.name}</div>
							<div className="text-xs text-muted-foreground">
								{w.description}
							</div>
						</div>
						<Badge variant="outline">{w.kind}</Badge>
					</div>
				))}
				<p className="text-xs text-muted-foreground">
					Add these to the dashboard from the dashboard's edit dialog.
				</p>
			</div>
		) : (
			<p className="text-sm text-muted-foreground">
				No widget kinds for this service type.
			</p>
		);

	const configBody = (
		<ConfigBody
			instance={instance}
			typeInfo={typeInfo}
			draftConfig={draftConfig}
			onConfigChange={setDraftConfig}
			draftSecrets={draftSecrets}
			onSecretsChange={setDraftSecrets}
			name={name}
			enabled={enabled}
			onNameChange={setName}
			onEnabledChange={setEnabled}
			onSave={save}
			savePending={saveService.isPending}
			onDelete={() => setDeleteOpen(true)}
		/>
	);

	// Mobile: render inside a SheetForm (open on mount; cancel navigates back).
	if (isMobile) {
		return (
			<div className="flex flex-col gap-4">
				<SheetForm
					open={sheetOpen}
					onOpenChange={setSheetOpen}
					title={name || instance.name}
					onSave={save}
					onCancel={() => {
						setSheetOpen(false);
						navigate("/services");
					}}
					isPending={saveService.isPending}
					isDirty={
						name !== instance.name ||
						enabled !== instance.enabled ||
						JSON.stringify(draftConfig) !== JSON.stringify(instance.config)
					}
				>
					<div className="flex flex-col gap-6">
						{allTabs.map((tab) => {
							const TabComponent = tab.Component;
							return (
								<div key={tab.label}>
									<h3 className="mb-2 text-sm font-semibold text-muted-foreground">
										{tab.label}
									</h3>
									<TabComponent instance={instance} />
								</div>
							);
						})}
						{widgetsContent}
						{configBody}
					</div>
				</SheetForm>
				<ConfirmDialog
					open={deleteOpen}
					title="Delete service?"
					message="This removes the service and any widgets that reference it. This cannot be undone."
					confirmLabel="Delete"
					onCancel={() => setDeleteOpen(false)}
					onConfirm={() => {
						deleteService.mutate(instance.id);
						setDeleteOpen(false);
						navigate("/services");
					}}
				/>
			</div>
		);
	}

	return (
		<div className="flex flex-col gap-4">
			{/* Header + instance switcher */}
			<div className="flex items-center justify-between gap-4">
				<div className="min-w-0">
					<h2 className="text-xl font-semibold">{instance.name}</h2>
					<p className="text-sm text-muted-foreground">{binding.description}</p>
				</div>
				<div className="flex items-center gap-2">
					{showSwitcher ? (
						<Select
							value={instance.id}
							onValueChange={(id) => navigate(`/services/${serviceType}/${id}`)}
						>
							<SelectTrigger className="w-[180px]">
								<SelectValue />
							</SelectTrigger>
							<SelectContent>
								{siblings.map((s) => (
									<SelectItem key={s.id} value={s.id}>
										{s.name}
									</SelectItem>
								))}
							</SelectContent>
						</Select>
					) : null}
					<Badge variant="outline">{binding.name}</Badge>
				</div>
			</div>

			{/* Tab skeleton */}
			<Tabs defaultValue="Overview">
				<TabsList>
					<TabsTrigger value="Overview">Overview</TabsTrigger>
					{contentTabs.map((tab) => (
						<TabsTrigger key={tab.label} value={tab.label}>
							{tab.label}
						</TabsTrigger>
					))}
					<TabsTrigger value="Widgets">Widgets</TabsTrigger>
					<TabsTrigger value="Config">Config</TabsTrigger>
				</TabsList>

				{allTabs.map((tab) => {
					const TabComponent = tab.Component;
					return (
						<TabsContent key={tab.label} value={tab.label}>
							<TabComponent instance={instance} />
						</TabsContent>
					);
				})}

				<TabsContent value="Widgets">
					<SectionCard
						title="Widgets"
						description="Widget kinds this service provides."
					>
						{widgetsContent}
					</SectionCard>
				</TabsContent>

				<TabsContent value="Config">{configBody}</TabsContent>
			</Tabs>

			<ConfirmDialog
				open={deleteOpen}
				title="Delete service?"
				message="This removes the service and any widgets that reference it. This cannot be undone."
				confirmLabel="Delete"
				onCancel={() => setDeleteOpen(false)}
				onConfirm={() => {
					deleteService.mutate(instance.id);
					setDeleteOpen(false);
					navigate("/services");
				}}
			/>
		</div>
	);
}

function ConfigBody({
	instance,
	typeInfo,
	draftConfig,
	onConfigChange,
	draftSecrets,
	onSecretsChange,
	name,
	enabled,
	onNameChange,
	onEnabledChange,
	onSave,
	savePending,
	onDelete,
}: {
	instance: ServiceInstance;
	typeInfo: ServiceTypeInfo | undefined;
	draftConfig: Record<string, unknown>;
	onConfigChange: (config: Record<string, unknown>) => void;
	draftSecrets: Record<string, string>;
	onSecretsChange: (secrets: Record<string, string>) => void;
	name: string;
	enabled: boolean;
	onNameChange: (name: string) => void;
	onEnabledChange: (enabled: boolean) => void;
	onSave: () => void;
	savePending: boolean;
	onDelete: () => void;
}) {
	const properties =
		(
			(typeInfo?.config_schema ?? {}) as {
				properties?: Record<
					string,
					{ type?: string; description?: string; default?: unknown }
				>;
			}
		).properties ?? {};
	const configEntries: Array<
		[string, { type?: string; description?: string }]
	> =
		Object.keys(properties).length > 0
			? Object.entries(properties).map(([key, schema]) => [
					key,
					{ type: schema?.type, description: schema?.description },
				])
			: Object.entries(instance.config).map(([key, value]) => [
					key,
					{ type: typeof value === "number" ? "integer" : "string" },
				]);

	return (
		<SectionCard title="Config">
			<div className="flex flex-col gap-3">
				<Field label="Name" htmlFor="service-name">
					<Input
						id="service-name"
						value={name}
						onChange={(e) => onNameChange(e.target.value)}
					/>
				</Field>
				<div className="flex items-center gap-2">
					<Switch
						id="service-enabled"
						checked={enabled}
						onCheckedChange={onEnabledChange}
					/>
					<Label htmlFor="service-enabled">Enabled</Label>
				</div>

				{configEntries.length === 0 ? (
					<p className="text-sm text-muted-foreground">No connection config.</p>
				) : (
					<div className="flex flex-col gap-3">
						{configEntries.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(draftConfig[key] ?? "")}
										onChange={(e) =>
											onConfigChange({
												...draftConfig,
												[key]: isNumber
													? e.target.value === ""
														? undefined
														: Number(e.target.value)
													: e.target.value,
											})
										}
									/>
								</Field>
							);
						})}
					</div>
				)}

				{Object.keys(instance.secrets_set).length === 0 ? null : (
					<div className="flex flex-col gap-3">
						{Object.entries(instance.secrets_set).map(([key, isSet]) => (
							<div key={key} className="flex flex-col gap-1.5">
								<Field
									label={key}
									htmlFor={`secret-${key}`}
									helper="Leave blank to keep the current value."
								>
									<Input
										id={`secret-${key}`}
										type="password"
										placeholder={isSet ? "•••••• (set)" : "Not set"}
										value={draftSecrets[key] ?? ""}
										onChange={(e) =>
											onSecretsChange({
												...draftSecrets,
												[key]: e.target.value,
											})
										}
									/>
								</Field>
								{isSet ? <Badge variant="secondary">set</Badge> : null}
							</div>
						))}
					</div>
				)}

				<div className="flex justify-between">
					<Button onClick={onSave} disabled={savePending}>
						Save
					</Button>
					<Button variant="destructive" onClick={onDelete}>
						Delete
					</Button>
				</div>
			</div>
		</SectionCard>
	);
}
