"use client";

import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { AlertTriangle, Inbox, Megaphone, SquareLibrary } from "lucide-react";
import { useParams, usePathname, useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { useTheme } from "next-themes";
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { currentThreadAtom, resetCurrentThreadAtom } from "@/atoms/chat/current-thread.atom";
import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
import { statusInboxItemsAtom } from "@/atoms/inbox/status-inbox.atom";
import { rightPanelCollapsedAtom } from "@/atoms/layout/right-panel.atom";
import { deleteSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms";
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import {
	searchSpaceSettingsDialogAtom,
	teamDialogAtom,
	userSettingsDialogAtom,
} from "@/atoms/settings/settings-dialog.atoms";
import {
	removeChatTabAtom,
	resetTabsAtom,
	syncChatTabAtom,
	type Tab,
} from "@/atoms/tabs/tabs.atom";
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { ActionLogSheet } from "@/components/agent-action-log/action-log-sheet";
import { SearchSpaceSettingsDialog } from "@/components/settings/search-space-settings-dialog";
import { TeamDialog } from "@/components/settings/team-dialog";
import { UserSettingsDialog } from "@/components/settings/user-settings-dialog";
import {
	AlertDialog,
	AlertDialogAction,
	AlertDialogCancel,
	AlertDialogContent,
	AlertDialogDescription,
	AlertDialogFooter,
	AlertDialogHeader,
	AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import {
	Dialog,
	DialogContent,
	DialogDescription,
	DialogFooter,
	DialogHeader,
	DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/spinner";
import { useAnnouncements } from "@/hooks/use-announcements";
import { useInbox } from "@/hooks/use-inbox";
import { useIsMobile } from "@/hooks/use-mobile";
import { notificationsApiService } from "@/lib/apis/notifications-api.service";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
import { getLoginPath, logout } from "@/lib/auth-utils";
import { deleteThread, fetchThreads, updateThread } from "@/lib/chat/thread-persistence";
import { resetUser, trackLogout } from "@/lib/posthog/events";
import { cacheKeys } from "@/lib/query-client/cache-keys";
import type { ChatItem, NavItem, SearchSpace } from "../types/layout.types";
import { CreateSearchSpaceDialog } from "../ui/dialogs";
import { LayoutShell } from "../ui/shell";

interface LayoutDataProviderProps {
	searchSpaceId: string;
	children: React.ReactNode;
}

/**
 * Format count for display: shows numbers up to 999, then "1k+", "2k+", etc.
 */
function formatInboxCount(count: number): string {
	if (count <= 999) {
		return count.toString();
	}
	const thousands = Math.floor(count / 1000);
	return `${thousands}k+`;
}

export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProviderProps) {
	const t = useTranslations("dashboard");
	const tCommon = useTranslations("common");
	const tSidebar = useTranslations("sidebar");
	const router = useRouter();
	const params = useParams();
	const pathname = usePathname();
	const queryClient = useQueryClient();
	const { theme, setTheme } = useTheme();
	const isMobile = useIsMobile();

	// Announcements
	const { unreadCount: announcementUnreadCount } = useAnnouncements();

	// Atoms
	const { data: user } = useAtomValue(currentUserAtom);
	const {
		data: searchSpacesData,
		refetch: refetchSearchSpaces,
		isSuccess: searchSpacesLoaded,
	} = useAtomValue(searchSpacesAtom);
	const { mutateAsync: deleteSearchSpace } = useAtomValue(deleteSearchSpaceMutationAtom);
	const currentThreadState = useAtomValue(currentThreadAtom);
	const resetCurrentThread = useSetAtom(resetCurrentThreadAtom);
	const syncChatTab = useSetAtom(syncChatTabAtom);
	const resetTabs = useSetAtom(resetTabsAtom);
	const removeChatTab = useSetAtom(removeChatTabAtom);

	// Key used to force-remount the page component (e.g. after deleting the active chat
	// when the router is out of sync due to replaceState)
	const [chatResetKey, setChatResetKey] = useState(0);

	// Current IDs from URL, with fallback to atom for replaceState updates
	const currentChatId = params?.chat_id
		? Number(Array.isArray(params.chat_id) ? params.chat_id[0] : params.chat_id)
		: currentThreadState.id;

	// Fetch current search space (for caching purposes)
	useQuery({
		queryKey: cacheKeys.searchSpaces.detail(searchSpaceId),
		queryFn: () => searchSpacesApiService.getSearchSpace({ id: Number(searchSpaceId) }),
		enabled: !!searchSpaceId,
	});

	// Fetch threads (40 total to allow up to 20 per section - shared/private)
	const { data: threadsData, isPending: isLoadingThreads } = useQuery({
		queryKey: ["threads", searchSpaceId, { limit: 40 }],
		queryFn: () => fetchThreads(Number(searchSpaceId), 40),
		enabled: !!searchSpaceId,
	});

	// Unified slide-out panel state (only one can be open at a time)
	type SlideoutPanel = "inbox" | "shared" | "private" | "announcements" | null;
	const [activeSlideoutPanel, setActiveSlideoutPanel] = useState<SlideoutPanel>(null);

	const isInboxSidebarOpen = activeSlideoutPanel === "inbox";
	const isAnnouncementsSidebarOpen = activeSlideoutPanel === "announcements";

	// Documents sidebar state (shared atom so Composer can toggle it)
	const [isDocumentsSidebarOpen, setIsDocumentsSidebarOpen] = useAtom(documentsSidebarOpenAtom);
	const [isDocumentsDocked, setIsDocumentsDocked] = useState(true);
	const [isRightPanelCollapsed, setIsRightPanelCollapsed] = useAtom(rightPanelCollapsedAtom);

	// Open documents sidebar by default on desktop (docked mode)
	const documentsInitialized = useRef(false);
	useEffect(() => {
		if (!documentsInitialized.current) {
			documentsInitialized.current = true;
			const isDesktop = typeof window !== "undefined" && window.innerWidth >= 768;
			if (isDesktop) {
				setIsDocumentsSidebarOpen(true);
			}
		}
	}, [setIsDocumentsSidebarOpen]);

	// Search space dialog state
	const [isCreateSearchSpaceDialogOpen, setIsCreateSearchSpaceDialogOpen] = useState(false);

	const userId = user?.id ? String(user.id) : null;
	const numericSpaceId = Number(searchSpaceId) || null;

	// Batch-fetch unread counts for all categories in a single request
	// instead of 2 separate /unread-count calls.
	const { data: batchUnread, isLoading: isBatchUnreadLoading } = useQuery({
		queryKey: cacheKeys.notifications.batchUnreadCounts(numericSpaceId),
		queryFn: () => notificationsApiService.getBatchUnreadCounts(numericSpaceId ?? undefined),
		enabled: !!userId && !!numericSpaceId,
		staleTime: 30_000,
	});

	const commentsInbox = useInbox(
		userId,
		numericSpaceId,
		"comments",
		batchUnread?.comments,
		!isBatchUnreadLoading
	);
	const statusInbox = useInbox(
		userId,
		numericSpaceId,
		"status",
		batchUnread?.status,
		!isBatchUnreadLoading
	);

	const totalUnreadCount = commentsInbox.unreadCount + statusInbox.unreadCount;

	// Sync status inbox items to a shared atom so child components
	// (e.g. ConnectorPopup) can read them without creating duplicate useInbox hooks.
	const setStatusInboxItems = useSetAtom(statusInboxItemsAtom);
	useEffect(() => {
		setStatusInboxItems(statusInbox.inboxItems);
	}, [statusInbox.inboxItems, setStatusInboxItems]);

	// Track seen notification IDs to detect new page_limit_exceeded notifications
	const seenPageLimitNotifications = useRef<Set<number>>(new Set());
	const isInitialLoad = useRef(true);

	// Effect to show toast for new page_limit_exceeded notifications
	useEffect(() => {
		if (statusInbox.loading) return;

		const pageLimitNotifications = statusInbox.inboxItems.filter(
			(item) => item.type === "page_limit_exceeded"
		);

		if (isInitialLoad.current) {
			for (const notification of pageLimitNotifications) {
				seenPageLimitNotifications.current.add(notification.id);
			}
			isInitialLoad.current = false;
			return;
		}

		const newNotifications = pageLimitNotifications.filter(
			(notification) => !seenPageLimitNotifications.current.has(notification.id)
		);

		for (const notification of newNotifications) {
			seenPageLimitNotifications.current.add(notification.id);

			toast.error(notification.title, {
				description: notification.message,
				duration: 8000,
				icon: <AlertTriangle className="h-5 w-5 text-amber-500" />,
				action: {
					label: "Get More Pages",
					onClick: () => router.push(`/dashboard/${searchSpaceId}/more-pages`),
				},
			});
		}
	}, [statusInbox.inboxItems, statusInbox.loading, searchSpaceId, router]);

	// Delete dialogs state
	const [showDeleteChatDialog, setShowDeleteChatDialog] = useState(false);
	const [chatToDelete, setChatToDelete] = useState<{ id: number; name: string } | null>(null);
	const [isDeletingChat, setIsDeletingChat] = useState(false);

	// Rename dialog state
	const [showRenameChatDialog, setShowRenameChatDialog] = useState(false);
	const [chatToRename, setChatToRename] = useState<{ id: number; name: string } | null>(null);
	const [newChatTitle, setNewChatTitle] = useState("");
	const [isRenamingChat, setIsRenamingChat] = useState(false);

	// Delete/Leave search space dialog state
	const [showDeleteSearchSpaceDialog, setShowDeleteSearchSpaceDialog] = useState(false);
	const [showLeaveSearchSpaceDialog, setShowLeaveSearchSpaceDialog] = useState(false);
	const [searchSpaceToDelete, setSearchSpaceToDelete] = useState<SearchSpace | null>(null);
	const [searchSpaceToLeave, setSearchSpaceToLeave] = useState<SearchSpace | null>(null);
	const [isDeletingSearchSpace, setIsDeletingSearchSpace] = useState(false);
	const [isLeavingSearchSpace, setIsLeavingSearchSpace] = useState(false);

	// Reset transient slide-out panels and tabs when switching search spaces.
	// Use a ref to skip the initial mount — only reset when the space actually changes.
	const prevSearchSpaceIdRef = useRef(searchSpaceId);
	useEffect(() => {
		if (prevSearchSpaceIdRef.current !== searchSpaceId) {
			prevSearchSpaceIdRef.current = searchSpaceId;
			setActiveSlideoutPanel(null);
			resetTabs();
		}
	}, [searchSpaceId, resetTabs]);

	const searchSpaces: SearchSpace[] = useMemo(() => {
		if (!searchSpacesData || !Array.isArray(searchSpacesData)) return [];
		return searchSpacesData.map((space) => ({
			id: space.id,
			name: space.name,
			description: space.description,
			isOwner: space.is_owner,
			memberCount: space.member_count || 0,
			createdAt: space.created_at,
		}));
	}, [searchSpacesData]);

	// Find active search space from list (has is_owner and member_count)
	const activeSearchSpace: SearchSpace | null = useMemo(() => {
		if (!searchSpaceId || !searchSpaces.length) return null;
		return searchSpaces.find((s) => s.id === Number(searchSpaceId)) ?? null;
	}, [searchSpaceId, searchSpaces]);

	// Safety redirect: if the current search space is no longer in the user's list
	// (e.g. deleted by background task, membership revoked), redirect to a valid space.
	useEffect(() => {
		if (!searchSpacesLoaded || !searchSpaceId || isDeletingSearchSpace || isLeavingSearchSpace)
			return;
		if (searchSpaces.length > 0 && !activeSearchSpace) {
			router.replace(`/dashboard/${searchSpaces[0].id}/new-chat`);
		} else if (searchSpaces.length === 0 && searchSpacesLoaded) {
			router.replace("/dashboard");
		}
	}, [
		searchSpacesLoaded,
		searchSpaceId,
		searchSpaces,
		activeSearchSpace,
		isDeletingSearchSpace,
		isLeavingSearchSpace,
		router,
	]);

	// Sync current chat route with tab state
	useEffect(() => {
		const chatId = currentChatId ?? null;
		const chatUrl = chatId
			? `/dashboard/${searchSpaceId}/new-chat/${chatId}`
			: `/dashboard/${searchSpaceId}/new-chat`;
		const thread = threadsData?.threads?.find((t) => t.id === chatId);
		syncChatTab({
			chatId,
			// Avoid overwriting live SSE-updated tab titles with fallback values.
			title: chatId ? (thread?.title ?? undefined) : "New Chat",
			chatUrl,
		});
	}, [currentChatId, searchSpaceId, threadsData?.threads, syncChatTab]);

	// Transform and split chats into private and shared based on visibility
	const { myChats, sharedChats } = useMemo(() => {
		if (!threadsData?.threads) return { myChats: [], sharedChats: [] };

		const privateChats: ChatItem[] = [];
		const sharedChatsList: ChatItem[] = [];

		for (const thread of threadsData.threads) {
			const chatItem: ChatItem = {
				id: thread.id,
				name: thread.title || `Chat ${thread.id}`,
				url: `/dashboard/${searchSpaceId}/new-chat/${thread.id}`,
				visibility: thread.visibility,
				isOwnThread: thread.is_own_thread,
				archived: thread.archived,
			};

			// Split based on visibility, not ownership:
			// - PRIVATE chats go to "Private Chats" section
			// - SEARCH_SPACE chats go to "Shared Chats" section
			if (thread.visibility === "SEARCH_SPACE") {
				sharedChatsList.push(chatItem);
			} else {
				privateChats.push(chatItem);
			}
		}

		return { myChats: privateChats, sharedChats: sharedChatsList };
	}, [threadsData, searchSpaceId]);

	// Navigation items
	const navItems: NavItem[] = useMemo(
		() =>
			(
				[
					{
						title: "Inbox",
						url: "#inbox",
						icon: Inbox,
						isActive: isInboxSidebarOpen,
						badge: totalUnreadCount > 0 ? formatInboxCount(totalUnreadCount) : undefined,
					},
					isMobile
						? {
								title: "Documents",
								url: "#documents",
								icon: SquareLibrary,
								isActive: isDocumentsSidebarOpen,
							}
						: null,
					{
						title: "Announcements",
						url: "#announcements",
						icon: Megaphone,
						isActive: isAnnouncementsSidebarOpen,
						badge:
							announcementUnreadCount > 0 ? formatInboxCount(announcementUnreadCount) : undefined,
					},
				] as (NavItem | null)[]
			).filter((item): item is NavItem => item !== null),
		[
			isMobile,
			isInboxSidebarOpen,
			isDocumentsSidebarOpen,
			totalUnreadCount,
			isAnnouncementsSidebarOpen,
			announcementUnreadCount,
		]
	);

	// Handlers
	const handleSearchSpaceSelect = useCallback(
		(id: number) => {
			router.push(`/dashboard/${id}/new-chat`);
		},
		[router]
	);

	const handleAddSearchSpace = useCallback(() => {
		setIsCreateSearchSpaceDialogOpen(true);
	}, []);

	const setUserSettingsDialog = useSetAtom(userSettingsDialogAtom);
	const setSearchSpaceSettingsDialog = useSetAtom(searchSpaceSettingsDialogAtom);
	const setTeamDialogOpen = useSetAtom(teamDialogAtom);

	const handleUserSettings = useCallback(() => {
		setUserSettingsDialog({ open: true, initialTab: "profile" });
	}, [setUserSettingsDialog]);

	const handleSearchSpaceSettings = useCallback(
		(_space: SearchSpace) => {
			setSearchSpaceSettingsDialog({ open: true, initialTab: "general" });
		},
		[setSearchSpaceSettingsDialog]
	);

	const handleSearchSpaceDeleteClick = useCallback((space: SearchSpace) => {
		// If user is owner, show delete dialog; otherwise show leave dialog
		if (space.isOwner) {
			setSearchSpaceToDelete(space);
			setShowDeleteSearchSpaceDialog(true);
		} else {
			setSearchSpaceToLeave(space);
			setShowLeaveSearchSpaceDialog(true);
		}
	}, []);

	const confirmDeleteSearchSpace = useCallback(async () => {
		if (!searchSpaceToDelete) return;
		setIsDeletingSearchSpace(true);
		try {
			await deleteSearchSpace({ id: searchSpaceToDelete.id });

			const isCurrentSpace = Number(searchSpaceId) === searchSpaceToDelete.id;

			// Await refetch so we have the freshest list (backend now hides [DELETING] spaces)
			const result = await refetchSearchSpaces();
			const updatedSpaces = (result.data ?? []).filter((s) => s.id !== searchSpaceToDelete.id);

			if (isCurrentSpace) {
				if (updatedSpaces.length > 0) {
					router.push(`/dashboard/${updatedSpaces[0].id}/new-chat`);
				} else {
					router.push("/dashboard");
				}
			}
		} catch (error) {
			console.error("Error deleting search space:", error);
			toast.error(
				t.has("delete_space_error") ? t("delete_space_error") : "Failed to delete search space"
			);
		} finally {
			setIsDeletingSearchSpace(false);
			setShowDeleteSearchSpaceDialog(false);
			setSearchSpaceToDelete(null);
		}
	}, [searchSpaceToDelete, deleteSearchSpace, refetchSearchSpaces, searchSpaceId, router, t]);

	const confirmLeaveSearchSpace = useCallback(async () => {
		if (!searchSpaceToLeave) return;
		setIsLeavingSearchSpace(true);
		try {
			await searchSpacesApiService.leaveSearchSpace(searchSpaceToLeave.id);

			const isCurrentSpace = Number(searchSpaceId) === searchSpaceToLeave.id;

			const result = await refetchSearchSpaces();
			const updatedSpaces = (result.data ?? []).filter((s) => s.id !== searchSpaceToLeave.id);

			if (isCurrentSpace) {
				if (updatedSpaces.length > 0) {
					router.push(`/dashboard/${updatedSpaces[0].id}/new-chat`);
				} else {
					router.push("/dashboard");
				}
			}
		} catch (error) {
			console.error("Error leaving search space:", error);
			toast.error(t.has("leave_error") ? t("leave_error") : "Failed to leave search space");
		} finally {
			setIsLeavingSearchSpace(false);
			setShowLeaveSearchSpaceDialog(false);
			setSearchSpaceToLeave(null);
		}
	}, [searchSpaceToLeave, refetchSearchSpaces, searchSpaceId, router, t]);

	const handleTabSwitch = useCallback(
		(tab: Tab) => {
			if (tab.type === "chat") {
				const url = tab.chatUrl || `/dashboard/${searchSpaceId}/new-chat`;
				router.push(url);
			}
			// Document tabs are handled in-place by LayoutShell — no navigation needed
		},
		[router, searchSpaceId]
	);

	const handleNavItemClick = useCallback(
		(item: NavItem) => {
			if (item.url === "#inbox") {
				setActiveSlideoutPanel((prev) => (prev === "inbox" ? null : "inbox"));
				return;
			}
			if (item.url === "#documents") {
				if (!isMobile) {
					if (!isDocumentsSidebarOpen) {
						setIsDocumentsSidebarOpen(true);
						setIsRightPanelCollapsed(false);
						setActiveSlideoutPanel(null);
					} else {
						setIsRightPanelCollapsed((prev) => !prev);
					}
				} else {
					setIsDocumentsSidebarOpen((prev) => {
						if (!prev) {
							setActiveSlideoutPanel(null);
						}
						return !prev;
					});
				}
				return;
			}
			if (item.url === "#announcements") {
				setActiveSlideoutPanel((prev) => (prev === "announcements" ? null : "announcements"));
				return;
			}
			router.push(item.url);
		},
		[router, isMobile, isDocumentsSidebarOpen, setIsDocumentsSidebarOpen, setIsRightPanelCollapsed]
	);

	const handleNewChat = useCallback(() => {
		// Check if router is out of sync (thread created via replaceState but params don't have chat_id)
		const isOutOfSync = currentThreadState.id !== null && !params?.chat_id;

		if (isOutOfSync) {
			resetCurrentThread();
			// Immediately set the browser URL so the page remounts with a clean /new-chat path
			window.history.replaceState(null, "", `/dashboard/${searchSpaceId}/new-chat`);
			// Force-remount the page component to reset all React state synchronously
			setChatResetKey((k) => k + 1);
			// Sync Next.js router internals so useParams/usePathname stay correct going forward
			router.replace(`/dashboard/${searchSpaceId}/new-chat`);
		} else {
			router.push(`/dashboard/${searchSpaceId}/new-chat`);
		}
	}, [router, searchSpaceId, currentThreadState.id, params?.chat_id, resetCurrentThread]);

	const handleChatSelect = useCallback(
		(chat: ChatItem) => {
			router.push(chat.url);
		},
		[router]
	);

	const handleChatDelete = useCallback((chat: ChatItem) => {
		setChatToDelete({ id: chat.id, name: chat.name });
		setShowDeleteChatDialog(true);
	}, []);

	const handleChatRename = useCallback((chat: ChatItem) => {
		setChatToRename({ id: chat.id, name: chat.name });
		setNewChatTitle(chat.name);
		setShowRenameChatDialog(true);
	}, []);

	const handleChatArchive = useCallback(
		async (chat: ChatItem) => {
			const newArchivedState = !chat.archived;
			const successMessage = newArchivedState
				? tSidebar("chat_archived") || "Chat archived"
				: tSidebar("chat_unarchived") || "Chat restored";

			try {
				await updateThread(chat.id, { archived: newArchivedState });
				toast.success(successMessage);
				// Invalidate queries to refresh UI (React Query will only refetch active queries)
				queryClient.invalidateQueries({ queryKey: ["threads", searchSpaceId] });
				queryClient.invalidateQueries({ queryKey: ["all-threads", searchSpaceId] });
				queryClient.invalidateQueries({ queryKey: ["search-threads", searchSpaceId] });
			} catch (error) {
				console.error("Error archiving thread:", error);
				toast.error(tSidebar("error_archiving_chat") || "Failed to archive chat");
			}
		},
		[queryClient, searchSpaceId, tSidebar]
	);

	const handleSettings = useCallback(() => {
		setSearchSpaceSettingsDialog({ open: true, initialTab: "general" });
	}, [setSearchSpaceSettingsDialog]);

	const handleManageMembers = useCallback(() => {
		setTeamDialogOpen(true);
	}, [setTeamDialogOpen]);

	const handleLogout = useCallback(async () => {
		try {
			trackLogout();
			resetUser();

			// Revoke refresh token on server and clear all tokens from localStorage
			await logout();

			if (typeof window !== "undefined") {
				router.push(getLoginPath());
			}
		} catch (error) {
			console.error("Error during logout:", error);
			await logout();
			router.push(getLoginPath());
		}
	}, [router]);

	const handleViewAllSharedChats = useCallback(() => {
		setActiveSlideoutPanel((prev) => (prev === "shared" ? null : "shared"));
	}, []);

	const handleViewAllPrivateChats = useCallback(() => {
		setActiveSlideoutPanel((prev) => (prev === "private" ? null : "private"));
	}, []);

	// Delete handlers
	const confirmDeleteChat = useCallback(async () => {
		if (!chatToDelete) return;
		setIsDeletingChat(true);
		try {
			await deleteThread(chatToDelete.id);
			const fallbackTab = removeChatTab(chatToDelete.id);
			queryClient.invalidateQueries({ queryKey: ["threads", searchSpaceId] });
			if (currentChatId === chatToDelete.id) {
				resetCurrentThread();
				if (fallbackTab?.type === "chat" && fallbackTab.chatUrl) {
					router.push(fallbackTab.chatUrl);
				} else {
					const isOutOfSync = currentThreadState.id !== null && !params?.chat_id;
					if (isOutOfSync) {
						window.history.replaceState(null, "", `/dashboard/${searchSpaceId}/new-chat`);
						setChatResetKey((k) => k + 1);
					} else {
						router.push(`/dashboard/${searchSpaceId}/new-chat`);
					}
				}
			}
		} catch (error) {
			console.error("Error deleting thread:", error);
		} finally {
			setIsDeletingChat(false);
			setShowDeleteChatDialog(false);
			setChatToDelete(null);
		}
	}, [
		chatToDelete,
		queryClient,
		searchSpaceId,
		resetCurrentThread,
		currentChatId,
		currentThreadState.id,
		params?.chat_id,
		router,
		removeChatTab,
	]);

	// Rename handler
	const confirmRenameChat = useCallback(async () => {
		if (!chatToRename || !newChatTitle.trim()) return;
		setIsRenamingChat(true);
		try {
			await updateThread(chatToRename.id, { title: newChatTitle.trim() });
			toast.success(tSidebar("chat_renamed") || "Chat renamed");
			queryClient.invalidateQueries({ queryKey: ["threads", searchSpaceId] });
			queryClient.invalidateQueries({ queryKey: ["all-threads", searchSpaceId] });
			queryClient.invalidateQueries({ queryKey: ["search-threads", searchSpaceId] });
		} catch (error) {
			console.error("Error renaming thread:", error);
			toast.error(tSidebar("error_renaming_chat") || "Failed to rename chat");
		} finally {
			setIsRenamingChat(false);
			setShowRenameChatDialog(false);
			setChatToRename(null);
			setNewChatTitle("");
		}
	}, [chatToRename, newChatTitle, queryClient, searchSpaceId, tSidebar]);

	// Detect if we're on the chat page (needs overflow-hidden for chat's own scroll)
	const isChatPage = pathname?.includes("/new-chat") ?? false;

	return (
		<>
			<LayoutShell
				searchSpaces={searchSpaces}
				activeSearchSpaceId={Number(searchSpaceId)}
				onSearchSpaceSelect={handleSearchSpaceSelect}
				onSearchSpaceDelete={handleSearchSpaceDeleteClick}
				onSearchSpaceSettings={handleSearchSpaceSettings}
				onAddSearchSpace={handleAddSearchSpace}
				searchSpace={activeSearchSpace}
				navItems={navItems}
				onNavItemClick={handleNavItemClick}
				chats={myChats}
				sharedChats={sharedChats}
				activeChatId={currentChatId}
				onNewChat={handleNewChat}
				onChatSelect={handleChatSelect}
				onChatRename={handleChatRename}
				onChatDelete={handleChatDelete}
				onChatArchive={handleChatArchive}
				onViewAllSharedChats={handleViewAllSharedChats}
				onViewAllPrivateChats={handleViewAllPrivateChats}
				user={{
					email: user?.email || "",
					name: user?.display_name || user?.email?.split("@")[0],
					avatarUrl: user?.avatar_url || undefined,
				}}
				onSettings={handleSettings}
				onManageMembers={handleManageMembers}
				onUserSettings={handleUserSettings}
				onLogout={handleLogout}
				theme={theme}
				setTheme={setTheme}
				isChatPage={isChatPage}
				isLoadingChats={isLoadingThreads}
				activeSlideoutPanel={activeSlideoutPanel}
				onSlideoutPanelChange={setActiveSlideoutPanel}
				inbox={{
					isOpen: isInboxSidebarOpen,
					totalUnreadCount,
					comments: {
						items: commentsInbox.inboxItems,
						unreadCount: commentsInbox.unreadCount,
						loading: commentsInbox.loading,
						loadingMore: commentsInbox.loadingMore,
						hasMore: commentsInbox.hasMore,
						loadMore: commentsInbox.loadMore,
						markAsRead: commentsInbox.markAsRead,
						markAllAsRead: commentsInbox.markAllAsRead,
					},
					status: {
						items: statusInbox.inboxItems,
						unreadCount: statusInbox.unreadCount,
						loading: statusInbox.loading,
						loadingMore: statusInbox.loadingMore,
						hasMore: statusInbox.hasMore,
						loadMore: statusInbox.loadMore,
						markAsRead: statusInbox.markAsRead,
						markAllAsRead: statusInbox.markAllAsRead,
					},
				}}
				allSharedChatsPanel={{
					searchSpaceId,
				}}
				allPrivateChatsPanel={{
					searchSpaceId,
				}}
				documentsPanel={{
					open: isDocumentsSidebarOpen,
					onOpenChange: setIsDocumentsSidebarOpen,
					isDocked: isDocumentsDocked,
					onDockedChange: setIsDocumentsDocked,
				}}
				onTabSwitch={handleTabSwitch}
			>
				<Fragment key={chatResetKey}>{children}</Fragment>
			</LayoutShell>

			{/* Delete Chat Dialog */}
			<AlertDialog open={showDeleteChatDialog} onOpenChange={setShowDeleteChatDialog}>
				<AlertDialogContent className="sm:max-w-md">
					<AlertDialogHeader>
						<AlertDialogTitle>{t("delete_chat")}</AlertDialogTitle>
						<AlertDialogDescription>
							{t("delete_chat_confirm")}{" "}
							<span className="font-medium break-all">{chatToDelete?.name}</span>?{" "}
							{t("action_cannot_undone")}
						</AlertDialogDescription>
					</AlertDialogHeader>
					<AlertDialogFooter>
						<AlertDialogCancel disabled={isDeletingChat}>{tCommon("cancel")}</AlertDialogCancel>
						<AlertDialogAction
							onClick={(e) => {
								e.preventDefault();
								confirmDeleteChat();
							}}
							disabled={isDeletingChat}
							className="relative bg-destructive text-destructive-foreground hover:bg-destructive/90 items-center justify-center"
						>
							<span className={isDeletingChat ? "opacity-0" : ""}>{tCommon("delete")}</span>
							{isDeletingChat && <Spinner size="sm" className="absolute" />}
						</AlertDialogAction>
					</AlertDialogFooter>
				</AlertDialogContent>
			</AlertDialog>

			{/* Rename Chat Dialog */}
			<Dialog open={showRenameChatDialog} onOpenChange={setShowRenameChatDialog}>
				<DialogContent className="sm:max-w-md">
					<DialogHeader>
						<DialogTitle className="flex items-center gap-2">
							<span>{tSidebar("rename_chat") || "Rename Chat"}</span>
						</DialogTitle>
						<DialogDescription>
							{tSidebar("rename_chat_description") || "Enter a new name for this conversation."}
						</DialogDescription>
					</DialogHeader>
					<Input
						value={newChatTitle}
						onChange={(e) => setNewChatTitle(e.target.value)}
						placeholder={tSidebar("chat_title_placeholder") || "Chat title"}
						onKeyDown={(e) => {
							if (e.key === "Enter" && !isRenamingChat && newChatTitle.trim()) {
								confirmRenameChat();
							}
						}}
					/>
					<DialogFooter className="flex sm:justify-end">
						<Button
							variant="secondary"
							onClick={() => setShowRenameChatDialog(false)}
							disabled={isRenamingChat}
						>
							{tCommon("cancel")}
						</Button>
						<Button
							onClick={confirmRenameChat}
							disabled={
								isRenamingChat || !newChatTitle.trim() || newChatTitle.trim() === chatToRename?.name
							}
							className="relative"
						>
							<span className={isRenamingChat ? "opacity-0" : ""}>
								{tSidebar("rename") || "Rename"}
							</span>
							{isRenamingChat && <Spinner size="sm" className="absolute" />}
						</Button>
					</DialogFooter>
				</DialogContent>
			</Dialog>

			{/* Delete Search Space Dialog */}
			<AlertDialog open={showDeleteSearchSpaceDialog} onOpenChange={setShowDeleteSearchSpaceDialog}>
				<AlertDialogContent className="sm:max-w-md">
					<AlertDialogHeader>
						<AlertDialogTitle>{t("delete_search_space")}</AlertDialogTitle>
						<AlertDialogDescription>
							{t("delete_space_confirm", { name: searchSpaceToDelete?.name || "" })}
						</AlertDialogDescription>
					</AlertDialogHeader>
					<AlertDialogFooter>
						<AlertDialogCancel disabled={isDeletingSearchSpace}>
							{tCommon("cancel")}
						</AlertDialogCancel>
						<AlertDialogAction
							onClick={(e) => {
								e.preventDefault();
								confirmDeleteSearchSpace();
							}}
							disabled={isDeletingSearchSpace}
							className="relative bg-destructive text-destructive-foreground hover:bg-destructive/90"
						>
							<span className={isDeletingSearchSpace ? "opacity-0" : ""}>{tCommon("delete")}</span>
							{isDeletingSearchSpace && <Spinner size="sm" className="absolute" />}
						</AlertDialogAction>
					</AlertDialogFooter>
				</AlertDialogContent>
			</AlertDialog>

			{/* Leave Search Space Dialog */}
			<AlertDialog open={showLeaveSearchSpaceDialog} onOpenChange={setShowLeaveSearchSpaceDialog}>
				<AlertDialogContent className="sm:max-w-md">
					<AlertDialogHeader>
						<AlertDialogTitle>{t("leave_title")}</AlertDialogTitle>
						<AlertDialogDescription>
							{t("leave_confirm", { name: searchSpaceToLeave?.name || "" })}
						</AlertDialogDescription>
					</AlertDialogHeader>
					<AlertDialogFooter>
						<AlertDialogCancel disabled={isLeavingSearchSpace}>
							{tCommon("cancel")}
						</AlertDialogCancel>
						<AlertDialogAction
							onClick={(e) => {
								e.preventDefault();
								confirmLeaveSearchSpace();
							}}
							disabled={isLeavingSearchSpace}
							className="relative bg-destructive text-destructive-foreground hover:bg-destructive/90"
						>
							<span className={isLeavingSearchSpace ? "opacity-0" : ""}>{t("leave")}</span>
							{isLeavingSearchSpace && <Spinner size="sm" className="absolute" />}
						</AlertDialogAction>
					</AlertDialogFooter>
				</AlertDialogContent>
			</AlertDialog>

			{/* Create Search Space Dialog */}
			<CreateSearchSpaceDialog
				open={isCreateSearchSpaceDialogOpen}
				onOpenChange={setIsCreateSearchSpaceDialogOpen}
			/>

			{/* Settings Dialogs */}
			<SearchSpaceSettingsDialog searchSpaceId={Number(searchSpaceId)} />
			<UserSettingsDialog />
			<TeamDialog searchSpaceId={Number(searchSpaceId)} />

			{/* Agent action log + revert sheet */}
			<ActionLogSheet />
		</>
	);
}
