import React from 'react';
import { Dimensions, type EmitterSubscription, Linking } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { enableScreens } from 'react-native-screens';
import { Provider } from 'react-redux';
import { KeyboardProvider } from 'react-native-keyboard-controller';

import ResponsiveLayoutProvider from './lib/hooks/useResponsiveLayout/useResponsiveLayout';
import AppContainer from './AppContainer';
import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app';
import { deepLinkingOpen } from './actions/deepLinking';
import { ActionSheetProvider } from './containers/ActionSheet';
import InAppNotification from './containers/InAppNotification';
import Loading from './containers/Loading';
import Toast from './containers/Toast';
import TwoFactor from './containers/TwoFactor';
import { type IThemePreference } from './definitions/ITheme';
import { DimensionsContext } from './dimensions';
import { colors, themes } from './lib/constants/colors';
import { MIN_WIDTH_MASTER_DETAIL_LAYOUT } from './lib/constants/tablet';
import { getAllowAnalyticsEvents, getAllowCrashReport } from './lib/methods/crashReport';
import { debounce, isTablet } from './lib/methods/helpers';
import { toggleAnalyticsEventsReport, toggleCrashErrorsReport } from './lib/methods/helpers/log';
import parseQuery from './lib/methods/helpers/parseQuery';
import {
	getTheme,
	initialTheme,
	newThemeState,
	setNativeTheme,
	subscribeTheme,
	unsubscribeTheme
} from './lib/methods/helpers/theme';
import { initializePushNotifications, onNotification } from './lib/notifications';
import { getInitialNotification, setupVideoConfActionListener } from './lib/notifications/videoConf/getInitialNotification';
import {
	getInitialMediaCallEvents,
	setupMediaCallEvents,
	type MediaCallEventsAdapters
} from './lib/services/voip/MediaCallEvents';
import store from './lib/store';
import { initStore } from './lib/store/auxStore';
import { type TSupportedThemes, ThemeContext } from './theme';
import ChangePasscodeView from './views/ChangePasscodeView';
import ScreenLockedView from './views/ScreenLockedView';
import StatusBar from './containers/StatusBar';

enableScreens();
initStore(store);

interface IDimensions {
	width: number;
	height: number;
	scale: number;
	fontScale: number;
}

interface IState {
	theme: TSupportedThemes;
	themePreferences: IThemePreference;
	width: number;
	height: number;
	scale: number;
	fontScale: number;
}

const parseDeepLinking = (url: string) => {
	if (url) {
		url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, '');
		const regex = /^(room|auth|invite|shareextension)\?/;
		const match = url.match(regex);
		if (match) {
			const matchedPattern = match[1];
			const query = url.replace(regex, '').trim();

			if (query) {
				const parsedQuery = parseQuery(query);
				return {
					...parsedQuery,
					type: matchedPattern === 'shareextension' ? matchedPattern : parsedQuery?.type
				};
			}
		}
	}

	// Return null if the URL doesn't match or is not valid
	return null;
};

export default class Root extends React.Component<{}, IState> {
	private listenerTimeout!: any;
	private dimensionsListener?: EmitterSubscription;
	private videoConfActionCleanup?: () => void;
	private mediaCallEventCleanup?: () => void;

	constructor(props: any) {
		super(props);
		this.init();
		this.initCrashReport();
		const { width, height, scale, fontScale } = Dimensions.get('window');
		const theme = initialTheme();
		this.state = {
			theme: getTheme(theme),
			themePreferences: theme,
			width,
			height,
			scale,
			fontScale
		};
		if (isTablet) {
			this.initTablet();
		}
		setNativeTheme(theme);
	}

	private getMediaCallEventsAdapters(): MediaCallEventsAdapters {
		return {
			getActiveServerUrl: () => store.getState().server.server,
			onOpenDeepLink: params => store.dispatch(deepLinkingOpen(params))
		};
	}

	componentDidMount() {
		this.listenerTimeout = setTimeout(() => {
			Linking.addEventListener('url', ({ url }) => {
				const parsedDeepLinkingURL = parseDeepLinking(url);
				if (parsedDeepLinkingURL) {
					store.dispatch(deepLinkingOpen(parsedDeepLinkingURL));
				}
			});
		}, 5000);
		this.dimensionsListener = Dimensions.addEventListener('change', this.onDimensionsChange);

		// Set up video conf action listener for background accept/decline
		this.videoConfActionCleanup = setupVideoConfActionListener();
		// Set up media call event listeners for incoming calls
		this.mediaCallEventCleanup = setupMediaCallEvents(this.getMediaCallEventsAdapters());
	}

	componentWillUnmount() {
		clearTimeout(this.listenerTimeout);
		this.dimensionsListener?.remove?.();
		this.videoConfActionCleanup?.();
		this.mediaCallEventCleanup?.();

		unsubscribeTheme();
	}

	init = async () => {
		store.dispatch(appInitLocalSettings());

		// Open app from push notification
		const notification = await initializePushNotifications();
		if (notification) {
			if ('configured' in notification) {
				return;
			}
			onNotification(notification);
			return;
		}

		const handledVideoConf = await getInitialNotification();
		if (handledVideoConf) {
			return;
		}

		const voipInitialHandled = await getInitialMediaCallEvents(this.getMediaCallEventsAdapters());
		if (voipInitialHandled) {
			// VoIP path already dispatched navigation (or will via deep linking); do not call appInit() in parallel
			return;
		}

		// Open app from deep linking
		const deepLinking = await Linking.getInitialURL();
		const parsedDeepLinkingURL = parseDeepLinking(deepLinking!);
		if (parsedDeepLinkingURL) {
			store.dispatch(deepLinkingOpen(parsedDeepLinkingURL));
			return;
		}

		// Open app from app icon
		store.dispatch(appInit());
	};

	getMasterDetail = (width: number) => {
		if (!isTablet) {
			return false;
		}
		return width > MIN_WIDTH_MASTER_DETAIL_LAYOUT;
	};

	setMasterDetail = (width: number) => {
		const isMasterDetail = this.getMasterDetail(width);
		store.dispatch(setMasterDetailAction(isMasterDetail));
	};

	// Dimensions update fires twice
	onDimensionsChange = debounce(({ window: { width, height, scale, fontScale } }: { window: IDimensions }) => {
		this.setDimensions({
			width,
			height,
			scale,
			fontScale
		});
		this.setMasterDetail(width);
	});

	setTheme = (newTheme = {}) => {
		// change theme state
		this.setState(
			prevState => newThemeState(prevState, newTheme as IThemePreference),
			() => {
				const { themePreferences } = this.state;
				// subscribe to Appearance changes
				subscribeTheme(themePreferences, this.setTheme);
			}
		);
	};

	setDimensions = ({ width, height, scale, fontScale }: IDimensions) => {
		this.setState({ width, height, scale, fontScale });
	};

	initTablet = () => {
		const { width } = this.state;
		this.setMasterDetail(width);
	};

	initCrashReport = () => {
		getAllowCrashReport().then(allowCrashReport => {
			toggleCrashErrorsReport(allowCrashReport);
		});
		getAllowAnalyticsEvents().then(allowAnalyticsEvents => {
			toggleAnalyticsEventsReport(allowAnalyticsEvents);
		});
	};

	render() {
		const { themePreferences, theme, width, height, scale, fontScale } = this.state;
		return (
			<SafeAreaProvider style={{ backgroundColor: themes[this.state.theme].surfaceRoom }}>
				<Provider store={store}>
					<ThemeContext.Provider
						value={{
							theme,
							themePreferences,
							setTheme: this.setTheme,
							colors: colors[theme]
						}}>
						<ResponsiveLayoutProvider>
							<DimensionsContext.Provider
								value={{
									width,
									height,
									scale,
									fontScale,
									setDimensions: this.setDimensions
								}}>
								<GestureHandlerRootView>
									<KeyboardProvider>
										<ActionSheetProvider>
											<StatusBar />
											<AppContainer />
											<TwoFactor />
											<ScreenLockedView />
											<ChangePasscodeView />
											<InAppNotification />
											<Toast />
											<Loading />
										</ActionSheetProvider>
									</KeyboardProvider>
								</GestureHandlerRootView>
							</DimensionsContext.Provider>
						</ResponsiveLayoutProvider>
					</ThemeContext.Provider>
				</Provider>
			</SafeAreaProvider>
		);
	}
}
