import { useState, useCallback, useRef } from "react";
import {
	stopInstance,
	deleteInstance,
	startInstance,
	recreateInstanceTunnel,
	renameInstance,
} from "../api/sessions";
import type { Session } from "../api/sessions";
import { useSessions } from "../state/sessions";

interface UseInstanceActionsOptions {
	onRefresh: () => Promise<void>;
}

interface UseInstanceActionsReturn {
	loadingSessionId: string | null;
	dirtyDeleteSession: Session | null;
	dirtyDeleteFiles: string[];
	handleOpen: (session: Session) => void;
	handleStart: (session: Session) => Promise<void>;
	handleStop: (session: Session) => Promise<void>;
	handleDelete: (session: Session) => Promise<void>;
	handleForceDelete: (session: Session) => Promise<void>;
	handleRecreateTunnel: (session: Session) => Promise<void>;
	handleRename: (session: Session, newName: string) => Promise<void>;
	clearDirtyDelete: () => void;
}

export function useInstanceActions(
	options: UseInstanceActionsOptions,
): UseInstanceActionsReturn {
	const { onRefresh } = options;
	const { removeSession } = useSessions();
	const [loadingSessionId, setLoadingSessionId] = useState<string | null>(null);
	const [dirtyDeleteSession, setDirtyDeleteSession] = useState<Session | null>(
		null,
	);
	const [dirtyDeleteFiles, setDirtyDeleteFiles] = useState<string[]>([]);
	const tabRefs = useRef<Map<string, Window | null>>(new Map());

	const handleOpen = useCallback((session: Session) => {
		const key = session.id;
		const existing = tabRefs.current.get(key);
		if (existing && !existing.closed) {
			existing.focus();
			return;
		}

		let url: string;
		if (session.url) {
			url = session.url;
		} else if (session.tool_type_interfaces?.includes("terminal")) {
			url = `/instances/${session.id}/terminal`;
		} else {
			url = `/projects/${session.project_id}`;
		}

		const w = window.open(url, `session-${session.id}`);
		tabRefs.current.set(key, w);
	}, []);

	const handleStart = useCallback(
		async (session: Session) => {
			if (loadingSessionId === session.id) return;
			setLoadingSessionId(session.id);
			try {
				await startInstance(
					session.project_id,
					session.repository_id,
					session.id,
				);
				await onRefresh();
			} finally {
				setLoadingSessionId(null);
			}
		},
		[loadingSessionId, onRefresh],
	);

	const handleStop = useCallback(
		async (session: Session) => {
			if (loadingSessionId === session.id) return;
			setLoadingSessionId(session.id);
			try {
				await stopInstance(
					session.project_id,
					session.repository_id,
					session.id,
				);
				await onRefresh();
			} finally {
				setLoadingSessionId(null);
			}
		},
		[loadingSessionId, onRefresh],
	);

	const handleDelete = useCallback(
		async (session: Session) => {
			if (loadingSessionId === session.id) return;
			setLoadingSessionId(session.id);
			try {
				await deleteInstance(
					session.project_id,
					session.repository_id,
					session.id,
				);
				setDirtyDeleteSession(null);
				setDirtyDeleteFiles([]);
				removeSession(session.id);
				await onRefresh();
			} catch (error) {
				const axiosError = error as {
					response?: {
						status?: number;
						data?: { detail?: { changed_files?: string[] } };
					};
				};
				if (axiosError.response?.status === 409) {
					const detail = axiosError.response.data?.detail;
					if (detail?.changed_files) {
						setDirtyDeleteSession(session);
						setDirtyDeleteFiles(detail.changed_files);
						return;
					}
				}
			} finally {
				setLoadingSessionId(null);
			}
		},
		[loadingSessionId, onRefresh, removeSession],
	);

	const handleForceDelete = useCallback(
		async (session: Session) => {
			if (loadingSessionId === session.id) return;
			setLoadingSessionId(session.id);
			try {
				await deleteInstance(
					session.project_id,
					session.repository_id,
					session.id,
					true,
				);
				setDirtyDeleteSession(null);
				setDirtyDeleteFiles([]);
				removeSession(session.id);
				await onRefresh();
			} finally {
				setLoadingSessionId(null);
			}
		},
		[loadingSessionId, onRefresh, removeSession],
	);

	const handleRecreateTunnel = useCallback(
		async (session: Session) => {
			if (loadingSessionId === session.id) return;
			setLoadingSessionId(session.id);
			try {
				await recreateInstanceTunnel(
					session.project_id,
					session.repository_id,
					session.id,
				);
				await onRefresh();
			} catch (err) {
				const message =
					(err as { response?: { data?: { detail?: string } } })?.response?.data
						?.detail || "Failed to recreate tunnel";
				alert(message);
			} finally {
				setLoadingSessionId(null);
			}
		},
		[loadingSessionId, onRefresh],
	);

	const handleRename = useCallback(
		async (session: Session, newName: string) => {
			if (!newName.trim()) return;
			setLoadingSessionId(session.id);
			try {
				await renameInstance(
					session.project_id,
					session.repository_id,
					session.id,
					newName.trim(),
				);
				await onRefresh();
			} catch {
				// ignore
			} finally {
				setLoadingSessionId(null);
			}
		},
		[onRefresh],
	);

	const clearDirtyDelete = useCallback(() => {
		setDirtyDeleteSession(null);
		setDirtyDeleteFiles([]);
	}, []);

	return {
		loadingSessionId,
		dirtyDeleteSession,
		dirtyDeleteFiles,
		handleOpen,
		handleStart,
		handleStop,
		handleDelete,
		handleForceDelete,
		handleRecreateTunnel,
		handleRename,
		clearDirtyDelete,
	};
}
