import React, {
	createContext,
	useCallback,
	useEffect,
	useRef,
	useState,
} from "react";
import {
	getNotifications,
	getUnreadCount,
	markNotificationRead,
	markAllNotificationsRead,
	dismissNotification,
	clearAllNotifications,
} from "../api/notifications";
import type { NotificationItem } from "../api/notifications";

export interface NotificationContextValue {
	notifications: NotificationItem[];
	unreadCount: number;
	isLoading: boolean;
	error: Error | null;
	markRead: (id: string) => Promise<void>;
	markAllRead: () => Promise<void>;
	clearAll: () => Promise<void>;
	dismiss: (id: string) => Promise<void>;
	refreshList: () => Promise<void>;
	isDropdownOpen: boolean;
	setIsDropdownOpen: (open: boolean) => void;
}

export const NotificationContext =
	createContext<NotificationContextValue | null>(null);

const UNREAD_POLL_MS = 15000;
const LIST_POLL_MS = 30000;

export function NotificationProvider({
	children,
}: {
	children: React.ReactNode;
}) {
	const [notifications, setNotifications] = useState<NotificationItem[]>([]);
	const [unreadCount, setUnreadCount] = useState(0);
	const [isLoading, setIsLoading] = useState(false);
	const [error, setError] = useState<Error | null>(null);
	const [isDropdownOpen, setIsDropdownOpen] = useState(false);

	const stateRef = useRef({
		notifications,
		unreadCount,
		isDropdownOpen,
		stopped: false,
	});
	stateRef.current = {
		notifications,
		unreadCount,
		isDropdownOpen,
		stopped: false,
	};

	const unreadIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
	const listIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);

	const fetchUnreadCount = useCallback(async () => {
		if (stateRef.current.stopped) return;
		try {
			const count = await getUnreadCount();
			if (!stateRef.current.stopped) {
				setUnreadCount(count);
			}
		} catch (err) {
			const status = (err as { response?: { status?: number } })?.response
				?.status;
			if (status === 401) {
				stateRef.current.stopped = true;
				if (unreadIntervalRef.current) {
					clearInterval(unreadIntervalRef.current);
					unreadIntervalRef.current = null;
				}
				if (listIntervalRef.current) {
					clearInterval(listIntervalRef.current);
					listIntervalRef.current = null;
				}
			}
			// Silently log other errors; next cycle proceeds
			console.error("Notification unread count poll failed", err);
		}
	}, []);

	const fetchList = useCallback(async () => {
		if (stateRef.current.stopped) return;
		setIsLoading(true);
		try {
			const data = await getNotifications();
			if (!stateRef.current.stopped) {
				setNotifications(data.items);
			}
		} catch (err) {
			const status = (err as { response?: { status?: number } })?.response
				?.status;
			if (status === 401) {
				stateRef.current.stopped = true;
				if (unreadIntervalRef.current) {
					clearInterval(unreadIntervalRef.current);
					unreadIntervalRef.current = null;
				}
				if (listIntervalRef.current) {
					clearInterval(listIntervalRef.current);
					listIntervalRef.current = null;
				}
			}
			console.error("Notification list poll failed", err);
		} finally {
			setIsLoading(false);
		}
	}, []);

	const startPolling = useCallback(() => {
		if (stateRef.current.stopped) return;

		if (!unreadIntervalRef.current) {
			void fetchUnreadCount();
			unreadIntervalRef.current = setInterval(() => {
				void fetchUnreadCount();
			}, UNREAD_POLL_MS);
		}

		if (!listIntervalRef.current && !stateRef.current.isDropdownOpen) {
			void fetchList();
			listIntervalRef.current = setInterval(() => {
				if (!stateRef.current.isDropdownOpen) {
					void fetchList();
				}
			}, LIST_POLL_MS);
		}
	}, [fetchUnreadCount, fetchList]);

	const stopPolling = useCallback(() => {
		if (unreadIntervalRef.current) {
			clearInterval(unreadIntervalRef.current);
			unreadIntervalRef.current = null;
		}
		if (listIntervalRef.current) {
			clearInterval(listIntervalRef.current);
			listIntervalRef.current = null;
		}
	}, []);

	// Handle visibility changes
	useEffect(() => {
		const handleVisibilityChange = () => {
			if (document.hidden) {
				stopPolling();
			} else {
				startPolling();
			}
		};

		document.addEventListener("visibilitychange", handleVisibilityChange);
		return () => {
			document.removeEventListener("visibilitychange", handleVisibilityChange);
		};
	}, [startPolling, stopPolling]);

	// Start/stop polling based on dropdown state
	useEffect(() => {
		if (isDropdownOpen) {
			if (listIntervalRef.current) {
				clearInterval(listIntervalRef.current);
				listIntervalRef.current = null;
			}
			void fetchList();
		} else {
			if (
				!listIntervalRef.current &&
				!document.hidden &&
				unreadIntervalRef.current
			) {
				listIntervalRef.current = setInterval(() => {
					if (!stateRef.current.isDropdownOpen) {
						void fetchList();
					}
				}, LIST_POLL_MS);
			}
		}
	}, [isDropdownOpen, fetchList]);

	// Initial start
	useEffect(() => {
		startPolling();
		return () => {
			stopPolling();
		};
	}, [startPolling, stopPolling]);

	const markRead = useCallback(async (id: string) => {
		const { notifications: currentNotifications, unreadCount: currentCount } =
			stateRef.current;
		const target = currentNotifications.find((n) => n.id === id);
		const wasUnread = target ? target.read_at === null : false;

		setNotifications(
			currentNotifications.map((n) =>
				n.id === id ? { ...n, read_at: new Date().toISOString() } : n,
			),
		);
		if (wasUnread) {
			setUnreadCount((c) => Math.max(0, c - 1));
		}
		setError(null);

		try {
			await markNotificationRead(id);
		} catch (err) {
			setNotifications(currentNotifications);
			setUnreadCount(currentCount);
			setError(err as Error);
		}
	}, []);

	const markAllRead = useCallback(async () => {
		const { notifications: currentNotifications, unreadCount: currentCount } =
			stateRef.current;

		setNotifications(
			currentNotifications.map((n) =>
				n.read_at === null ? { ...n, read_at: new Date().toISOString() } : n,
			),
		);
		setUnreadCount(0);
		setError(null);

		try {
			await markAllNotificationsRead();
		} catch (err) {
			setNotifications(currentNotifications);
			setUnreadCount(currentCount);
			setError(err as Error);
		}
	}, []);

	const dismiss = useCallback(async (id: string) => {
		const { notifications: currentNotifications, unreadCount: currentCount } =
			stateRef.current;
		const target = currentNotifications.find((n) => n.id === id);
		const wasUnread = target ? target.read_at === null : false;

		setNotifications(currentNotifications.filter((n) => n.id !== id));
		if (wasUnread) {
			setUnreadCount((c) => Math.max(0, c - 1));
		}
		setError(null);

		try {
			await dismissNotification(id);
		} catch (err) {
			setNotifications(currentNotifications);
			setUnreadCount(currentCount);
			setError(err as Error);
		}
	}, []);

	const refreshList = useCallback(async () => {
		await fetchList();
	}, [fetchList]);

	const clearAll = useCallback(async () => {
		const { notifications: currentNotifications } = stateRef.current;
		const unreadInList = currentNotifications.filter(
			(n) => n.read_at === null,
		).length;

		setNotifications([]);
		setUnreadCount((c) => Math.max(0, c - unreadInList));
		setError(null);

		try {
			await clearAllNotifications();
		} catch (err) {
			setNotifications(currentNotifications);
			setError(err as Error);
		}
	}, []);

	const value: NotificationContextValue = {
		notifications,
		unreadCount,
		isLoading,
		error,
		markRead,
		markAllRead,
		clearAll,
		dismiss,
		refreshList,
		isDropdownOpen,
		setIsDropdownOpen,
	};

	return (
		<NotificationContext.Provider value={value}>
			{children}
		</NotificationContext.Provider>
	);
}
