import type { ReactNode } from "react";
import { useMemo, useState } from "react";
import type { SavedTask, SavedTaskInput, ServiceInstance } from "../types";
import {
	useDeleteTask,
	useRunTask,
	useSaveTask,
	useTaskRuns,
	useTasks,
} from "../hooks/useSettings";
import { useServiceInstances } from "../hooks/useServices";
import { DialogFooter } from "../components/DialogFooter";
import { HoverEditButton } from "../components/HoverEditButton";
import { SectionCard } from "../components/SectionCard";
import { SelectionRailCard } from "../components/SelectionRailCard";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import {
	Dialog,
	DialogContent,
	DialogDescription,
	DialogHeader,
	DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
	Select,
	SelectContent,
	SelectItem,
	SelectTrigger,
	SelectValue,
} from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";

// Radix Select disallows empty-string item values; the "None" option maps to
// this sentinel and converts back to "" at the draft boundary.
const NONE = "__none__";

type ActionTab = "new" | string;

/** Small labeled-field wrapper replacing the MUI `<TextField label>` shell. */
function FormField({
	label,
	htmlFor,
	helperText,
	children,
}: {
	label: string;
	htmlFor?: string;
	helperText?: string;
	children: ReactNode;
}) {
	return (
		<div className="flex flex-col">
			<Label htmlFor={htmlFor} className="mb-1">
				{label}
			</Label>
			{children}
			{helperText ? (
				<p className="mt-1 text-xs text-muted-foreground">{helperText}</p>
			) : null}
		</div>
	);
}

function emptyTask(): SavedTaskInput {
	return {
		id: null,
		name: "",
		task_type: "shell",
		content: "",
		enabled: true,
		default_service_id: "",
		notes: "",
	};
}

function sameTask(a: SavedTaskInput, b: SavedTaskInput) {
	return (
		a.id === b.id &&
		a.name === b.name &&
		a.task_type === b.task_type &&
		a.content === b.content &&
		a.enabled === b.enabled &&
		a.default_service_id === b.default_service_id &&
		a.notes === b.notes
	);
}

function initialFromTask(task: SavedTask): SavedTaskInput {
	return {
		id: task.id,
		name: task.name,
		task_type: task.task_type,
		content: task.content,
		enabled: task.enabled,
		default_service_id: task.default_service_id,
		notes: task.notes,
	};
}

function TaskEditor({
	task,
	services,
	onChange,
}: {
	task: SavedTaskInput;
	services: ServiceInstance[];
	onChange: (task: SavedTaskInput) => void;
}) {
	const selectedService = services.find(
		(service) => service.id === task.default_service_id,
	);
	return (
		<div className="flex flex-col gap-4">
			<div className="flex flex-row flex-wrap items-center gap-2">
				<p className="text-sm font-semibold">
					{task.id ? "Edit action" : "New action"}
				</p>
				<Badge variant="outline">{task.task_type}</Badge>
				<Badge variant="outline">{task.enabled ? "enabled" : "disabled"}</Badge>
				{selectedService && (
					<Badge variant="outline">{`default: ${selectedService.name}`}</Badge>
				)}
			</div>

			<div className="flex flex-col gap-2">
				<FormField label="Name" htmlFor="task-name">
					<Input
						id="task-name"
						value={task.name}
						onChange={(e) => onChange({ ...task, name: e.target.value })}
					/>
				</FormField>
				<div className="flex flex-row flex-wrap gap-2">
					<div className="min-w-[180px] flex-1">
						<FormField label="Type">
							<Select
								value={task.task_type}
								onValueChange={(value) =>
									onChange({
										...task,
										task_type: value as SavedTaskInput["task_type"],
									})
								}
							>
								<SelectTrigger className="w-full" size="sm">
									<SelectValue />
								</SelectTrigger>
								<SelectContent>
									<SelectItem value="shell">Shell</SelectItem>
									<SelectItem value="python">Python</SelectItem>
								</SelectContent>
							</Select>
						</FormField>
					</div>
					<div className="min-w-[220px] flex-1">
						<FormField label="Default SSH task service">
							<Select
								value={task.default_service_id || NONE}
								onValueChange={(value) =>
									onChange({
										...task,
										default_service_id: value === NONE ? "" : value,
									})
								}
							>
								<SelectTrigger className="w-full" size="sm">
									<SelectValue placeholder="None" />
								</SelectTrigger>
								<SelectContent>
									<SelectItem value={NONE}>None</SelectItem>
									{services.map((service) => (
										<SelectItem key={service.id} value={service.id}>
											{service.name}
										</SelectItem>
									))}
								</SelectContent>
							</Select>
						</FormField>
					</div>
				</div>
				<FormField label="Notes">
					<Input
						value={task.notes}
						onChange={(e) => onChange({ ...task, notes: e.target.value })}
					/>
				</FormField>
				<FormField
					label={
						task.task_type === "python" ? "Python script" : "Shell command"
					}
					helperText={
						task.task_type === "python"
							? "Python is run as `python3 -c`."
							: "Shell commands are run through `/bin/sh -c`."
					}
				>
					<Textarea
						rows={9}
						value={task.content}
						onChange={(e) => onChange({ ...task, content: e.target.value })}
					/>
				</FormField>
			</div>
		</div>
	);
}

function TaskDialog({
	open,
	task,
	baseline,
	services,
	onClose,
	onChange,
	onSave,
	onDelete,
}: {
	open: boolean;
	task: SavedTaskInput;
	baseline: SavedTaskInput;
	services: ServiceInstance[];
	onClose: () => void;
	onChange: (task: SavedTaskInput) => void;
	onSave: () => void;
	onDelete?: () => void;
}) {
	const requestClose = () => {
		if (
			!sameTask(task, baseline) &&
			!window.confirm("Discard unsaved changes?")
		) {
			return;
		}
		onClose();
	};

	return (
		<Dialog
			open={open}
			onOpenChange={(next) => {
				if (!next) requestClose();
			}}
		>
			<DialogContent className="sm:max-w-2xl">
				<DialogHeader>
					<DialogTitle>{task.id ? "Edit action" : "New action"}</DialogTitle>
					<DialogDescription>
						Save a reusable server task. Shell commands run via{" "}
						<code>/bin/sh -c</code>; Python runs via <code>python3 -c</code>.
						Runs execute on the selected SSH task service instance.
					</DialogDescription>
				</DialogHeader>
				<TaskEditor task={task} services={services} onChange={onChange} />
				<DialogFooter
					onCancel={requestClose}
					cancelLabel="Cancel"
					onConfirm={onSave}
					confirmLabel="Save action"
					confirmBusyLabel="Save action"
					secondaryAction={
						onDelete ? (
							<Button variant="destructive" onClick={onDelete}>
								Delete
							</Button>
						) : undefined
					}
				/>
			</DialogContent>
		</Dialog>
	);
}

export function Actions() {
	const { data: sshServices = [] } = useServiceInstances("ssh_tasks");
	const { data: tasks = [] } = useTasks();
	const saveTask = useSaveTask();
	const deleteTask = useDeleteTask();
	const runTask = useRunTask();
	const [tab, setTab] = useState<ActionTab>("new");
	const [draft, setDraft] = useState<SavedTaskInput>(emptyTask());
	const [draftBaseline, setDraftBaseline] = useState<SavedTaskInput>(
		emptyTask(),
	);
	const [runServiceId, setRunServiceId] = useState("");
	const [editOpen, setEditOpen] = useState(false);

	const selectedTask = useMemo(
		() => tasks.find((task) => task.id === tab) ?? null,
		[tasks, tab],
	);
	const selectedRuns = useTaskRuns(selectedTask?.id);

	const openEdit = (initial: SavedTaskInput) => {
		setDraft(initial);
		setDraftBaseline(initial);
		setEditOpen(true);
	};

	const createNew = () => {
		const initial = emptyTask();
		setDraft(initial);
		setDraftBaseline(initial);
		setRunServiceId(sshServices[0]?.id || "");
		setEditOpen(true);
	};

	const saveDraft = async () => {
		const saved = await saveTask.mutateAsync(draft);
		setTab(saved.id);
		setEditOpen(false);
		const nextDraft = {
			id: saved.id,
			name: saved.name,
			task_type: saved.task_type,
			content: saved.content,
			enabled: saved.enabled,
			default_service_id: saved.default_service_id,
			notes: saved.notes,
		};
		setDraft(nextDraft);
		setDraftBaseline(nextDraft);
	};

	const editingTask = selectedTask;

	return (
		<div className="flex flex-col gap-6">
			<div className="flex flex-row flex-wrap items-center justify-between gap-2">
				<div>
					<h1 className="text-lg font-semibold">Actions</h1>
					<p className="text-xs text-muted-foreground">
						Save reusable server tasks and switch between them with tabs.
					</p>
				</div>
				<Badge variant="outline">{`${tasks.length} saved`}</Badge>
			</div>

			{saveTask.error && (
				<Alert variant="destructive">
					<AlertDescription>{String(saveTask.error)}</AlertDescription>
				</Alert>
			)}
			{deleteTask.error && (
				<Alert variant="destructive">
					<AlertDescription>{String(deleteTask.error)}</AlertDescription>
				</Alert>
			)}
			{runTask.error && (
				<Alert variant="destructive">
					<AlertDescription>{String(runTask.error)}</AlertDescription>
				</Alert>
			)}

			<div className="grid grid-cols-1 gap-4 md:grid-cols-[280px_minmax(0,1fr)]">
				<SelectionRailCard
					title="Saved actions"
					description="Pick a saved task, then edit or run it from the detail pane."
					contentSx={{}}
					footer={
						<Button
							variant="outline"
							size="sm"
							className="w-full"
							onClick={createNew}
						>
							Add action
						</Button>
					}
				>
					<Tabs
						value={tab}
						onValueChange={(value) => setTab(value)}
						orientation="vertical"
						className="w-full"
					>
						<TabsList variant="line" className="h-fit w-full justify-start">
							{tasks.map((task) => (
								<div
									key={task.id}
									className="group relative w-full group-hover:[&_.rail-edit]:opacity-100"
								>
									<TabsTrigger
										value={task.id}
										className="w-full justify-start pr-9"
										onClick={() => setTab(task.id)}
										onDoubleClick={() => openEdit(initialFromTask(task))}
									>
										{task.name}
									</TabsTrigger>
									<div className="absolute top-1/2 right-1 -translate-y-1/2">
										<HoverEditButton
											onClick={() => openEdit(initialFromTask(task))}
										/>
									</div>
								</div>
							))}
						</TabsList>
					</Tabs>
				</SelectionRailCard>

				<div className="flex flex-col gap-4">
					{editingTask ? (
						<SectionCard
							title={editingTask.name}
							description="Open the editor popup to modify this action."
							action={
								<div className="flex flex-row flex-wrap items-center gap-2">
									<Button
										variant="outline"
										onClick={() => openEdit(initialFromTask(editingTask))}
									>
										Edit
									</Button>
									<Button
										disabled={runTask.isPending || !runServiceId}
										onClick={async () => {
											await runTask.mutateAsync({
												taskId: editingTask.id,
												serviceId: runServiceId,
											});
										}}
									>
										{runTask.isPending ? "Running..." : "Run action"}
									</Button>
								</div>
							}
						>
							<div className="flex flex-wrap items-center gap-2">
								<FormField
									label="Run on SSH task service"
									htmlFor="run-service-id"
								>
									<Select
										value={runServiceId}
										onValueChange={(value) => setRunServiceId(value)}
									>
										<SelectTrigger
											id="run-service-id"
											className="min-w-[240px]"
											size="sm"
										>
											<SelectValue placeholder="Select service" />
										</SelectTrigger>
										<SelectContent>
											{sshServices.map((service) => (
												<SelectItem key={service.id} value={service.id}>
													{service.name}
												</SelectItem>
											))}
										</SelectContent>
									</Select>
								</FormField>
							</div>

							<Separator />

							<p className="text-sm font-semibold">Recent runs</p>
							{selectedRuns.data?.items?.length ? (
								<div className="flex flex-col gap-2">
									{selectedRuns.data.items.map((run) => (
										<Card key={run.id}>
											<CardContent className="flex flex-col gap-2 p-3">
												<div className="flex flex-row flex-wrap items-center gap-2">
													<Badge variant="outline">{run.status}</Badge>
													<p className="text-xs text-muted-foreground">
														{new Date(run.created_at * 1000).toLocaleString()}
													</p>
												</div>
												{run.stdout_tail && (
													<div className="rounded-lg border border-border px-3 py-2">
														<p className="text-xs text-muted-foreground">
															stdout
														</p>
														<p className="whitespace-pre-wrap break-words font-mono text-sm">
															{run.stdout_tail}
														</p>
													</div>
												)}
												{run.stderr_tail && (
													<div className="rounded-lg border border-border px-3 py-2">
														<p className="text-xs text-muted-foreground">
															stderr
														</p>
														<p className="whitespace-pre-wrap break-words font-mono text-sm">
															{run.stderr_tail}
														</p>
													</div>
												)}
												{run.error && (
													<Alert variant="destructive">
														<AlertDescription>{run.error}</AlertDescription>
													</Alert>
												)}
											</CardContent>
										</Card>
									))}
								</div>
							) : (
								<Alert>
									<AlertDescription>No runs yet.</AlertDescription>
								</Alert>
							)}
						</SectionCard>
					) : (
						<div className="flex flex-col gap-4">
							<SectionCard
								title="No action selected"
								description="Select a saved action from the list on the left to view its details, run it, or open the editor popup. Use the button at the bottom to add a new action."
							>
								{tasks[0] && (
									<Button variant="outline" onClick={() => setTab(tasks[0].id)}>
										Select first action
									</Button>
								)}
							</SectionCard>

							<SectionCard title="What this panel shows">
								<p className="text-xs text-muted-foreground">
									Saved actions stay on the left rail, while details, run
									controls, and recent history appear here.
								</p>
							</SectionCard>
						</div>
					)}
				</div>
			</div>

			<TaskDialog
				open={editOpen}
				task={draft}
				baseline={draftBaseline}
				services={sshServices}
				onClose={() => setEditOpen(false)}
				onChange={setDraft}
				onSave={saveDraft}
				onDelete={
					draft.id ? () => deleteTask.mutate(String(draft.id)) : undefined
				}
			/>
		</div>
	);
}
