import React, {FC, useCallback, useContext, useEffect, useState} from "react";
import {
	Image,
	ImageBackground,
	Keyboard,
	Platform,
	SafeAreaView,
	ScrollView,
	StatusBar,
	View,
} from "react-native";

import {useHeaderHeight} from "@react-navigation/elements";
import {useFocusEffect} from "@react-navigation/native";
import {StackNavigationProp} from "@react-navigation/stack";
import DeviceInfo from "react-native-device-info";
import KeyChain from "react-native-keychain";

import Alert from "@components/alert";
import Background from "@components/background";
import BiometricAlert from "@components/biometricAlert";
import Card from "@components/card";
import Divider from "@components/divider";
import TextInput from "@components/input";
import Button from "@components/primaryButton";
import SecondaryButton from "@components/secondaryButton";

import {apiServerMessages} from "@config/constants/APIMessagesConstants";
import {IS_DEVICE_REGISTERED_TO_PUSH_NOTIFICATION} from "@config/constants/AppConstants";
import {keyChainICServers} from "@config/constants/AuthConstants";
import {
	defaultErrorDescription,
	defaultErrorTitle,
} from "@config/constants/ErrorMsgConstants";
import {
	AppState,
	Platforms,
} from "@config/constants/GenericReactNativeConstants";
import {AuthNavigatorParamList} from "@config/constants/NavConstants";
import {translationKeys} from "@config/constants/TranslationConstants";
import {getLocalizationContext} from "@config/Translations";
import {iStateMessage} from "@config/types/GenericReduxTypes";
import {httpStatusCodes} from "@config/types/NetworkTypes";
import {
	ButtonOnPressType,
	TextInputOnChangeTextType,
} from "@config/types/ReactNativeTypes";
import useAppState from "@hooks/appStateListener";
import useKeyboard from "@hooks/keyboardListener";
import useIsNetworkReachable from "@hooks/networkStatusListener";
import RootScreen from "@screens/rootScreen";
import localStorage from "@services/asyncStorage";
import getFCMToken from "@services/firebase/fcm";
import {
	resetAuthState,
	updateAuthPersistState,
	updateManualLoginStatus,
} from "@store/auth/AuthSlice";
import * as authThunkActions from "@store/auth/ThunkActions";
import {fetchDealerLocationInformation} from "@store/dealerServices/ThunkAction";
import {useAppDispatch, useAppSelector} from "@store/Hooks";
import {
	hideLoader as hideFanLoader,
	showLoader as showFanLoader,
} from "@store/loader/LoaderSlice";
import {resetLocationState} from "@store/location/LocationSlice";
import * as userThunkActions from "@store/user/ThunkActions";

import {
	getCurrentTheme,
	getSystemTheme,
	useThemeAwareObject,
} from "@styles/theming/ThemingHelper";
import AppUtil from "@utils/AppUtil";
import {validateEmail, validatePassword} from "@utils/InputValidationUtil";
import {turnOffAllNotifications} from "@utils/PushNotificationsUtil";

import BiometricView from "./components/BiometricView";
import Styles from "./Styles";
import {getBackgroundImage, getLogo} from "./Utils";

type LoginScreenNavigationProp = StackNavigationProp<
	AuthNavigatorParamList,
	"LoginScreen"
>;

type Props = {
	navigation: LoginScreenNavigationProp;
};

const LoginScreen: FC<Props> = ({navigation}: Props) => {
	/** Initializers */
	const styles = useThemeAwareObject(Styles.createStyles);
	const {translate} = useContext(getLocalizationContext());

	const isKeyBoardOpen = useKeyboard();

	const dispatch = useAppDispatch();

	const settingsState = useAppSelector((state) => {
		return state.settingsReducer;
	});

	const networkStatus = useIsNetworkReachable();
	const appState = useAppState();

	const [themeId, setThemeId] = useState<string>(getSystemTheme().themeId);

	/** State variables */
	const [email, setEmail] = useState("");
	const [password, setPassword] = useState("");
	const [isLoginCredentialsPresent, setIsLoginCredentialsPresent] = useState<
		boolean | null
	>(null);
	const [isAlertVisible, setIsAlertVisible] = useState(false);
	const [alertStatus, setAlertStatus] = useState<{
		isVisible: boolean;
		title: string;
		message: string;
		onPress: Function | null;
		buttonTitle: string;
	}>({
		isVisible: false,
		title: translate(defaultErrorTitle),
		message: "",
		onPress: null,
		buttonTitle: translate(translationKeys.alertButtonTitle.capsOk),
	});

	/**
	 * Need to add this when view comes into focus -
	 * as on android when any back navigation event happens UI is not updating properly if this is only mentioned in render
	 * Hence need it at focus level and render level.
	 */
	useFocusEffect(
		useCallback(() => {
			StatusBar.setBarStyle("dark-content");
			if (Platform.OS === Platforms.android) {
				StatusBar.setTranslucent(true);
				StatusBar.setBackgroundColor("transparent");
			}
		}, []),
	);

	useEffect(() => {
		(async () => {
			if (appState === AppState.active) {
				const theme = await getCurrentTheme();
				setThemeId(theme.themeId);
			}
		})();
	}, [styles.statusbar.backgroundColor, appState]);

	/**
	 * Function which shows or hide loader based on boolean value passed
	 *
	 * @param loading
	 */
	const showLoader = (loading: boolean) => {
		if (navigation.isFocused()) {
			if (loading) {
				dispatch(showFanLoader());
			} else {
				dispatch(hideFanLoader({className: "LoginScreen"}));
			}
		}
	};

	/** Custom functions */
	const onNavigate = async () => {
		/* 
       Dispatch api to fetch location information to get
       to know if dealer services alert is to be shown.
    // */
		dispatch(userThunkActions.getUserDetails());
		dispatch(fetchDealerLocationInformation());
		await dispatch(updateAuthPersistState());
	};

	const onChangeTextEmail: TextInputOnChangeTextType = (id, text) => {
		setEmail(text);
	};

	const onChangeTextPassword: TextInputOnChangeTextType = (id, text) => {
		setPassword(text);
	};

	const onCallback = (isCredentialsPresent: boolean) => {
		setIsLoginCredentialsPresent(isCredentialsPresent);
	};

	const onDismissAlert = () => {
		setAlertStatus({
			isVisible: false,
			title: alertStatus.title,
			message: alertStatus.message,
			onPress: null,
			buttonTitle: alertStatus.buttonTitle,
		});
	};

	const showAlert = (
		message: string,
		title: string,
		buttonTitle: string,
		onPress: Function | null = null,
	) => {
		showLoader(false);
		setAlertStatus({
			isVisible: appState === AppState.active,
			title,
			message,
			onPress,
			buttonTitle,
		});
	};

	const onNavigateToConfirmScreen = async (email: string) => {
		showLoader(true);
		const resendResponse = await dispatch(
			userThunkActions.resendOtpForAccountCreation({
				email,
			}),
		);

		if (resendResponse.meta.requestStatus === "fulfilled") {
			showLoader(false);
			navigation.navigate("ConfirmAccountScreen", {
				email,
				password,
			});
		} else {
			const {message, httpStatusCode} = resendResponse.payload as iStateMessage;
			showLoader(false);
			if (httpStatusCode !== httpStatusCodes.NO_INTERNET) {
				showAlert(
					message as string,
					translate(defaultErrorTitle),
					translate(translationKeys.alertButtonTitle.capsOk),
				);
			}
		}
	};

	/** Registers user for push notification */
	const registerDeviceForNotification = async (
		email: string,
	): Promise<boolean> => {
		try {
			const fcmToken = await getFCMToken();
			if (fcmToken) {
				const appUUID = await AppUtil.getAppUUID();

				await dispatch(
					userThunkActions.registerDeviceForNotification({
						deviceInfo: {
							email,
							device_id: appUUID,
							device_token: fcmToken,
							device_type: Platform.OS,
							brand: AppUtil.getAppBrandName(),
						},
					}),
				).unwrap();
				return true;
			}
			return false;
		} catch (error) {
			return false;
		}
	};

	/** Gets login and user details */
	const getLoginCredentials = async (
		email: string,
		password: string,
	): Promise<{
		status: boolean;
		httpStatusCode?: httpStatusCodes | null | undefined;
		messageId: string | null | undefined;
		title?: string | null | undefined;
		message: any;
	}> => {
		const loginDetails = await dispatch(
			authThunkActions.authenticate({email, password}),
		);

		/** We are going with old approach of checking for request status rather that hooks as it was having some side effects */
		if (loginDetails.meta.requestStatus === "fulfilled") {
			/**
			 * Fetching user details if login is successful
			 * TODO: See if we can move this logic to a service file and call that in login thunk rather at screen level
			 */
			const userDetails = await dispatch(userThunkActions.getUserDetails());
			if (userDetails.meta.requestStatus === "fulfilled") {
				/** If user details are fetched successfully register device for receiving notification */
				const isRegistered = await registerDeviceForNotification(email.trim());
				showLoader(false);
				setIsAlertVisible(true);
				if (isRegistered) {
					/** If device is registered set local storage to true */
					localStorage.setItem(IS_DEVICE_REGISTERED_TO_PUSH_NOTIFICATION, true);
				} else {
					/** otherwise turn all notification preferences to off */
					turnOffAllNotifications(email.trim());
				}

				/* 
        Dispatch api to fetch location information to get
        to know if dealer services alert is to be shown.
        */
				dispatch(updateManualLoginStatus({isManualLogin: true}));
				dispatch(fetchDealerLocationInformation());
				return {
					status: true,
					messageId: null,
					message: "success",
				};
			}
			const errorMessage = userDetails.payload as iStateMessage;
			return {
				status: false,
				httpStatusCode: errorMessage.httpStatusCode,
				messageId: errorMessage.messageId,
				title: errorMessage.title,
				message: errorMessage.message,
			};
		}
		const errorMessage = loginDetails.payload as iStateMessage;
		return {
			status: false,
			httpStatusCode: errorMessage.httpStatusCode,
			messageId: errorMessage.messageId,
			title: errorMessage.title,
			message: errorMessage.message,
		};
	};

	const onLoginResponse = (
		email: string,
		response: {
			status: boolean;
			httpStatusCode?: httpStatusCodes | null | undefined;
			messageId: string | null | undefined;
			title?: string | null | undefined;
			message: any;
		},
	) => {
		// Checks
		// 1. USER_NOT_CONFIRMED_EXCEPTION
		// 2. BRAND_EXCEPTION_ERROR
		// 2. Show alert for generic error
		if (
			!response.status &&
			response.messageId ===
				apiServerMessages.errors.USER_NOT_CONFIRMED_EXCEPTION
		) {
			onNavigateToConfirmScreen(email);
		} else if (!response.status) {
			showAlert(
				translate(response.message || defaultErrorDescription),
				translate(defaultErrorTitle),
				translate(translationKeys.alertButtonTitle.capsOk),
			);
		}
	};

	const authenticate = async () => {
		const isLoggedIn = await getLoginCredentials(email.trim(), password);
		// Checks
		// 1. USER_NOT_CONFIRMED_EXCEPTION
		// 2. BRAND_EXCEPTION_ERROR
		// 3. Generic error - which means we need to try again with email.toLowerCase()
		if (
			!isLoggedIn.status &&
			isLoggedIn.messageId ===
				apiServerMessages.errors.USER_NOT_CONFIRMED_EXCEPTION
		) {
			onNavigateToConfirmScreen(email.trim());
		} else if (!isLoggedIn.status) {
			const isLoggedInWithLowerCaseEmail = await getLoginCredentials(
				email.trim().toLowerCase(),
				password,
			);
			onLoginResponse(email.trim().toLowerCase(), isLoggedInWithLowerCaseEmail);
		}
	};

	const onPressLoginBtn: ButtonOnPressType = async () => {
		Keyboard.dismiss();
		showLoader(true);
		const isValidEmail = validateEmail(email.trim());
		const isValidPassword = validatePassword(password);

		if (!isValidEmail) {
			showAlert(
				translate(translationKeys.alertDescription.invalidEmail),
				translate(translationKeys.alertTitle.invalidEmail),
				translate(translationKeys.alertButtonTitle.capsOk),
			);
		} else if (!isValidPassword) {
			showAlert(
				translate(translationKeys.alertDescription.invalidPassword),
				translate(translationKeys.alertTitle.invalidPassword),
				translate(translationKeys.alertButtonTitle.tryAgain),
			);
		} else {
			/**
			 * Whenever user press login we are checking if network is connected then we are clearing out the stored credentials in keychain
			 * Also resetting location state & auth state
			 */
			const {isConnected} = networkStatus;
			if (isConnected) {
				dispatch(resetLocationState());
				dispatch(resetAuthState());
				await KeyChain.resetInternetCredentials(
					keyChainICServers.DOH_APP_SERVER,
				);
				setIsLoginCredentialsPresent(false);
			}

			await authenticate();
		}
	};

	/** Sub View */
	const loginCard = () => {
		return (
			<View style={styles.cardView}>
				<TextInput
					placeholder={translate(translationKeys.EmailPlaceholder)}
					keyBoardType="email-address"
					value={email}
					onChangeText={onChangeTextEmail}
				/>
				<TextInput
					containerStyle={styles.textInputContainer}
					placeholder={translate(translationKeys.PasswordPlaceholder)}
					isSecure
					value={password}
					onChangeText={onChangeTextPassword}
				/>
				<Button
					title={translate(translationKeys.LogIn)}
					onPress={onPressLoginBtn}
				/>

				<BiometricView
					isLoginCredentialsPresent={isLoginCredentialsPresent}
					onAuthenticated={() => {
						return onNavigate();
					}}
				/>

				<Divider type="horizontal" />

				<View style={styles.row}>
					<SecondaryButton
						title={translate(translationKeys.ButtonForgotPassword)}
						onPress={() => {
							navigation.navigate("ForgotPasswordScreen");
						}}
					/>
					<Divider type="vertical" />
					<SecondaryButton
						title={translate(translationKeys.ButtonCreateAccount)}
						onPress={() => {
							navigation.navigate("TermsAndConditionsScreen");
						}}
					/>
				</View>
			</View>
		);
	};

	const renderRootScreen = () => {
		if (isLoginCredentialsPresent === null) {
			showLoader(true);
		}

		const {updateStatus} = settingsState;
		const {isUpdateAvailable} = updateStatus;

		if (isUpdateAvailable) {
			showLoader(false);
			return null;
		}
		return <RootScreen onCallback={onCallback} />;
	};

	/** Main view */
	return (
		<Background keyBoardHandled keyboardVerticalOffset={useHeaderHeight()}>
			{renderRootScreen()}

			<Alert
				visible={alertStatus.isVisible}
				title={alertStatus.title}
				description={alertStatus.message}
				positiveButtonTitle={alertStatus.buttonTitle}
				onPress={alertStatus.onPress || onDismissAlert}
			/>
			<StatusBar
				translucent
				barStyle="dark-content"
				backgroundColor="transparent"
			/>
			<ImageBackground
				style={styles.backgroundImage}
				source={{uri: getBackgroundImage(themeId)}}
			>
				<SafeAreaView>
					<ScrollView
						style={styles.container}
						scrollEnabled={isKeyBoardOpen}
						showsVerticalScrollIndicator={false}
						keyboardShouldPersistTaps="handled"
					>
						<View style={styles.cardContainer}>
							<Image
								style={styles.logo}
								resizeMode={DeviceInfo.isTablet() ? "contain" : "cover"}
								resizeMethod="scale"
								source={{uri: getLogo(themeId)}}
							/>
							<Card>{loginCard()}</Card>
						</View>
					</ScrollView>
					<BiometricAlert show={isAlertVisible} />
				</SafeAreaView>
			</ImageBackground>
		</Background>
	);
};

export default LoginScreen; explain this stepwise in detail and should be easy to understand