import { Ionicons } from '@expo/vector-icons';
import { type ListenerEvent } from '@fressh/react-native-uniffi-russh';
import {
	XtermJsWebView,
	type XtermWebViewHandle,
} from '@fressh/react-native-xtermjs-webview';

import { useQueryClient } from '@tanstack/react-query';
import {
	Stack,
	useLocalSearchParams,
	useRouter,
	useFocusEffect,
} from 'expo-router';
import React, { startTransition, useEffect, useRef, useState } from 'react';
import { Dimensions, Platform, Pressable, Text, View } from 'react-native';

import {
	SafeAreaView,
	useSafeAreaInsets,
} from 'react-native-safe-area-context';
import { disconnectSshConnectionAndInvalidateQuery } from '@/lib/query-fns';
import { useSshStore, makeSessionKey } from '@/lib/ssh-store';
import { useTheme } from '@/lib/theme';

export default function TabsShellDetail() {
	const [ready, setReady] = useState(false);

	useFocusEffect(
		React.useCallback(() => {
			startTransition(() => {
				setReady(true);
			}); // React 19: non-urgent

			return () => {
				setReady(false);
			};
		}, []),
	);

	if (!ready) return <RouteSkeleton />;
	return <ShellDetail />;
}

function RouteSkeleton() {
	return (
		<View>
			<Text>Loading</Text>
		</View>
	);
}

function ShellDetail() {
	const xtermRef = useRef<XtermWebViewHandle>(null);
	const terminalReadyRef = useRef(false);
	// Legacy buffer no longer used; relying on Rust ring for replay
	const listenerIdRef = useRef<bigint | null>(null);

	const { connectionId, channelId } = useLocalSearchParams<{
		connectionId?: string;
		channelId?: string;
	}>();
	const router = useRouter();
	const theme = useTheme();

	const channelIdNum = Number(channelId);
	const sess = useSshStore((s) =>
		connectionId && channelId
			? s.getByKey(makeSessionKey(connectionId, channelIdNum))
			: undefined,
	);
	const connection = sess?.connection;
	const shell = sess?.shell;

	// If the shell disconnects, leave this screen to the list view
	useEffect(() => {
		if (!sess) return;
		if (sess.status === 'disconnected') {
			// Replace so the detail screen isn't on the stack anymore
			router.replace('/shell');
		}
	}, [router, sess]);

	// SSH -> xterm: on initialized, replay ring head then attach live listener
	useEffect(() => {
		const xterm = xtermRef.current;
		return () => {
			if (shell && listenerIdRef.current != null)
				shell.removeListener(listenerIdRef.current);
			listenerIdRef.current = null;
			if (xterm) xterm.flush();
		};
	}, [shell]);

	const queryClient = useQueryClient();
	const insets = useSafeAreaInsets();
	const estimatedTabBarHeight = Platform.select({
		ios: 49,
		android: 80,
		default: 56,
	});
	const windowH = Dimensions.get('window').height;
	const computeBottomExtra = (y: number, height: number) => {
		const extra = windowH - (y + height);
		return extra > 0 ? extra : 0;
	};

	// Measure any bottom overlap (e.g., native tab bar) and add padding to avoid it
	const [bottomExtra, setBottomExtra] = useState(0);

	return (
		<SafeAreaView
			onLayout={(e) => {
				const { y, height } = e.nativeEvent.layout;
				const extra = computeBottomExtra(y, height);
				if (extra !== bottomExtra) setBottomExtra(extra);
			}}
			style={{
				flex: 1,
				backgroundColor: theme.colors.background,
				padding: 12,
				paddingBottom:
					12 + insets.bottom + (bottomExtra || estimatedTabBarHeight),
			}}
		>
			<Stack.Screen
				options={{
					headerBackVisible: true,
					headerRight: () => (
						<Pressable
							accessibilityLabel="Disconnect"
							hitSlop={10}
							onPress={async () => {
								if (!connection) return;
								try {
									await disconnectSshConnectionAndInvalidateQuery({
										connectionId: connection.connectionId,
										queryClient,
									});
								} catch (e) {
									console.warn('Failed to disconnect', e);
								}
								router.replace('/shell');
							}}
						>
							<Ionicons name="power" size={20} color={theme.colors.primary} />
						</Pressable>
					),
				}}
			/>
			<XtermJsWebView
				ref={xtermRef}
				style={{ flex: 1 }}
				// WebView behavior that suits terminals
				keyboardDisplayRequiresUserAction={false}
				setSupportMultipleWindows={false}
				overScrollMode="never"
				pullToRefreshEnabled={false}
				bounces={false}
				setBuiltInZoomControls={false}
				setDisplayZoomControls={false}
				textZoom={100}
				allowsLinkPreview={false}
				textInteractionEnabled={false}
				// xterm options
				options={{
					fontFamily: 'Menlo, ui-monospace, monospace',
					fontSize: 18,
					cursorBlink: true,
					scrollback: 10000,
					theme: {
						background: theme.colors.background,
						foreground: theme.colors.textPrimary,
					},
				}}
				onRenderProcessGone={() => {
					console.log('WebView render process gone -> clear()');
					const xr = xtermRef.current;
					if (xr) xr.clear();
				}}
				onContentProcessDidTerminate={() => {
					console.log('WKWebView content process terminated -> clear()');
					const xr = xtermRef.current;
					if (xr) xr.clear();
				}}
				onLoadEnd={() => {
					console.log('WebView onLoadEnd');
				}}
				onMessage={(m) => {
					console.log('received msg', m);
					if (m.type === 'initialized') {
						if (terminalReadyRef.current) return;
						terminalReadyRef.current = true;

						// Replay from head, then attach live listener
						if (shell) {
							void (async () => {
								const res = await shell.readBuffer({ mode: 'head' });
								console.log('readBuffer(head)', {
									chunks: res.chunks.length,
									nextSeq: res.nextSeq,
									dropped: res.dropped,
								});
								if (res.chunks.length) {
									const chunks = res.chunks.map((c) => c.bytes);
									const xr = xtermRef.current;
									if (xr) {
										xr.writeMany(chunks);
										xr.flush();
									}
								}
								const id = shell.addListener(
									(ev: ListenerEvent) => {
										if ('kind' in ev) {
											console.log('listener.dropped', ev);
											return;
										}
										const chunk = ev;
										const xr3 = xtermRef.current;
										if (xr3) xr3.write(chunk.bytes);
									},
									{ cursor: { mode: 'seq', seq: res.nextSeq } },
								);
								console.log('shell listener attached', id.toString());
								listenerIdRef.current = id;
							})();
						}

						// Focus to pop the keyboard (iOS needs the prop we set)
						const xr2 = xtermRef.current;
						if (xr2) xr2.focus();
						return;
					}
					if (m.type === 'data') {
						console.log('xterm->SSH', { len: m.data.length });
						const { buffer, byteOffset, byteLength } = m.data;
						const ab = buffer.slice(byteOffset, byteOffset + byteLength);
						if (shell) {
							shell.sendData(ab as ArrayBuffer).catch((e: unknown) => {
								console.warn('sendData failed', e);
								router.back();
							});
						}
						return;
					} else {
						console.log('xterm.debug', m.message);
					}
				}}
			/>
		</SafeAreaView>
	);
}
