import authenticateUser, { parseAuthicationErrors } from "@/utils/requests/authenticateUser";
import type { IuserInformations } from "@/utils/requests/types";
import getUserInformations, {
	parseUserInformationsErrors,
} from "@/utils/requests/userInformations";
import ErrorModal from "@components/modals/ErrorModal";
import { LOG } from "@logger";
import AsyncStorage from "@react-native-async-storage/async-storage";
// import { type NavigationProp, useNavigation } from "@react-navigation/native";
import { useMutation } from "@tanstack/react-query";
import * as SplashScreen from "expo-splash-screen";
import { createContext, useCallback, useContext, useEffect, useState } from "react";
import { useModalsManagerContext } from "./ModalsManagerContext";
import type { IauthenticationData } from "./Types";

const log = LOG.extend("UserAuthenticationContext");

SplashScreen.preventAutoHideAsync();

export interface UserAuthenticationContextProps {
	isAuthenticated: boolean;
	setIsAuthenticated: React.Dispatch<React.SetStateAction<boolean>>;
	setAuthenticationData: React.Dispatch<React.SetStateAction<IauthenticationData>>;
	userInformations: IuserInformations;
	setUserInformations: React.Dispatch<React.SetStateAction<IuserInformations>>;
	login: (email: string, password: string) => void;
	isAuthenticating: boolean;
	logout: () => void;
}

export const UserAuthenticationContext = createContext<UserAuthenticationContextProps>({
	isAuthenticated: false,
	setIsAuthenticated: () => {},
	setAuthenticationData: () => {},
	userInformations: {
		username: "",
		email: "",
		// biome-ignore lint/style/useNamingConvention: <Api response>
		first_name: "",
		// biome-ignore lint/style/useNamingConvention: <Api response>
		last_name: "",
		marchand: {
			// biome-ignore lint/style/useNamingConvention: <Api response>
			marchand_id: "",
			nom: "",
			code: "",
			adresse: "",
			// biome-ignore lint/style/useNamingConvention: <Api response>
			url_succes: "",
			// biome-ignore lint/style/useNamingConvention: <Api response>
			url_echec: "",
			entreprise: 0,
			user: 0,
		},
	},
	setUserInformations: () => {},
	login: () => {},
	isAuthenticating: false,
	logout: () => {},
});

export const UserAuthenticationContextProvider = ({ children }: { children: React.ReactNode }) => {
	// States

	const [isAuthenticated, setIsAuthenticated] = useState(false);
	const [isAuthenticating, setIsAuthenticating] = useState(false);
	const [authenticationData, setAuthenticationData] = useState<IauthenticationData>({
		access: "",
		refresh: "",
	});
	const [error, setError] = useState("");
	const [userInformations, setUserInformations] = useState<IuserInformations>({
		username: "JohnDoe",
		email: "JohnDoe@example.com",
		// biome-ignore lint/style/useNamingConvention: <Api response>
		first_name: "John",
		// biome-ignore lint/style/useNamingConvention: <Api response>
		last_name: "Doe",
		marchand: {
			// biome-ignore lint/style/useNamingConvention: <Api response>
			marchand_id: "id123",
			nom: "Beasy",
			code: "BEASY-EXAMPLE-1",
			adresse: "Plateau 2, 1023, Immeuble Chardy",
			// biome-ignore lint/style/useNamingConvention: <Api response>
			url_succes: "https://example.com/success",
			// biome-ignore lint/style/useNamingConvention: <Api response>
			url_echec: "https://example.com/echec",
			entreprise: 0,
			user: 0,
		},
	});

	// Hoooks
	const { showModal } = useModalsManagerContext();

	// const navigation = useNavigation<NavigationProp<ImainStackNavigator>>();

	// Mutations

	const authenticationMutation = useMutation({
		mutationFn: authenticateUser,
		onMutate: () => {
			setIsAuthenticating(true);
			setError("");
		},
		onSuccess: (data) => {
			setAuthenticationData(data);

			log.info("Receive data from authenticateUser, running getUserInformations...");
			userInformationsMutation.mutate(data.access);
		},
		onError: (error: unknown) => {
			const errorString = parseAuthicationErrors(error);
			showModal(<ErrorModal message={errorString} />);
		},
		onSettled: () => {
			setIsAuthenticating(false);
		},
	});

	const userInformationsMutation = useMutation({
		mutationFn: (userAccessToken: string) => getUserInformations(userAccessToken),
		onMutate: () => {
			setIsAuthenticating(true);
			setError("");
		},
		onSettled: () => {
			setIsAuthenticating(false);
		},
		onSuccess: (userInformations) => {
			log.info("getUserInformations request was a success, navigating to homepage");
			setUserInformations(userInformations);
			setIsAuthenticated(true);
			storeAuthenticationData(authenticationData);
			storeUserInformations(userInformations);
			// navigation.navigate("appBottomTabsNavigator");
		},
		onError: (error) => {
			log.error("userInformationsMutation", error);
			const errorString = parseUserInformationsErrors(error);
			showModal(<ErrorModal message={errorString} />);
			clearStorages();
		},
	});

	// Methods

	const login = useCallback(
		(email: string, password: string) => {
			authenticationMutation.mutate({
				username: email,
				password: password,
			});
		},
		[authenticationMutation],
	);

	const logout = useCallback(() => {
		(async () => {
			setIsAuthenticated(false);
			setAuthenticationData({
				access: "",
				refresh: "",
			});
			setUserInformations({
				username: "",
				email: "",
				// biome-ignore lint/style/useNamingConvention: <explanation>
				first_name: "",
				// biome-ignore lint/style/useNamingConvention: <explanation>
				last_name: "",
				marchand: {
					// biome-ignore lint/style/useNamingConvention: <explanation>
					marchand_id: "",
					nom: "",
					code: "",
					adresse: "",
					// biome-ignore lint/style/useNamingConvention: <explanation>
					url_succes: "",
					// biome-ignore lint/style/useNamingConvention: <explanation>
					url_echec: "",
					entreprise: 0,
					user: 0,
				},
			});
			await clearStorages();
		})();
	}, []);

	// Storages

	const storeAuthenticationData = async (authenticationData: IauthenticationData) => {
		try {
			await AsyncStorage.setItem("authenticationData", JSON.stringify(authenticationData));
		} catch (error) {
			log.error("storeAuthenticationData |", JSON.stringify(error, null, 2));
			// saving error
		}
	};

	const storeUserInformations = async (userInformations: IuserInformations) => {
		try {
			await AsyncStorage.setItem("userInformations", JSON.stringify(userInformations));
		} catch (error) {
			log.error("storeUserInformations |", JSON.stringify(error, null, 2));
			// saving error
		}
	};

	const clearStorages = async () => {
		try {
			await AsyncStorage.clear();
		} catch (error) {
			log.error("clearStorages |", JSON.stringify(error, null, 2));
			// saving error
		}
	};

	const loadAuthenticationData = async () => {
		log.debug("loadAuthenticationData | Loading authentication data");
		const jsonRepresentation = await AsyncStorage.getItem("authenticationData");
		return jsonRepresentation ? JSON.parse(jsonRepresentation) : null;
	};

	const loadUserInformations = async () => {
		log.debug("loadUserInformations | Loading user informations");
		const jsonRepresentation = await AsyncStorage.getItem("userInformations");
		return jsonRepresentation ? JSON.parse(jsonRepresentation) : null;
	};

	// biome-ignore lint/correctness/useExhaustiveDependencies: <This should only be executed once. At startup.>
	useEffect(() => {
		log.debug("UserAuthenticationContext | App Startup | loading saved user data.");
		(async () => {
			try {
				const authenticationData = await loadAuthenticationData();
				const userInformations = await loadUserInformations();
				if (authenticationData && userInformations) {
					setAuthenticationData(authenticationData);
					setUserInformations(userInformations);
					setIsAuthenticated(true);
				}
			} catch (error) {
				log.error(
					"UserAuthenticationContext | App Startup | error during retrieval of stored data |",
					JSON.stringify(error, null, 2),
				);
			} finally {
				setTimeout(async () => await SplashScreen.hideAsync(), 500);
			}
		})();
	}, []);

	return (
		<UserAuthenticationContext.Provider
			value={{
				isAuthenticated: isAuthenticated,
				isAuthenticating: isAuthenticating,
				setIsAuthenticated,
				setAuthenticationData,
				userInformations,
				setUserInformations,
				login,
				logout,
			}}
		>
			{children}
		</UserAuthenticationContext.Provider>
	);
};

export const useUserAuthenticationContext = () => {
	return useContext(UserAuthenticationContext);
};
